From cd0f2bc3fee9ff7e35858eb84d1e178d144a37d7 Mon Sep 17 00:00:00 2001 From: Florian Wilhelm <2292245+fwilhe@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:20:24 +0000 Subject: [PATCH] Initial commit --- .github/workflows/repo.yml | 38 + LICENSE.md | 22 + README.md | 13 + debian/.gitignore | 1 + debian/README.md | 43 + debian/build | 114 +++ debian/features/ostreeImage/file.exclude | 1 + debian/features/ostreeImage/fstab | 3 + debian/features/ostreeImage/image.ostree.raw | 88 ++ debian/features/ostreeImage/info.yaml | 2 + debian/features/ostreeImage/pkg.include | 1 + debian/features/ostreeRepo/exec.config | 5 + debian/features/ostreeRepo/exec.late | 133 +++ debian/features/ostreeRepo/exec.post | 11 + debian/features/ostreeRepo/file.exclude | 1 + .../serial-getty@.service.d/autologin.conf | 3 + .../any.conf | 3 + .../file.include/network/99-default.network | 5 + .../usr/lib/tmpfiles.d/ostree.conf | 2 + debian/features/ostreeRepo/fstab | 3 + .../ostreeRepo/image.ostreeRepo.tar.gz | 53 + debian/features/ostreeRepo/info.yaml | 2 + debian/features/ostreeRepo/pkg.include | 173 ++++ debian/get_commit | 7 + debian/get_repo | 5 + debian/get_timestamp | 5 + debian/get_version | 5 + debian/keyring.gpg | Bin 0 -> 56156 bytes gardenlinux/.gitignore | 39 + gardenlinux/VERSION | 4 + gardenlinux/VERSION.md | 14 + gardenlinux/bin/.constants.sh | 170 +++ gardenlinux/bin/.dpkg-arch.sh | 24 + gardenlinux/bin/.fix-apt-comments.sh | 28 + gardenlinux/bin/.tar-exclude | 44 + gardenlinux/bin/README.md | 225 ++++ gardenlinux/bin/check-pkgs-availability.py | 125 +++ gardenlinux/bin/check-pkgs-pipelines.py | 73 ++ gardenlinux/bin/clean-build-result-repository | 1 + gardenlinux/bin/garden-chroot | 45 + gardenlinux/bin/garden-debian-sources-list | 146 +++ .../bin/garden-integration-test-config | 70 ++ gardenlinux/bin/garden-version | 106 ++ gardenlinux/bin/get_arch.sh | 15 + gardenlinux/bin/get_filename | 10 + gardenlinux/bin/inject-sshkey | 191 ++++ gardenlinux/bin/make-ali-ami | 17 + gardenlinux/bin/make-gcp-ami | 235 +++++ gardenlinux/bin/make-vhd | 3 + gardenlinux/bin/shrink.sh | 49 + gardenlinux/bin/start-vm | 752 ++++++++++++++ gardenlinux/bin/upload-openstack | 19 + gardenlinux/bin/urlescape | 21 + gardenlinux/bin/uuid_hash | 5 + gardenlinux/build | 114 +++ gardenlinux/cert/Containerfile | 10 + gardenlinux/cert/Makefile | 69 ++ gardenlinux/cert/build | 59 ++ .../cert/gardenlinux-intermediate-ca.chain | 0 .../cert/gardenlinux-intermediate-ca.conf | 2 + .../cert/gardenlinux-intermediate-ca.crt | 33 + .../cert/gardenlinux-kernel-sign.chain | 33 + gardenlinux/cert/gardenlinux-kernel-sign.conf | 2 + gardenlinux/cert/gardenlinux-kernel-sign.crt | 33 + gardenlinux/cert/gardenlinux-root-ca.conf | 2 + gardenlinux/cert/gardenlinux-root-ca.crt | 35 + .../cert/gardenlinux-secureboot.aws-efivars | 1 + .../cert/gardenlinux-secureboot.db.auth | Bin 0 -> 3807 bytes .../cert/gardenlinux-secureboot.db.chain | 99 ++ .../cert/gardenlinux-secureboot.db.conf | 2 + .../cert/gardenlinux-secureboot.db.crt | 33 + .../cert/gardenlinux-secureboot.kek.auth | Bin 0 -> 3785 bytes .../cert/gardenlinux-secureboot.kek.chain | 66 ++ .../cert/gardenlinux-secureboot.kek.conf | 2 + .../cert/gardenlinux-secureboot.kek.crt | 33 + .../cert/gardenlinux-secureboot.null.pk.auth | Bin 0 -> 2267 bytes .../cert/gardenlinux-secureboot.pk.auth | Bin 0 -> 3774 bytes .../cert/gardenlinux-secureboot.pk.chain | 33 + .../cert/gardenlinux-secureboot.pk.conf | 2 + .../cert/gardenlinux-secureboot.pk.crt | 33 + gardenlinux/cert/gardenlinux.io.conf | 3 + gardenlinux/cert/gardenlinux.io.conf.ext | 1 + gardenlinux/cert/gencert | 143 +++ gardenlinux/cert/genefiauth | 72 ++ gardenlinux/cert/gengpg | 35 + gardenlinux/cert/gpg.conf | 7 + gardenlinux/cert/intermediate-ca.conf | 2 + gardenlinux/cert/kernel-sign.conf | 2 + gardenlinux/cert/root-ca.conf | 2 + gardenlinux/cert/secureboot.db.conf | 2 + gardenlinux/cert/secureboot.kek.conf | 2 + gardenlinux/cert/secureboot.pk.conf | 2 + gardenlinux/container/Makefile | 80 ++ gardenlinux/container/base-test/Dockerfile | 55 + gardenlinux/container/base/create.sh | 4 + gardenlinux/container/build-cert/Dockerfile | 22 + gardenlinux/container/build-deb/Dockerfile | 6 + gardenlinux/container/build-image/Dockerfile | 68 ++ .../container/build-kernelmodule/.gitignore | 1 + .../container/build-kernelmodule/Dockerfile | 58 ++ .../gardenlinux-apt-preferences | 15 + gardenlinux/container/build/Dockerfile | 43 + gardenlinux/container/build/install-cfssl.sh | 25 + .../container/integration-test/.gitignore | 1 + .../container/integration-test/Dockerfile | 42 + .../integration-test/cloud.google.gpg.base64 | 22 + gardenlinux/container/needslim | 31 + gardenlinux/distroless/libc/base | 1 + gardenlinux/distroless/libc/dpkg_include | 1 + gardenlinux/distroless/libc/include | 1 + gardenlinux/distroless/libc/mode | 1 + gardenlinux/distroless/libc/target | 1 + gardenlinux/distroless/sapmachine/base | 1 + gardenlinux/distroless/sapmachine/mode | 1 + gardenlinux/distroless/sapmachine/target | 1 + gardenlinux/features/_boot/exec.late | 35 + gardenlinux/features/_boot/file.exclude | 1 + .../usr/local/sbin/update-kernel-cmdline | 14 + .../usr/local/sbin/update-syslinux | 70 ++ gardenlinux/features/_boot/info.yaml | 2 + gardenlinux/features/_boot/pkg.include | 4 + gardenlinux/features/_curl/info.yaml | 2 + gardenlinux/features/_curl/pkg.include | 2 + gardenlinux/features/_dev/README.md | 21 + gardenlinux/features/_dev/exec.late | 11 + .../system/emergency.service.d/sulogin.conf | 2 + .../getty@tty1.service.d/autologin.conf | 4 + .../system/rescue.service.d/sulogin.conf | 2 + .../serial-getty@.service.d/autologin.conf | 4 + gardenlinux/features/_dev/info.yaml | 2 + gardenlinux/features/_dev/pkg.include | 1 + .../features/_dev/test/autologin.disable | 0 gardenlinux/features/_dev/test/groups.chroot | 1 + .../features/_dev/test/groups.d/groups.list | 1 + .../_dev/test/test_packages_musthave.py | 1 + gardenlinux/features/_ignite/README.md | 8 + .../etc/dracut.conf.d/30-ignition.conf | 1 + .../ignition-env-generator.sh | 22 + .../30ignition-extra/ignition-files.env | 1 + .../30ignition-extra/module-setup.sh | 11 + gardenlinux/features/_ignite/info.yaml | 2 + gardenlinux/features/_ignite/pkg.include | 3 + gardenlinux/features/_selinux/README.md | 20 + gardenlinux/features/_selinux/exec.post | 18 + .../etc/kernel/cmdline.d/90-lsm.cfg | 1 + gardenlinux/features/_selinux/info.yaml | 2 + gardenlinux/features/_selinux/pkg.include | 3 + .../_selinux/test/test_kernel_cmdline.py | 14 + .../_selinux/test/test_packages_musthave.py | 1 + gardenlinux/features/_slim/README.md | 19 + gardenlinux/features/_slim/file.exclude | 6 + gardenlinux/features/_slim/info.yaml | 2 + gardenlinux/features/aws/README.md | 22 + gardenlinux/features/aws/exec.config | 9 + .../etc/cloud/cloud.cfg.d/01_debian-cloud.cfg | 13 + .../cloud.cfg.d/99_disable-network-config.cfg | 1 + .../dracut.conf.d/90-xen-blkfront-driver.conf | 1 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + .../etc/kernel/cmdline.d/70-nvme.cfg | 3 + .../resolved.conf.d/00-gardenlinux-aws.conf | 3 + .../systemd/system/aws-clocksource.service | 9 + .../timesyncd.conf.d/00-gardenlinux-aws.conf | 3 + .../usr/local/sbin/clocksource-setup.sh | 23 + gardenlinux/features/aws/info.yaml | 5 + gardenlinux/features/aws/pkg.include | 5 + .../aws/test/test_packages_musthave.py | 1 + gardenlinux/features/aws/test/test_users.py | 6 + gardenlinux/features/azure/README.md | 22 + gardenlinux/features/azure/convert.vhd | 22 + gardenlinux/features/azure/exec.config | 5 + gardenlinux/features/azure/file.exclude | 2 + .../azure/file.include/etc/chrony/chrony.conf | 50 + .../etc/cloud/cloud.cfg.d/01_debian-cloud.cfg | 13 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + .../99-azure-unmanaged-devices.network | 6 + .../etc/udev/rules.d/60-hyperv-ptp.rules | 1 + .../etc/udev/rules.d/66-azure-storage.rules | 36 + .../udev/rules.d/99-azure-product-uuid.rules | 11 + gardenlinux/features/azure/info.yaml | 5 + gardenlinux/features/azure/pkg.exclude | 1 + gardenlinux/features/azure/pkg.include | 3 + .../features/azure/test/test_azure_times.py | 29 + .../azure/test/test_packages_musthave.py | 1 + gardenlinux/features/azure/test/test_users.py | 6 + gardenlinux/features/base/README.md | 21 + gardenlinux/features/base/exec.config | 47 + gardenlinux/features/base/exec.post | 32 + gardenlinux/features/base/file.exclude | 11 + .../file.include/etc/apt/apt.conf.d/autoclean | 3 + .../etc/apt/apt.conf.d/gzip-indexes | 1 + .../file.include/etc/apt/apt.conf.d/no-caches | 2 + .../etc/apt/apt.conf.d/no-languages | 1 + .../etc/apt/apt.conf.d/no-recommends | 2 + .../etc/apt/apt.conf.d/no-suggests | 2 + .../etc/apt/preferences.d/gardenlinux | 3 + .../file.include/etc/dpkg/dpkg.cfg.d/forceold | 2 + .../file.include/etc/dpkg/dpkg.cfg.d/speedup | 1 + .../file.include/etc/dpkg/origins/gardenlinux | 4 + .../etc/sysctl.d/10-disable-sysrq.conf | 2 + .../features/base/file.include/etc/ucf.conf | 1 + .../features/base/file.include/etc/veritytab | 0 gardenlinux/features/base/fstab | 3 + gardenlinux/features/base/info.yaml | 4 + gardenlinux/features/base/pkg.exclude | 3 + gardenlinux/features/base/pkg.include | 2 + .../test/kernel-config.d/kernel-config.txt | 2 + .../base/test/rkhunter.d/rkhunter.conf | 21 + gardenlinux/features/base/test/test_basics.py | 70 ++ .../base/test/test_blacklisted_packages.py | 18 + .../features/base/test/test_debsums.py | 16 + .../features/base/test/test_disable_sysrq.py | 17 + gardenlinux/features/base/test/test_docker.py | 16 + .../features/base/test/test_duplicate_uids.py | 1 + gardenlinux/features/base/test/test_groups.py | 15 + .../features/base/test/test_kernel_config.py | 34 + .../base/test/test_metadata_connection.py | 28 + .../features/base/test/test_network.py | 44 + .../features/base/test/test_orphaned.py | 7 + .../base/test/test_packages_musthave.py | 1 + .../features/base/test/test_partition.py | 22 + .../base/test/test_password_hashes.py | 1 + .../base/test/test_password_shadow.py | 1 + .../base/test/test_platform_driver.py | 8 + gardenlinux/features/base/test/test_proc.py | 26 + gardenlinux/features/base/test/test_random.py | 70 ++ .../features/base/test/test_rkhunter.py | 1 + .../base/test/test_sgid_suid_files.py | 40 + .../features/base/test/test_systemd.py | 42 + gardenlinux/features/base/test/test_tiger.py | 1 + .../features/base/test/test_time_config.py | 111 ++ gardenlinux/features/base/test/test_umask.py | 20 + gardenlinux/features/base/test/test_users.py | 4 + gardenlinux/features/base/test/test_usr_ro.py | 5 + .../features/base/test/tiger.d/tigerrc | 115 +++ gardenlinux/features/cloud/README.md | 19 + gardenlinux/features/cloud/exec.config | 4 + .../etc/kernel/cmdline.d/00-default.cfg | 5 + .../40-enable-swap-cgroup-accounting.cfg | 1 + .../etc/kernel/cmdline.d/60-timeout.cfg | 2 + .../cloud/file.include/etc/kernel/entry-token | 1 + .../etc/kernel/postinst.d/00-kernel-cmdline | 5 + .../etc/kernel/postinst.d/zz-update-syslinux | 7 + .../etc/kernel/postrm.d/zz-update-syslinux | 7 + .../etc/modprobe.d/disabled_firewire.conf | 2 + .../etc/modprobe.d/disabled_fs.conf | 10 + .../etc/modprobe.d/disabled_net.conf | 8 + .../etc/modprobe.d/disabled_udf.conf | 2 + .../etc/modprobe.d/disabled_usb.conf | 6 + .../etc/profile.d/50-autologout.sh | 4 + .../cloud/file.include/etc/repart.d/root.conf | 2 + .../file.include/etc/sysctl.d/20-cloud.conf | 14 + .../etc/sysctl.d/21-ipv4-settings.conf | 42 + .../etc/sysctl.d/22-ipv6-settings.conf | 5 + .../system/rngd.service.d/architecture.conf | 2 + gardenlinux/features/cloud/info.yaml | 6 + gardenlinux/features/cloud/pkg.include | 2 + .../features/cloud/test/debsums.disable | 2 + .../features/cloud/test/test_autologout.py | 24 + .../cloud/test/test_packages_musthave.py | 1 + .../features/cloud/test/test_wireguard.py | 12 + gardenlinux/features/firewall/README.md | 36 + gardenlinux/features/firewall/exec.config | 8 + .../file.include/etc/nft.d/default.conf | 21 + gardenlinux/features/firewall/info.yaml | 4 + gardenlinux/features/firewall/pkg.include | 1 + .../features/firewall/test/test_nft_config.py | 8 + .../firewall/test/test_packages_musthave.py | 1 + .../firewall/test/test_systemd_units.py | 16 + gardenlinux/features/gcp/README.md | 36 + .../features/gcp/convert.gcpimage.tar.gz | 5 + gardenlinux/features/gcp/exec.config | 15 + .../etc/kernel/cmdline.d/00-cmdline.cfg | 2 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + .../etc/modprobe.d/gce-blacklist.conf | 2 + .../etc/sysctl.d/60-gce-network-security.conf | 53 + .../timesyncd.conf.d/00-gardenlinux-gcp.conf | 3 + .../file.include/usr/lib/udev/google_nvme_id | 245 +++++ .../udev/rules.d/64-gce-disk-removal.rules | 17 + .../lib/udev/rules.d/65-gce-disk-naming.rules | 34 + gardenlinux/features/gcp/info.yaml | 5 + gardenlinux/features/gcp/pkg.exclude | 1 + gardenlinux/features/gcp/pkg.include | 3 + .../gcp/test/test_blacklisted_packages.py | 11 + .../gcp/test/test_packages_musthave.py | 1 + gardenlinux/features/gcp/test/test_users.py | 6 + gardenlinux/features/kvm/README.md | 20 + gardenlinux/features/kvm/exec.config | 1 + gardenlinux/features/kvm/file.exclude | 4 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + .../etc/kernel/cmdline.d/50-ignition.cfg | 1 + .../systemd/system/ignition-disable.service | 24 + .../etc/udev/rules.d/60-onmetal.rules | 13 + .../usr/local/sbin/update-bootloaders | 9 + gardenlinux/features/kvm/info.yaml | 6 + gardenlinux/features/kvm/pkg.include | 2 + .../kvm/test/capabilities.d/capabilities.list | 1 + .../kvm/test/test_packages_musthave.py | 1 + gardenlinux/features/log/exec.config | 5 + gardenlinux/features/log/file.include.stat | 1 + .../etc/audit/rules.d/10-base-config.rules | 13 + .../etc/audit/rules.d/10-no-audit.rules | 9 + .../etc/audit/rules.d/11-loginuid.rules | 3 + .../etc/audit/rules.d/12-cont-fail.rules | 7 + .../etc/audit/rules.d/12-ignore-error.rules | 7 + .../etc/audit/rules.d/20-dont-audit.rules | 13 + .../etc/audit/rules.d/21-no32bit.rules | 5 + .../etc/audit/rules.d/22-ignore-chrony.rules | 3 + .../audit/rules.d/23-ignore-filesystems.rules | 8 + .../etc/audit/rules.d/30-nispom.rules | 94 ++ .../rules.d/30-ospp-v42-1-create-failed.rules | 13 + .../30-ospp-v42-1-create-success.rules | 7 + .../rules.d/30-ospp-v42-2-modify-failed.rules | 13 + .../30-ospp-v42-2-modify-success.rules | 7 + .../rules.d/30-ospp-v42-3-access-failed.rules | 5 + .../30-ospp-v42-3-access-success.rules | 4 + .../rules.d/30-ospp-v42-4-delete-failed.rules | 5 + .../30-ospp-v42-4-delete-success.rules | 3 + .../30-ospp-v42-5-perm-change-failed.rules | 5 + .../30-ospp-v42-5-perm-change-success.rules | 3 + .../30-ospp-v42-6-owner-change-failed.rules | 5 + .../30-ospp-v42-6-owner-change-success.rules | 3 + .../etc/audit/rules.d/30-ospp-v42.rules | 90 ++ .../etc/audit/rules.d/30-pci-dss-v31.rules | 120 +++ .../etc/audit/rules.d/30-stig.rules | 142 +++ .../etc/audit/rules.d/31-privileged.rules | 11 + .../etc/audit/rules.d/32-power-abuse.rules | 4 + .../etc/audit/rules.d/40-local.rules | 4 + .../etc/audit/rules.d/41-containers.rules | 9 + .../etc/audit/rules.d/42-injection.rules | 13 + .../etc/audit/rules.d/43-module-load.rules | 6 + .../etc/audit/rules.d/44-installers.rules | 9 + .../etc/audit/rules.d/70-einval.rules | 6 + .../etc/audit/rules.d/71-networking.rules | 3 + .../etc/audit/rules.d/99-finalize.rules | 3 + .../log/file.include/etc/audit/rules.d/README | 31 + .../log/file.include/etc/rsyslog.conf | 23 + .../etc/rsyslog.d/10-local.conf.disabled | 18 + .../file.include/etc/rsyslog.d/20-input.conf | 1 + .../etc/rsyslog.d/21-input-klog.conf.disabled | 2 + .../etc/rsyslog.d/29-input-mark.conf.disabled | 2 + .../etc/rsyslog.d/30-server.conf.disabled | 8 + .../60-audit-log-service.conf.disabled | 9 + .../systemd/journald.conf.d/10-minimum.conf | 4 + .../systemd/journald.conf.d/20-rsyslog.conf | 3 + gardenlinux/features/log/info.yaml | 2 + gardenlinux/features/log/pkg.include | 3 + gardenlinux/features/metal/README.md | 20 + gardenlinux/features/metal/exec.config | 4 + gardenlinux/features/metal/file.exclude | 8 + .../etc/cron.monthly/update-pciids | 1 + .../etc/cron.monthly/update-usbids | 1 + .../etc/kernel/cmdline.d/00-default.cfg | 5 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + .../metal/file.include/etc/kernel/entry-token | 1 + .../etc/kernel/postinst.d/00-kernel-cmdline | 5 + .../etc/kernel/postinst.d/00-ucode | 9 + .../etc/kernel/postinst.d/zz-update-syslinux | 7 + .../etc/kernel/postrm.d/zz-update-syslinux | 7 + .../etc/udev/rules.d/69-nostbyrot.rules | 1 + .../etc/udev/rules.d/71-intellldp.rules | 1 + .../metal/file.include/usr/sbin/update-usbids | 46 + gardenlinux/features/metal/info.yaml | 6 + gardenlinux/features/metal/pkg.include | 16 + .../features/metal/test/debsums.disable | 2 + .../metal/test/test_packages_musthave.py | 1 + gardenlinux/features/openstack/README.md | 21 + gardenlinux/features/openstack/convert.qcow2 | 5 + gardenlinux/features/openstack/convert.vmdk | 5 + gardenlinux/features/openstack/exec.config | 7 + .../etc/cloud/cloud.cfg.d/01_debian-cloud.cfg | 13 + .../etc/cloud/cloud.cfg.d/50-datasource.cfg | 1 + .../cloud.cfg.d/99_disable-network-config.cfg | 1 + .../file.include/etc/cloud/ds-identify.cfg | 2 + .../etc/default/grub.d/90_openstack_cc_ee.cfg | 1 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + gardenlinux/features/openstack/info.yaml | 5 + gardenlinux/features/openstack/pkg.include | 5 + .../openstack/test/test_check_files.py | 18 + .../openstack/test/test_content_in_file.py | 17 + .../openstack/test/test_packages_musthave.py | 1 + gardenlinux/features/ostreeImage/file.exclude | 1 + gardenlinux/features/ostreeImage/fstab | 3 + .../features/ostreeImage/image.ostree.raw | 88 ++ gardenlinux/features/ostreeImage/info.yaml | 2 + gardenlinux/features/ostreeImage/pkg.include | 1 + gardenlinux/features/ostreeRepo/exec.late | 136 +++ gardenlinux/features/ostreeRepo/file.exclude | 1 + .../usr/lib/tmpfiles.d/ostree.conf | 2 + .../usr/local/sbin/update-kernel-cmdline | 14 + .../usr/local/sbin/update-syslinux | 70 ++ gardenlinux/features/ostreeRepo/fstab | 3 + .../ostreeRepo/image.ostreeRepo.tar.gz | 48 + gardenlinux/features/ostreeRepo/info.yaml | 5 + gardenlinux/features/ostreeRepo/pkg.include | 10 + gardenlinux/features/server/README.md | 20 + gardenlinux/features/server/exec.config | 34 + gardenlinux/features/server/exec.early | 6 + gardenlinux/features/server/exec.post | 11 + gardenlinux/features/server/file.exclude | 25 + .../etc/dracut.conf.d/25-uefi-stub.conf | 2 + .../etc/dracut.conf.d/general.conf | 3 + .../server/file.include/etc/kernel-img.conf | 1 + .../server/file.include/etc/locale.conf | 2 + .../server/file.include/etc/machine-id | 1 + .../etc/profile.d/50-nohistory.sh | 4 + .../server/file.include/etc/sudoers.d/keepssh | 1 + .../server/file.include/etc/sudoers.d/wheel | 1 + .../enable-unprivileged-user-namespaces.conf | 1 + .../etc/sysctl.d/restric-dmesg.conf | 1 + .../etc/systemd/network/99-default.network | 11 + .../00-gardenlinux-server.conf | 3 + .../resolved.conf.d/00-disable-llmnr.conf | 2 + .../resolved.conf.d/01-disable-mdns.conf | 2 + .../system.conf.d/00-gardenlinux-server.conf | 2 + .../etc/systemd/system/kexec-load@.service | 12 + .../systemd-coredump@.service.d/override.conf | 2 + .../systemd-growfs@.service.d/override.conf | 2 + .../any.conf | 3 + .../wait-for-networkd.conf | 2 + .../systemd-timesyncd.service.d/override.conf | 3 + .../file.include/etc/systemd/system/tmp.mount | 28 + .../file.include/etc/update-motd.d/05-logo | 9 + .../etc/update-motd.d/10-hostname | 2 + .../file.include/etc/update-motd.d/20-uname | 2 + .../file.include/etc/update-motd.d/30-load | 10 + .../file.include/etc/update-motd.d/40-free | 16 + .../file.include/etc/update-motd.d/45-line | 2 + .../file.include/etc/update-motd.d/50-network | 46 + .../file.include/etc/update-motd.d/55-line | 2 + .../etc/update-motd.d/92-unattended-upgrades | 5 + .../etc/update-motd.d/95-needrestart | 10 + .../usr/lib/systemd/system/dbus.socket | 5 + .../file.include/usr/share/pam-configs/garden | 26 + .../usr/share/pam-configs/garden-extra | 8 + gardenlinux/features/server/info.yaml | 8 + gardenlinux/features/server/pkg.include | 32 + .../test/capabilities.d/capabilities.list | 1 + gardenlinux/features/server/test/pkg.ignore | 1 + .../test/systemctl.d/systemctl_disabled.list | 0 .../test/systemctl.d/systemctl_enabled.list | 2 + .../features/server/test/test_autologin.py | 26 + .../features/server/test/test_capabilities.py | 1 + .../features/server/test/test_dmesg.py | 18 + .../features/server/test/test_history.py | 36 + .../server/test/test_kernel_parameter.py | 13 + .../features/server/test/test_machine_id.py | 8 + .../server/test/test_packages_musthave.py | 1 + gardenlinux/features/server/test/test_pam.py | 14 + .../features/server/test/test_pam_faillock.py | 15 + .../server/test/test_reset_env_vars.py | 12 + .../features/server/test/test_systemctl.py | 18 + .../features/server/test/test_tiger.py | 1 + .../features/server/test/test_users.py | 4 + .../features/server/test/tiger.d/tigerrc | 2 + gardenlinux/features/server/todo | 1 + gardenlinux/features/ssh/README.md | 26 + gardenlinux/features/ssh/exec.config | 5 + gardenlinux/features/ssh/file.exclude | 2 + gardenlinux/features/ssh/file.include.stat | 1 + .../ssh/file.include/etc/fail2ban/jail.conf | 969 ++++++++++++++++++ .../ssh/file.include/etc/ssh/ssh_config | 17 + .../ssh/file.include/etc/ssh/sshd_config | 46 + .../system/fail2ban.service.d/override.conf | 6 + .../etc/systemd/system/ssh-keygen.service | 19 + .../etc/systemd/system/ssh-moduli.service | 13 + gardenlinux/features/ssh/info.yaml | 5 + gardenlinux/features/ssh/pkg.include | 3 + .../features/ssh/test/test_ssh_authorized.py | 1 + gardenlinux/features/ssh/test/test_sshd.py | 48 + gardenlinux/features/vmware/README.md | 24 + gardenlinux/features/vmware/convert.ova | 20 + gardenlinux/features/vmware/exec.config | 7 + .../etc/cloud/cloud.cfg.d/01_debian-cloud.cfg | 13 + .../cloud.cfg.d/99_disable-network-config.cfg | 1 + .../cloud.cfg.d/99_enabled-datasources.cfg | 13 + .../vmware/file.include/etc/default/grub.cfg | 15 + .../etc/kernel/cmdline.d/10-console.cfg | 1 + .../etc/kernel/cmdline.d/50-ignition.cfg | 1 + .../systemd/system/ignition-disable.service | 24 + .../usr/bin/dscheck_VMwareGuestInfo | 54 + .../sources/DataSourceVMwareGuestInfo.py | 781 ++++++++++++++ .../usr/local/sbin/update-bootloaders | 9 + gardenlinux/features/vmware/info.yaml | 6 + gardenlinux/features/vmware/make-ova | 155 +++ gardenlinux/features/vmware/pkg.include | 6 + .../test/capabilities.d/capabilities.list | 1 + .../features/vmware/test/test_check_files.py | 20 + .../vmware/test/test_content_in_file.py | 17 + .../vmware/test/test_packages_musthave.py | 1 + .../features/vmware/vmware.ovf.template | 190 ++++ gardenlinux/gardenlinux.asc | 57 ++ gardenlinux/get_commit | 7 + gardenlinux/get_repo | 5 + gardenlinux/get_timestamp | 6 + gardenlinux/get_version | 6 + gardenlinux/keyring.gpg | Bin 0 -> 2210 bytes gardenlinux/requirements.txt | 5 + 497 files changed, 11517 insertions(+) create mode 100644 .github/workflows/repo.yml create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 debian/.gitignore create mode 100644 debian/README.md create mode 100755 debian/build create mode 100644 debian/features/ostreeImage/file.exclude create mode 100644 debian/features/ostreeImage/fstab create mode 100755 debian/features/ostreeImage/image.ostree.raw create mode 100644 debian/features/ostreeImage/info.yaml create mode 100644 debian/features/ostreeImage/pkg.include create mode 100755 debian/features/ostreeRepo/exec.config create mode 100755 debian/features/ostreeRepo/exec.late create mode 100755 debian/features/ostreeRepo/exec.post create mode 100644 debian/features/ostreeRepo/file.exclude create mode 100644 debian/features/ostreeRepo/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf create mode 100644 debian/features/ostreeRepo/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf create mode 100644 debian/features/ostreeRepo/file.include/network/99-default.network create mode 100644 debian/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf create mode 100644 debian/features/ostreeRepo/fstab create mode 100755 debian/features/ostreeRepo/image.ostreeRepo.tar.gz create mode 100644 debian/features/ostreeRepo/info.yaml create mode 100644 debian/features/ostreeRepo/pkg.include create mode 100755 debian/get_commit create mode 100755 debian/get_repo create mode 100755 debian/get_timestamp create mode 100755 debian/get_version create mode 100644 debian/keyring.gpg create mode 100644 gardenlinux/.gitignore create mode 100644 gardenlinux/VERSION create mode 100644 gardenlinux/VERSION.md create mode 100644 gardenlinux/bin/.constants.sh create mode 100755 gardenlinux/bin/.dpkg-arch.sh create mode 100755 gardenlinux/bin/.fix-apt-comments.sh create mode 100644 gardenlinux/bin/.tar-exclude create mode 100644 gardenlinux/bin/README.md create mode 100755 gardenlinux/bin/check-pkgs-availability.py create mode 100755 gardenlinux/bin/check-pkgs-pipelines.py create mode 120000 gardenlinux/bin/clean-build-result-repository create mode 100755 gardenlinux/bin/garden-chroot create mode 100755 gardenlinux/bin/garden-debian-sources-list create mode 100755 gardenlinux/bin/garden-integration-test-config create mode 100755 gardenlinux/bin/garden-version create mode 100755 gardenlinux/bin/get_arch.sh create mode 100755 gardenlinux/bin/get_filename create mode 100755 gardenlinux/bin/inject-sshkey create mode 100755 gardenlinux/bin/make-ali-ami create mode 100755 gardenlinux/bin/make-gcp-ami create mode 100755 gardenlinux/bin/make-vhd create mode 100755 gardenlinux/bin/shrink.sh create mode 100755 gardenlinux/bin/start-vm create mode 100755 gardenlinux/bin/upload-openstack create mode 100755 gardenlinux/bin/urlescape create mode 100755 gardenlinux/bin/uuid_hash create mode 100755 gardenlinux/build create mode 100644 gardenlinux/cert/Containerfile create mode 100644 gardenlinux/cert/Makefile create mode 100755 gardenlinux/cert/build create mode 100644 gardenlinux/cert/gardenlinux-intermediate-ca.chain create mode 100644 gardenlinux/cert/gardenlinux-intermediate-ca.conf create mode 100644 gardenlinux/cert/gardenlinux-intermediate-ca.crt create mode 100644 gardenlinux/cert/gardenlinux-kernel-sign.chain create mode 100644 gardenlinux/cert/gardenlinux-kernel-sign.conf create mode 100644 gardenlinux/cert/gardenlinux-kernel-sign.crt create mode 100644 gardenlinux/cert/gardenlinux-root-ca.conf create mode 100644 gardenlinux/cert/gardenlinux-root-ca.crt create mode 100644 gardenlinux/cert/gardenlinux-secureboot.aws-efivars create mode 100644 gardenlinux/cert/gardenlinux-secureboot.db.auth create mode 100644 gardenlinux/cert/gardenlinux-secureboot.db.chain create mode 100644 gardenlinux/cert/gardenlinux-secureboot.db.conf create mode 100644 gardenlinux/cert/gardenlinux-secureboot.db.crt create mode 100644 gardenlinux/cert/gardenlinux-secureboot.kek.auth create mode 100644 gardenlinux/cert/gardenlinux-secureboot.kek.chain create mode 100644 gardenlinux/cert/gardenlinux-secureboot.kek.conf create mode 100644 gardenlinux/cert/gardenlinux-secureboot.kek.crt create mode 100644 gardenlinux/cert/gardenlinux-secureboot.null.pk.auth create mode 100644 gardenlinux/cert/gardenlinux-secureboot.pk.auth create mode 100644 gardenlinux/cert/gardenlinux-secureboot.pk.chain create mode 100644 gardenlinux/cert/gardenlinux-secureboot.pk.conf create mode 100644 gardenlinux/cert/gardenlinux-secureboot.pk.crt create mode 100644 gardenlinux/cert/gardenlinux.io.conf create mode 100644 gardenlinux/cert/gardenlinux.io.conf.ext create mode 100755 gardenlinux/cert/gencert create mode 100755 gardenlinux/cert/genefiauth create mode 100755 gardenlinux/cert/gengpg create mode 100644 gardenlinux/cert/gpg.conf create mode 100644 gardenlinux/cert/intermediate-ca.conf create mode 100644 gardenlinux/cert/kernel-sign.conf create mode 100644 gardenlinux/cert/root-ca.conf create mode 100644 gardenlinux/cert/secureboot.db.conf create mode 100644 gardenlinux/cert/secureboot.kek.conf create mode 100644 gardenlinux/cert/secureboot.pk.conf create mode 100644 gardenlinux/container/Makefile create mode 100644 gardenlinux/container/base-test/Dockerfile create mode 100755 gardenlinux/container/base/create.sh create mode 100644 gardenlinux/container/build-cert/Dockerfile create mode 100644 gardenlinux/container/build-deb/Dockerfile create mode 100644 gardenlinux/container/build-image/Dockerfile create mode 100644 gardenlinux/container/build-kernelmodule/.gitignore create mode 100644 gardenlinux/container/build-kernelmodule/Dockerfile create mode 100644 gardenlinux/container/build-kernelmodule/gardenlinux-apt-preferences create mode 100644 gardenlinux/container/build/Dockerfile create mode 100755 gardenlinux/container/build/install-cfssl.sh create mode 100644 gardenlinux/container/integration-test/.gitignore create mode 100644 gardenlinux/container/integration-test/Dockerfile create mode 100644 gardenlinux/container/integration-test/cloud.google.gpg.base64 create mode 100755 gardenlinux/container/needslim create mode 100644 gardenlinux/distroless/libc/base create mode 100644 gardenlinux/distroless/libc/dpkg_include create mode 100644 gardenlinux/distroless/libc/include create mode 100644 gardenlinux/distroless/libc/mode create mode 100644 gardenlinux/distroless/libc/target create mode 100644 gardenlinux/distroless/sapmachine/base create mode 100644 gardenlinux/distroless/sapmachine/mode create mode 100644 gardenlinux/distroless/sapmachine/target create mode 100755 gardenlinux/features/_boot/exec.late create mode 100644 gardenlinux/features/_boot/file.exclude create mode 100755 gardenlinux/features/_boot/file.include/usr/local/sbin/update-kernel-cmdline create mode 100755 gardenlinux/features/_boot/file.include/usr/local/sbin/update-syslinux create mode 100644 gardenlinux/features/_boot/info.yaml create mode 100644 gardenlinux/features/_boot/pkg.include create mode 100644 gardenlinux/features/_curl/info.yaml create mode 100644 gardenlinux/features/_curl/pkg.include create mode 100644 gardenlinux/features/_dev/README.md create mode 100755 gardenlinux/features/_dev/exec.late create mode 100644 gardenlinux/features/_dev/file.include/etc/systemd/system/emergency.service.d/sulogin.conf create mode 100644 gardenlinux/features/_dev/file.include/etc/systemd/system/getty@tty1.service.d/autologin.conf create mode 100644 gardenlinux/features/_dev/file.include/etc/systemd/system/rescue.service.d/sulogin.conf create mode 100644 gardenlinux/features/_dev/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf create mode 100644 gardenlinux/features/_dev/info.yaml create mode 100644 gardenlinux/features/_dev/pkg.include create mode 100644 gardenlinux/features/_dev/test/autologin.disable create mode 120000 gardenlinux/features/_dev/test/groups.chroot create mode 100644 gardenlinux/features/_dev/test/groups.d/groups.list create mode 100644 gardenlinux/features/_dev/test/test_packages_musthave.py create mode 100644 gardenlinux/features/_ignite/README.md create mode 100644 gardenlinux/features/_ignite/file.include/etc/dracut.conf.d/30-ignition.conf create mode 100755 gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-env-generator.sh create mode 100644 gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-files.env create mode 100755 gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/module-setup.sh create mode 100644 gardenlinux/features/_ignite/info.yaml create mode 100644 gardenlinux/features/_ignite/pkg.include create mode 100644 gardenlinux/features/_selinux/README.md create mode 100755 gardenlinux/features/_selinux/exec.post create mode 100644 gardenlinux/features/_selinux/file.include/etc/kernel/cmdline.d/90-lsm.cfg create mode 100644 gardenlinux/features/_selinux/info.yaml create mode 100644 gardenlinux/features/_selinux/pkg.include create mode 100644 gardenlinux/features/_selinux/test/test_kernel_cmdline.py create mode 100644 gardenlinux/features/_selinux/test/test_packages_musthave.py create mode 100644 gardenlinux/features/_slim/README.md create mode 100644 gardenlinux/features/_slim/file.exclude create mode 100644 gardenlinux/features/_slim/info.yaml create mode 100644 gardenlinux/features/aws/README.md create mode 100755 gardenlinux/features/aws/exec.config create mode 100644 gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg create mode 100644 gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg create mode 100644 gardenlinux/features/aws/file.include/etc/dracut.conf.d/90-xen-blkfront-driver.conf create mode 100644 gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/70-nvme.cfg create mode 100644 gardenlinux/features/aws/file.include/etc/systemd/resolved.conf.d/00-gardenlinux-aws.conf create mode 100644 gardenlinux/features/aws/file.include/etc/systemd/system/aws-clocksource.service create mode 100644 gardenlinux/features/aws/file.include/etc/systemd/timesyncd.conf.d/00-gardenlinux-aws.conf create mode 100755 gardenlinux/features/aws/file.include/usr/local/sbin/clocksource-setup.sh create mode 100644 gardenlinux/features/aws/info.yaml create mode 100644 gardenlinux/features/aws/pkg.include create mode 100644 gardenlinux/features/aws/test/test_packages_musthave.py create mode 100644 gardenlinux/features/aws/test/test_users.py create mode 100644 gardenlinux/features/azure/README.md create mode 100755 gardenlinux/features/azure/convert.vhd create mode 100755 gardenlinux/features/azure/exec.config create mode 100644 gardenlinux/features/azure/file.exclude create mode 100644 gardenlinux/features/azure/file.include/etc/chrony/chrony.conf create mode 100644 gardenlinux/features/azure/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg create mode 100644 gardenlinux/features/azure/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/azure/file.include/etc/systemd/99-azure-unmanaged-devices.network create mode 100644 gardenlinux/features/azure/file.include/etc/udev/rules.d/60-hyperv-ptp.rules create mode 100644 gardenlinux/features/azure/file.include/etc/udev/rules.d/66-azure-storage.rules create mode 100644 gardenlinux/features/azure/file.include/etc/udev/rules.d/99-azure-product-uuid.rules create mode 100644 gardenlinux/features/azure/info.yaml create mode 100644 gardenlinux/features/azure/pkg.exclude create mode 100644 gardenlinux/features/azure/pkg.include create mode 100644 gardenlinux/features/azure/test/test_azure_times.py create mode 100644 gardenlinux/features/azure/test/test_packages_musthave.py create mode 100644 gardenlinux/features/azure/test/test_users.py create mode 100644 gardenlinux/features/base/README.md create mode 100755 gardenlinux/features/base/exec.config create mode 100755 gardenlinux/features/base/exec.post create mode 100644 gardenlinux/features/base/file.exclude create mode 100644 gardenlinux/features/base/file.include/etc/apt/apt.conf.d/autoclean create mode 100644 gardenlinux/features/base/file.include/etc/apt/apt.conf.d/gzip-indexes create mode 100644 gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-caches create mode 100644 gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-languages create mode 100644 gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-recommends create mode 100644 gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-suggests create mode 100644 gardenlinux/features/base/file.include/etc/apt/preferences.d/gardenlinux create mode 100644 gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/forceold create mode 100644 gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/speedup create mode 100644 gardenlinux/features/base/file.include/etc/dpkg/origins/gardenlinux create mode 100644 gardenlinux/features/base/file.include/etc/sysctl.d/10-disable-sysrq.conf create mode 100644 gardenlinux/features/base/file.include/etc/ucf.conf create mode 100644 gardenlinux/features/base/file.include/etc/veritytab create mode 100644 gardenlinux/features/base/fstab create mode 100644 gardenlinux/features/base/info.yaml create mode 100644 gardenlinux/features/base/pkg.exclude create mode 100644 gardenlinux/features/base/pkg.include create mode 100644 gardenlinux/features/base/test/kernel-config.d/kernel-config.txt create mode 100644 gardenlinux/features/base/test/rkhunter.d/rkhunter.conf create mode 100644 gardenlinux/features/base/test/test_basics.py create mode 100644 gardenlinux/features/base/test/test_blacklisted_packages.py create mode 100644 gardenlinux/features/base/test/test_debsums.py create mode 100644 gardenlinux/features/base/test/test_disable_sysrq.py create mode 100644 gardenlinux/features/base/test/test_docker.py create mode 100644 gardenlinux/features/base/test/test_duplicate_uids.py create mode 100644 gardenlinux/features/base/test/test_groups.py create mode 100644 gardenlinux/features/base/test/test_kernel_config.py create mode 100644 gardenlinux/features/base/test/test_metadata_connection.py create mode 100644 gardenlinux/features/base/test/test_network.py create mode 100644 gardenlinux/features/base/test/test_orphaned.py create mode 100644 gardenlinux/features/base/test/test_packages_musthave.py create mode 100644 gardenlinux/features/base/test/test_partition.py create mode 100644 gardenlinux/features/base/test/test_password_hashes.py create mode 100644 gardenlinux/features/base/test/test_password_shadow.py create mode 100644 gardenlinux/features/base/test/test_platform_driver.py create mode 100644 gardenlinux/features/base/test/test_proc.py create mode 100644 gardenlinux/features/base/test/test_random.py create mode 100644 gardenlinux/features/base/test/test_rkhunter.py create mode 100644 gardenlinux/features/base/test/test_sgid_suid_files.py create mode 100644 gardenlinux/features/base/test/test_systemd.py create mode 100644 gardenlinux/features/base/test/test_tiger.py create mode 100644 gardenlinux/features/base/test/test_time_config.py create mode 100644 gardenlinux/features/base/test/test_umask.py create mode 100644 gardenlinux/features/base/test/test_users.py create mode 100644 gardenlinux/features/base/test/test_usr_ro.py create mode 100644 gardenlinux/features/base/test/tiger.d/tigerrc create mode 100644 gardenlinux/features/cloud/README.md create mode 100755 gardenlinux/features/cloud/exec.config create mode 100644 gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/00-default.cfg create mode 100644 gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/40-enable-swap-cgroup-accounting.cfg create mode 100644 gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/60-timeout.cfg create mode 100644 gardenlinux/features/cloud/file.include/etc/kernel/entry-token create mode 100755 gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/00-kernel-cmdline create mode 100755 gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/zz-update-syslinux create mode 100755 gardenlinux/features/cloud/file.include/etc/kernel/postrm.d/zz-update-syslinux create mode 100644 gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_firewire.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_fs.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_net.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_udf.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_usb.conf create mode 100755 gardenlinux/features/cloud/file.include/etc/profile.d/50-autologout.sh create mode 100644 gardenlinux/features/cloud/file.include/etc/repart.d/root.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/sysctl.d/20-cloud.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/sysctl.d/21-ipv4-settings.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/sysctl.d/22-ipv6-settings.conf create mode 100644 gardenlinux/features/cloud/file.include/etc/systemd/system/rngd.service.d/architecture.conf create mode 100644 gardenlinux/features/cloud/info.yaml create mode 100644 gardenlinux/features/cloud/pkg.include create mode 100644 gardenlinux/features/cloud/test/debsums.disable create mode 100644 gardenlinux/features/cloud/test/test_autologout.py create mode 100644 gardenlinux/features/cloud/test/test_packages_musthave.py create mode 100644 gardenlinux/features/cloud/test/test_wireguard.py create mode 100644 gardenlinux/features/firewall/README.md create mode 100755 gardenlinux/features/firewall/exec.config create mode 100644 gardenlinux/features/firewall/file.include/etc/nft.d/default.conf create mode 100644 gardenlinux/features/firewall/info.yaml create mode 100644 gardenlinux/features/firewall/pkg.include create mode 100644 gardenlinux/features/firewall/test/test_nft_config.py create mode 100644 gardenlinux/features/firewall/test/test_packages_musthave.py create mode 100644 gardenlinux/features/firewall/test/test_systemd_units.py create mode 100644 gardenlinux/features/gcp/README.md create mode 100755 gardenlinux/features/gcp/convert.gcpimage.tar.gz create mode 100755 gardenlinux/features/gcp/exec.config create mode 100644 gardenlinux/features/gcp/file.include/etc/kernel/cmdline.d/00-cmdline.cfg create mode 100644 gardenlinux/features/gcp/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/gcp/file.include/etc/modprobe.d/gce-blacklist.conf create mode 100644 gardenlinux/features/gcp/file.include/etc/sysctl.d/60-gce-network-security.conf create mode 100644 gardenlinux/features/gcp/file.include/etc/systemd/timesyncd.conf.d/00-gardenlinux-gcp.conf create mode 100755 gardenlinux/features/gcp/file.include/usr/lib/udev/google_nvme_id create mode 100644 gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/64-gce-disk-removal.rules create mode 100644 gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/65-gce-disk-naming.rules create mode 100644 gardenlinux/features/gcp/info.yaml create mode 100644 gardenlinux/features/gcp/pkg.exclude create mode 100644 gardenlinux/features/gcp/pkg.include create mode 100644 gardenlinux/features/gcp/test/test_blacklisted_packages.py create mode 100644 gardenlinux/features/gcp/test/test_packages_musthave.py create mode 100644 gardenlinux/features/gcp/test/test_users.py create mode 100644 gardenlinux/features/kvm/README.md create mode 100755 gardenlinux/features/kvm/exec.config create mode 100644 gardenlinux/features/kvm/file.exclude create mode 100644 gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/50-ignition.cfg create mode 100644 gardenlinux/features/kvm/file.include/etc/systemd/system/ignition-disable.service create mode 100644 gardenlinux/features/kvm/file.include/etc/udev/rules.d/60-onmetal.rules create mode 100755 gardenlinux/features/kvm/file.include/usr/local/sbin/update-bootloaders create mode 100644 gardenlinux/features/kvm/info.yaml create mode 100644 gardenlinux/features/kvm/pkg.include create mode 100644 gardenlinux/features/kvm/test/capabilities.d/capabilities.list create mode 100644 gardenlinux/features/kvm/test/test_packages_musthave.py create mode 100755 gardenlinux/features/log/exec.config create mode 100644 gardenlinux/features/log/file.include.stat create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/10-base-config.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/10-no-audit.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/11-loginuid.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/12-cont-fail.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/12-ignore-error.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/20-dont-audit.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/21-no32bit.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/22-ignore-chrony.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/23-ignore-filesystems.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-nispom.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-failed.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-success.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-success.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-failed.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-success.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-success.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-pci-dss-v31.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/30-stig.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/31-privileged.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/32-power-abuse.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/40-local.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/41-containers.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/42-injection.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/43-module-load.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/44-installers.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/70-einval.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/71-networking.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/99-finalize.rules create mode 100644 gardenlinux/features/log/file.include/etc/audit/rules.d/README create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.conf create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.d/10-local.conf.disabled create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.d/20-input.conf create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.d/21-input-klog.conf.disabled create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.d/29-input-mark.conf.disabled create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.d/30-server.conf.disabled create mode 100644 gardenlinux/features/log/file.include/etc/rsyslog.d/60-audit-log-service.conf.disabled create mode 100644 gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/10-minimum.conf create mode 100644 gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/20-rsyslog.conf create mode 100644 gardenlinux/features/log/info.yaml create mode 100644 gardenlinux/features/log/pkg.include create mode 100644 gardenlinux/features/metal/README.md create mode 100755 gardenlinux/features/metal/exec.config create mode 100644 gardenlinux/features/metal/file.exclude create mode 120000 gardenlinux/features/metal/file.include/etc/cron.monthly/update-pciids create mode 120000 gardenlinux/features/metal/file.include/etc/cron.monthly/update-usbids create mode 100644 gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/00-default.cfg create mode 100644 gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/metal/file.include/etc/kernel/entry-token create mode 100755 gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-kernel-cmdline create mode 100755 gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-ucode create mode 100755 gardenlinux/features/metal/file.include/etc/kernel/postinst.d/zz-update-syslinux create mode 100755 gardenlinux/features/metal/file.include/etc/kernel/postrm.d/zz-update-syslinux create mode 100644 gardenlinux/features/metal/file.include/etc/udev/rules.d/69-nostbyrot.rules create mode 100644 gardenlinux/features/metal/file.include/etc/udev/rules.d/71-intellldp.rules create mode 100755 gardenlinux/features/metal/file.include/usr/sbin/update-usbids create mode 100644 gardenlinux/features/metal/info.yaml create mode 100644 gardenlinux/features/metal/pkg.include create mode 100644 gardenlinux/features/metal/test/debsums.disable create mode 100644 gardenlinux/features/metal/test/test_packages_musthave.py create mode 100644 gardenlinux/features/openstack/README.md create mode 100755 gardenlinux/features/openstack/convert.qcow2 create mode 100755 gardenlinux/features/openstack/convert.vmdk create mode 100755 gardenlinux/features/openstack/exec.config create mode 100644 gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg create mode 100644 gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/50-datasource.cfg create mode 100644 gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg create mode 100644 gardenlinux/features/openstack/file.include/etc/cloud/ds-identify.cfg create mode 100644 gardenlinux/features/openstack/file.include/etc/default/grub.d/90_openstack_cc_ee.cfg create mode 100644 gardenlinux/features/openstack/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/openstack/info.yaml create mode 100644 gardenlinux/features/openstack/pkg.include create mode 100644 gardenlinux/features/openstack/test/test_check_files.py create mode 100644 gardenlinux/features/openstack/test/test_content_in_file.py create mode 100644 gardenlinux/features/openstack/test/test_packages_musthave.py create mode 100644 gardenlinux/features/ostreeImage/file.exclude create mode 100644 gardenlinux/features/ostreeImage/fstab create mode 100755 gardenlinux/features/ostreeImage/image.ostree.raw create mode 100644 gardenlinux/features/ostreeImage/info.yaml create mode 100644 gardenlinux/features/ostreeImage/pkg.include create mode 100755 gardenlinux/features/ostreeRepo/exec.late create mode 100644 gardenlinux/features/ostreeRepo/file.exclude create mode 100644 gardenlinux/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf create mode 100755 gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-kernel-cmdline create mode 100755 gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-syslinux create mode 100644 gardenlinux/features/ostreeRepo/fstab create mode 100755 gardenlinux/features/ostreeRepo/image.ostreeRepo.tar.gz create mode 100644 gardenlinux/features/ostreeRepo/info.yaml create mode 100644 gardenlinux/features/ostreeRepo/pkg.include create mode 100644 gardenlinux/features/server/README.md create mode 100755 gardenlinux/features/server/exec.config create mode 100755 gardenlinux/features/server/exec.early create mode 100755 gardenlinux/features/server/exec.post create mode 100644 gardenlinux/features/server/file.exclude create mode 100644 gardenlinux/features/server/file.include/etc/dracut.conf.d/25-uefi-stub.conf create mode 100644 gardenlinux/features/server/file.include/etc/dracut.conf.d/general.conf create mode 100644 gardenlinux/features/server/file.include/etc/kernel-img.conf create mode 100644 gardenlinux/features/server/file.include/etc/locale.conf create mode 100644 gardenlinux/features/server/file.include/etc/machine-id create mode 100755 gardenlinux/features/server/file.include/etc/profile.d/50-nohistory.sh create mode 100644 gardenlinux/features/server/file.include/etc/sudoers.d/keepssh create mode 100644 gardenlinux/features/server/file.include/etc/sudoers.d/wheel create mode 100644 gardenlinux/features/server/file.include/etc/sysctl.d/enable-unprivileged-user-namespaces.conf create mode 100644 gardenlinux/features/server/file.include/etc/sysctl.d/restric-dmesg.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/network/99-default.network create mode 100644 gardenlinux/features/server/file.include/etc/systemd/networkd.conf.d/00-gardenlinux-server.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/00-disable-llmnr.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/01-disable-mdns.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system.conf.d/00-gardenlinux-server.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/kexec-load@.service create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/systemd-coredump@.service.d/override.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/systemd-growfs@.service.d/override.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/systemd-resolved.service.d/wait-for-networkd.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/systemd-timesyncd.service.d/override.conf create mode 100644 gardenlinux/features/server/file.include/etc/systemd/system/tmp.mount create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/05-logo create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/10-hostname create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/20-uname create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/30-load create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/40-free create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/45-line create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/50-network create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/55-line create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/92-unattended-upgrades create mode 100755 gardenlinux/features/server/file.include/etc/update-motd.d/95-needrestart create mode 100644 gardenlinux/features/server/file.include/usr/lib/systemd/system/dbus.socket create mode 100644 gardenlinux/features/server/file.include/usr/share/pam-configs/garden create mode 100644 gardenlinux/features/server/file.include/usr/share/pam-configs/garden-extra create mode 100644 gardenlinux/features/server/info.yaml create mode 100644 gardenlinux/features/server/pkg.include create mode 100644 gardenlinux/features/server/test/capabilities.d/capabilities.list create mode 100644 gardenlinux/features/server/test/pkg.ignore create mode 100644 gardenlinux/features/server/test/systemctl.d/systemctl_disabled.list create mode 100644 gardenlinux/features/server/test/systemctl.d/systemctl_enabled.list create mode 100644 gardenlinux/features/server/test/test_autologin.py create mode 100644 gardenlinux/features/server/test/test_capabilities.py create mode 100644 gardenlinux/features/server/test/test_dmesg.py create mode 100644 gardenlinux/features/server/test/test_history.py create mode 100644 gardenlinux/features/server/test/test_kernel_parameter.py create mode 100644 gardenlinux/features/server/test/test_machine_id.py create mode 100644 gardenlinux/features/server/test/test_packages_musthave.py create mode 100644 gardenlinux/features/server/test/test_pam.py create mode 100644 gardenlinux/features/server/test/test_pam_faillock.py create mode 100644 gardenlinux/features/server/test/test_reset_env_vars.py create mode 100644 gardenlinux/features/server/test/test_systemctl.py create mode 100644 gardenlinux/features/server/test/test_tiger.py create mode 100644 gardenlinux/features/server/test/test_users.py create mode 100644 gardenlinux/features/server/test/tiger.d/tigerrc create mode 100644 gardenlinux/features/server/todo create mode 100644 gardenlinux/features/ssh/README.md create mode 100755 gardenlinux/features/ssh/exec.config create mode 100644 gardenlinux/features/ssh/file.exclude create mode 100644 gardenlinux/features/ssh/file.include.stat create mode 100644 gardenlinux/features/ssh/file.include/etc/fail2ban/jail.conf create mode 100644 gardenlinux/features/ssh/file.include/etc/ssh/ssh_config create mode 100644 gardenlinux/features/ssh/file.include/etc/ssh/sshd_config create mode 100644 gardenlinux/features/ssh/file.include/etc/systemd/system/fail2ban.service.d/override.conf create mode 100644 gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-keygen.service create mode 100644 gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-moduli.service create mode 100644 gardenlinux/features/ssh/info.yaml create mode 100644 gardenlinux/features/ssh/pkg.include create mode 100644 gardenlinux/features/ssh/test/test_ssh_authorized.py create mode 100644 gardenlinux/features/ssh/test/test_sshd.py create mode 100644 gardenlinux/features/vmware/README.md create mode 100755 gardenlinux/features/vmware/convert.ova create mode 100755 gardenlinux/features/vmware/exec.config create mode 100644 gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg create mode 100644 gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg create mode 100644 gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_enabled-datasources.cfg create mode 100644 gardenlinux/features/vmware/file.include/etc/default/grub.cfg create mode 100644 gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/10-console.cfg create mode 100644 gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/50-ignition.cfg create mode 100644 gardenlinux/features/vmware/file.include/etc/systemd/system/ignition-disable.service create mode 100755 gardenlinux/features/vmware/file.include/usr/bin/dscheck_VMwareGuestInfo create mode 100644 gardenlinux/features/vmware/file.include/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceVMwareGuestInfo.py create mode 100755 gardenlinux/features/vmware/file.include/usr/local/sbin/update-bootloaders create mode 100644 gardenlinux/features/vmware/info.yaml create mode 100755 gardenlinux/features/vmware/make-ova create mode 100644 gardenlinux/features/vmware/pkg.include create mode 100644 gardenlinux/features/vmware/test/capabilities.d/capabilities.list create mode 100644 gardenlinux/features/vmware/test/test_check_files.py create mode 100644 gardenlinux/features/vmware/test/test_content_in_file.py create mode 100644 gardenlinux/features/vmware/test/test_packages_musthave.py create mode 100644 gardenlinux/features/vmware/vmware.ovf.template create mode 100644 gardenlinux/gardenlinux.asc create mode 100755 gardenlinux/get_commit create mode 100755 gardenlinux/get_repo create mode 100755 gardenlinux/get_timestamp create mode 100755 gardenlinux/get_version create mode 100644 gardenlinux/keyring.gpg create mode 100644 gardenlinux/requirements.txt diff --git a/.github/workflows/repo.yml b/.github/workflows/repo.yml new file mode 100644 index 0000000..62158fd --- /dev/null +++ b/.github/workflows/repo.yml @@ -0,0 +1,38 @@ +name: Repo + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + workflow_dispatch: + +jobs: + debian-repo: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + arch: [ amd64, arm64 ] + steps: + - uses: actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Build the repo + run: ./build ostreeRepo-${{ matrix.arch }} + working-directory: ./debian + + gardenlinux-repo: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + arch: [ amd64, arm64 ] + platform: [ kvm, metal ] + steps: + - uses: actions/checkout@v3 + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - name: Build the repo + run: ./build ${{ matrix.platform }}_dev_curl-ostreeRepo-${{ matrix.arch }} + working-directory: ./gardenlinux diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cbd8637 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,22 @@ +MIT License + +Copyright 2017 Tianon Gravi +Copyright (c) 2023 SAP SE gardenlinux@gardenlinux.io + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..11a74f4 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# OSTree Image Builder + +> **EXPERIMENTAL** This repository is part of a proof of concept. +The Garden Linux team does not provide any support or gurantee for this repository. + +Builder for [OSTree](https://ostreedev.github.io/ostree/)-based operating system images using the [Garden Linux Builder](https://github.com/gardenlinux/builder). + +This repo contains two os builder definitions. + +The `debian` directory contains a build for a debian trixie image. + +The `gardenlinux` directory contains a build for a gardenlinux today image. +This directory contains a lot of code taken from the [gardenlinux/gardenlinux](https://github.com/gardenlinux/gardenlinux) repo. diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 0000000..24e5b0a --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1 @@ +.build diff --git a/debian/README.md b/debian/README.md new file mode 100644 index 0000000..655cc44 --- /dev/null +++ b/debian/README.md @@ -0,0 +1,43 @@ +# Debian OSTree Builder + +This is a experimental repo to build Debian OSTree images. + +## Build + +This repo is built using the [Garden Linux Builder](https://github.com/gardenlinux/builder#builder), which uses podman, see it's readme for setup instructions. + +This repo has two ostree related features (build targets). +You can run them with the following commands: + +```bash +$ ./build ostreeRepo +$ ./build ostreeImage +``` + +For just getting a bootable system, use `ostreeImage`. +This will produce a raw disk image that can be booted in qemu. +See below for the command to boot the image. + +`ostreeRepo` will give you an OSTree repo in the form of a tarball. +The output will have a name like `.build/ostreeRepo-*-trixie-*.ostreeRepo.tar.gz` +This is the foundation for building the disk image, but it is also usable as a remote repo for upgrading the system. + +## Run + +Use the `bin/start-vm` script from [Garden Linux](https://github.com/gardenlinux/gardenlinux/blob/main/bin/start-vm). + +Depending on your architecture, it should look like this: + +```bash +$ path/to/gardenlinux/bin/start-vm --no-watchdog .build/ostreeImage-arm64-trixie*.ostree.raw +``` + +```bash +$ path/to/gardenlinux/bin/start-vm --no-watchdog .build/ostreeImage-amd64-trixie*.ostree.raw +``` + +Check for the actual name of the image in the `.build` directory. + +Inside the booted vm, you can run the `ostree-upgrade` script to upgrade your OS to the latest version. + +Refer to the [OSTree command man page](https://ostreedev.github.io/ostree/man/ostree.html) for instructions of using the cli. diff --git a/debian/build b/debian/build new file mode 100755 index 0000000..6d56def --- /dev/null +++ b/debian/build @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -euo pipefail +shopt -s nullglob + +container_image=ghcr.io/gardenlinux/builder:301ce9f70045c001c5d724c2f9d1a9503e1d5ccc +container_engine=podman +target_dir=.build + +container_run_opts=( + --security-opt seccomp=unconfined + --security-opt apparmor=unconfined + --security-opt label=disable + --read-only +) + +container_cmd=() + +use_kms=0 +resolve_cname=0 + +while [ $# -gt 0 ]; do + case "$1" in + --container-image) + container_image="$2" + shift 2 + ;; + --container-engine) + container_engine="$2" + shift 2 + ;; + --container-run-opts) + declare -a "container_run_opts=($2)" + shift 2 + ;; + --privileged) + container_run_opts+=(--privileged) + container_cmd=(--second-stage) + shift + ;; + --kms) + use_kms=1 + shift + ;; + --print-container-image) + printf '%s\n' "$container_image" + exit 0 + ;; + --resolve-cname) + resolve_cname=1 + shift + ;; + --target) + target_dir="$2" + shift 2 + ;; + *) + break + ;; + esac +done + +[ -d "$target_dir" ] || mkdir "$target_dir" + +container_mount_opts=( + -v "$PWD/keyring.gpg:/builder/keyring.gpg:ro" + -v "$(realpath "$target_dir"):/builder/.build" +) + +for feature in features/*; do + if [ -d "$feature" ]; then + container_mount_opts+=(-v "$(realpath -- "$feature"):/builder/$feature:ro") + fi +done + +if [ "$container_image" = localhost/builder ]; then + dir="$(dirname -- "$(realpath -- "${BASH_SOURCE[0]}")")" + "$container_engine" build -t "$container_image" "$dir" +fi + +repo="$(./get_repo)" +commit="$(./get_commit)" +timestamp="$(./get_timestamp)" +default_version="$(./get_version)" + + +if [ "$resolve_cname" = 1 ]; then + arch="$("$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" dpkg --print-architecture)" + cname="$("$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" /builder/parse_features --feature-dir /builder/features --default-arch "$arch" --default-version "$default_version" --cname "$1")" + short_commit="$(head -c 8 <<< "$commit")" + echo "$cname-$short_commit" + exit 0 +fi + +make_opts=( + REPO="$repo" + COMMIT="$commit" + TIMESTAMP="$timestamp" + DEFAULT_VERSION="$default_version" +) + +if [ "$use_kms" = 1 ]; then + for e in AWS_DEFAULT_REGION AWS_REGION AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN; do + if [ -n "${!e-}" ]; then + make_opts+=("$e=${!e}") + fi + done +fi + +if [ -d cert ]; then + container_mount_opts+=(-v "$PWD/cert:/builder/cert:ro") +fi + +"$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" ${container_cmd[@]+"${container_cmd[@]}"} make --no-print-directory -C /builder "${make_opts[@]}" "$@" diff --git a/debian/features/ostreeImage/file.exclude b/debian/features/ostreeImage/file.exclude new file mode 100644 index 0000000..2e0f9df --- /dev/null +++ b/debian/features/ostreeImage/file.exclude @@ -0,0 +1 @@ +/boot/efi/loader/random-seed diff --git a/debian/features/ostreeImage/fstab b/debian/features/ostreeImage/fstab new file mode 100644 index 0000000..bcb6fda --- /dev/null +++ b/debian/features/ostreeImage/fstab @@ -0,0 +1,3 @@ +# +LABEL=EFI /boot/efi vfat umask=0077 type=uefi,size=1G +LABEL=ROOT / ext4 rw,prjquota,discard diff --git a/debian/features/ostreeImage/image.ostree.raw b/debian/features/ostreeImage/image.ostree.raw new file mode 100755 index 0000000..19d2fd7 --- /dev/null +++ b/debian/features/ostreeImage/image.ostree.raw @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +export PATH="/builder/image.d:$PATH" + +rootfs_work="$(mktemp -d)" +mount -t tmpfs tmpfs "$rootfs_work" + +MYROOT="$(mktemp -d)" +mount -t tmpfs tmpfs "$MYROOT" +mkdir -p "$MYROOT"/sysroot +mkdir -p "$MYROOT"/sysroot/ostree/deploy +mkdir -p "$MYROOT"/sysroot/ostree/deploy/debian/var +OSTREE_SYSROOT="$MYROOT/sysroot" +OSTREE_REPO=$OSTREE_SYSROOT/ostree/repo +OSTREE_REF="debian/testing/$BUILDER_ARCH" + +rootfs="$1" +output="$2" + +tar xf "$rootfs" -C "$rootfs_work" + + +mkdir -p $OSTREE_REPO +mkdir -p $OSTREE_SYSROOT +download="$(mktemp -d)" +pushd $download +curl --remote-name http://ostree.gardenlinux.io/debian-testing-$BUILDER_ARCH.tar.gz +tar xf debian-testing-$BUILDER_ARCH.tar.gz --directory $OSTREE_REPO +ls -l $OSTREE_REPO +popd +rm -rf $download +ostree admin deploy --karg=root=LABEL=ROOT --karg-append=rw --karg-append=efi=runtime --sysroot=$OSTREE_SYSROOT --os=debian $OSTREE_REF + +boot_hash=`ls "$OSTREE_SYSROOT"/ostree/boot.1.1/debian/` +mkdir -p "$OSTREE_SYSROOT"/ostree/boot.1.1/debian/$boot_hash/0/sysroot + +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/debian/var/home +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/debian/var/home/user +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/debian/var/roothome +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/debian/var/opt +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/debian/var/srv + +chown 1000:1000 "$OSTREE_SYSROOT"/ostree/deploy/debian/var/home/user + +# Build disk image, this is hacky as of now, needs rework +# Setup bootloader +boot_dir=$(mktemp -d) +cp -r $OSTREE_SYSROOT/boot/* $boot_dir +LOADER_TEMP=$(mktemp -d) +rm -rf $boot_dir/loader +# move to temp dir to avoid errors with systemd-boot install +mv $boot_dir/loader.1 $LOADER_TEMP +mkdir -p $rootfs_work/boot/efi +mount --bind $boot_dir $rootfs_work/boot/efi +mount --rbind /proc $rootfs_work/proc +mount --rbind /sys $rootfs_work/sys +SYSTEMD_ESP_PATH=/boot/efi chroot $rootfs_work /usr/bin/bootctl --no-variables install +umount -l $rootfs_work/proc +umount -l $rootfs_work/sys +umount $rootfs_work/boot/efi +# recover from temp dir +cp -r $LOADER_TEMP/* $boot_dir/loader.1 +cp -r $LOADER_TEMP/* $boot_dir/loader +cp $boot_dir/loader/loader.1/entries/* $boot_dir/loader/entries +cat $boot_dir/loader/entries/* +echo 'timeout 7' > $boot_dir/loader/loader.conf + +efi_partition=$(mktemp) +root_partition=$(mktemp) +partitions=$(mktemp) + +# fixme: make disk size dynamic +truncate -s 500M "$efi_partition" +# make_reproducible_vfat $OSTREE_SYSROOT/boot "$efi_partition" +make_reproducible_vfat -t 11111111 $boot_dir "$efi_partition" +size_uefi=$(du -b "$efi_partition" | awk '{ padded_size = $1 + (MB - ($1 % MB) % MB); print (padded_size / MB) }' "MB=1048576") +part_uuid_uefi=b0e0359c-007b-4361-a0d1-a7ca2d73fe3c +echo -e "$part_uuid_uefi\tuefi\t$size_uefi\t0\t$efi_partition\tEFI" >> "$partitions" + +truncate -s 5G "$root_partition" +make_reproducible_ext4 -t 11111111 -l ROOT "$MYROOT"/sysroot "$root_partition" +size_rootfs=$(du -b "$root_partition" | awk '{ padded_size = $1 + (MB - ($1 % MB) % MB); print (padded_size / MB) }' "MB=1048576") +part_uuid_rootfs=a9bef950-8218-4888-9f1c-1ad8bb481807 +echo -e "$part_uuid_rootfs\tlinux\t$size_rootfs\t0\t$root_partition\tROOT" >> "$partitions" + +makedisk $rootfs_work "$output" < "$partitions" diff --git a/debian/features/ostreeImage/info.yaml b/debian/features/ostreeImage/info.yaml new file mode 100644 index 0000000..6bc5f79 --- /dev/null +++ b/debian/features/ostreeImage/info.yaml @@ -0,0 +1,2 @@ +description: 'image-based system using OSTree' +type: platform diff --git a/debian/features/ostreeImage/pkg.include b/debian/features/ostreeImage/pkg.include new file mode 100644 index 0000000..2bf617d --- /dev/null +++ b/debian/features/ostreeImage/pkg.include @@ -0,0 +1 @@ +systemd-boot diff --git a/debian/features/ostreeRepo/exec.config b/debian/features/ostreeRepo/exec.config new file mode 100755 index 0000000..295c59d --- /dev/null +++ b/debian/features/ostreeRepo/exec.config @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +systemctl enable systemd-networkd +systemctl enable systemd-resolved diff --git a/debian/features/ostreeRepo/exec.late b/debian/features/ostreeRepo/exec.late new file mode 100755 index 0000000..99cd193 --- /dev/null +++ b/debian/features/ostreeRepo/exec.late @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo OSTree builder +ostree --version + +OSTREE_SYSROOT=/sysroot + +kernel="$(find /boot -name 'vmlinuz-*' | sort -V | tail -n 1)" +version="${kernel#*-}" + +# Adapt to ostree root-fs requirements, see https://ostreedev.github.io/ostree/adapting-existing/ + +declare -A TOPLEVEL_LINKS=( + ["home"]="var/home" + ["media"]="run/media" + ["mnt"]="var/mnt" + ["opt"]="var/opt" + # ["ostree"]="$OSTREE_SYSROOT/ostree" + # ["root"]="var/roothome" + ["srv"]="var/srv" +) + +for link in "${!TOPLEVEL_LINKS[@]}"; do + target=${TOPLEVEL_LINKS[$link]} + echo mv $link $(dirname $target) + mv $link $(dirname $target) + echo ln -sf $target $link + ln -sf $target $link +done + +mv root var/roothome +ln -sf var/roothome root + +ln -s sysroot/ostree ostree + +echo $(date --utc +%Y-%m-%dT%H:%M:%S%Z) > /timestamp + +# Using a heredoc to create the update script in the root-fs +# This script is a workaround for multiple issues +# - the *.origin files lack the remote in the refspec +# - OSTree won't update the bootloader entries on the ESP +# Syntax taken from https://tldp.org/LDP/abs/html/here-docs.html +( +cat <<'EOF' +#!/usr/bin/bash + +echo Performing OSTree upgrade + +# Workaround for missing remote in origin file +find /ostree/deploy/*/deploy/*.origin | while read origin_file +do + sudo sed -i 's#refspec=debian#refspec=origin:debian#g' $origin_file +done + +sudo ostree admin upgrade + +# Workaround for wrong location of boot loader entries +sudo mkdir -p /var/ESPmnt +sudo mount /dev/vda1 /var/ESPmnt +sudo cp /boot/loader/entries/* /var/ESPmnt/loader/entries +sudo umount /dev/vda1 + +ostree admin status + +echo Done upgrading. Run 'sudo systemctl reboot' to boot into the latest commit. +EOF +) > /usr/bin/ostree-upgrade + +chmod +x /usr/bin/ostree-upgrade + +mkdir -p /sysroot + +mkdir -p /boot/efi/Default + +unshare --mount bash -c 'mount -t tmpfs none /sys && mount --bind /usr/bin/false /usr/bin/systemd-detect-virt && "$@"' \ + DRACUT_COMPRESS_XZ="$(command -v xz)" dracut \ + --no-hostonly \ + --force \ + --kver "${version}" \ + --add "ostree" \ + --modules "bash dash systemd systemd-initrd kernel-modules kernel-modules-extra terminfo udev-rules dracut-systemd base fs-lib shutdown" \ + --reproducible \ + "/boot/initrd.img-${version}" + +if ! command -v python3 > /dev/null; then + mkdir -p /etc/kernel/install.d + ln -s /usr/bin/true /etc/kernel/install.d/60-ukify.install +fi +SYSTEMD_ESP_PATH=/boot/efi kernel-install --verbose --entry-token literal:Default add "$version" "$kernel" + +# ostree kernel location: https://ostreedev.github.io/ostree/deployment/#contents-of-a-deployment +mkdir -p /usr/lib/modules/$version/ +cp $kernel /usr/lib/modules/$version/vmlinuz +cp "/boot/initrd.img-$version" /usr/lib/modules/$version/initramfs.img + +# Build fails if we do this here instead of in image.ostree.raw +# mv /etc /usr/etc + +useradd --user-group --home-dir /var/home/user --shell /usr/bin/bash user +printf "user:user123!" | chpasswd +echo "%sudo ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/nopasswd +usermod -aG sudo user + +dpkg --list --no-pager | grep '^ii' | awk '{printf "%-40s %s\n", $2, $3}' | tee /sbom.txt + +# Delete apt because this is an image-based system +rm -f /etc/cron.daily/apt-compat +rm -f /etc/logrotate.d/apt +rm -f /etc/systemd/system/timers.target.wants/apt-daily-upgrade.timer +rm -f /etc/systemd/system/timers.target.wants/apt-daily.timer +rm -f /usr/bin/apt* +rm -f /usr/bin/debconf-apt-progress +rm -rf /etc/apt +rm -rf /etc/dpkg +rm -rf /usr/lib/apt/ +rm -rf /usr/lib/dpkg/methods/apt/ +rm -rf /usr/lib/systemd/system/apt* +rm -rf /usr/share/bash-completion/completions/apt +rm -rf /usr/share/bug/apt/ +rm -rf /var/cache/apt/ +rm -rf /var/lib/apt/ +rm -rf /var/lib/dpkg +rm -rf /var/log/apt/ +rm -rf /usr/sbin/dpkg* +rm -rf /usr/lib/dpkg* +rm -rf /usr/share/dpkg* +rm -rf /usr/libexec/dpkg* +rm -rf /usr/share/doc* +rm -rf /usr/share/locale +rm -rf /usr/share/man + +find "/var/log/" -type f -delete diff --git a/debian/features/ostreeRepo/exec.post b/debian/features/ostreeRepo/exec.post new file mode 100755 index 0000000..47f00e6 --- /dev/null +++ b/debian/features/ostreeRepo/exec.post @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +rootfs="$1" + +echo "exec.post" +echo "rootfs: $rootfs" +ls -la "$rootfs" + +rm -rf "$rootfs"/usr/bin/dpkg* diff --git a/debian/features/ostreeRepo/file.exclude b/debian/features/ostreeRepo/file.exclude new file mode 100644 index 0000000..2e0f9df --- /dev/null +++ b/debian/features/ostreeRepo/file.exclude @@ -0,0 +1 @@ +/boot/efi/loader/random-seed diff --git a/debian/features/ostreeRepo/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf b/debian/features/ostreeRepo/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf new file mode 100644 index 0000000..7062620 --- /dev/null +++ b/debian/features/ostreeRepo/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf @@ -0,0 +1,3 @@ +[Service] +ExecStart= +ExecStart=-/sbin/agetty --autologin user -o '-p -f -- \\u' --keep-baud 115200,38400,9600 %I $TERM diff --git a/debian/features/ostreeRepo/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf b/debian/features/ostreeRepo/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf new file mode 100644 index 0000000..1ef5a03 --- /dev/null +++ b/debian/features/ostreeRepo/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf @@ -0,0 +1,3 @@ +[Service] +ExecStart= +ExecStart=/lib/systemd/systemd-networkd-wait-online --any diff --git a/debian/features/ostreeRepo/file.include/network/99-default.network b/debian/features/ostreeRepo/file.include/network/99-default.network new file mode 100644 index 0000000..81f36eb --- /dev/null +++ b/debian/features/ostreeRepo/file.include/network/99-default.network @@ -0,0 +1,5 @@ +[Match] +Name=en* eth* + +[Network] +DHCP=yes diff --git a/debian/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf b/debian/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf new file mode 100644 index 0000000..fd6877b --- /dev/null +++ b/debian/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf @@ -0,0 +1,2 @@ +d /var/log/journal 0755 root root - +d /var/log/audit 0755 root root - diff --git a/debian/features/ostreeRepo/fstab b/debian/features/ostreeRepo/fstab new file mode 100644 index 0000000..bcb6fda --- /dev/null +++ b/debian/features/ostreeRepo/fstab @@ -0,0 +1,3 @@ +# +LABEL=EFI /boot/efi vfat umask=0077 type=uefi,size=1G +LABEL=ROOT / ext4 rw,prjquota,discard diff --git a/debian/features/ostreeRepo/image.ostreeRepo.tar.gz b/debian/features/ostreeRepo/image.ostreeRepo.tar.gz new file mode 100755 index 0000000..063b41f --- /dev/null +++ b/debian/features/ostreeRepo/image.ostreeRepo.tar.gz @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +export PATH="/builder/image.d:$PATH" + +rootfs_work="$(mktemp -d)" +mount -t tmpfs tmpfs "$rootfs_work" + +MYROOT="$(mktemp -d)" +mount -t tmpfs tmpfs "$MYROOT" +mkdir -p "$MYROOT"/sysroot +OSTREE_SYSROOT="$MYROOT/sysroot" +OSTREE_REPO=$OSTREE_SYSROOT/ostree/repo +OSTREE_REF="debian/testing/$BUILDER_ARCH" + +rootfs="$1" +output="$2" + +tar xf "$rootfs" -C "$rootfs_work" + +mv "$rootfs_work"/etc "$rootfs_work"/usr/etc + +mkdir -p $OSTREE_REPO + +if curl --head --silent --fail http://ostree.gardenlinux.io/debian-testing-$BUILDER_ARCH.tar.gz 2> /dev/null; + then + echo "Using http://ostree.gardenlinux.io/debian-testing-$BUILDER_ARCH.tar.gz" + mkdir -p $OSTREE_REPO + download="$(mktemp -d)" + pushd $download + curl --remote-name http://ostree.gardenlinux.io/debian-testing-$BUILDER_ARCH.tar.gz + tar xf debian-testing-$BUILDER_ARCH.tar.gz --directory $OSTREE_REPO + popd + rm -rf $download + else + echo "Coud not download http://ostree.gardenlinux.io/debian-testing-$BUILDER_ARCH.tar.gz, building new repo" + ostree init --mode=archive --repo=$OSTREE_REPO + ostree admin init-fs --modern $OSTREE_SYSROOT + ostree admin os-init --sysroot=$OSTREE_SYSROOT debian + ostree config --repo=$OSTREE_REPO set sysroot.bootloader none + ostree remote --repo=$OSTREE_REPO add --no-gpg-verify --no-sign-verify origin http://ostree.gardenlinux.io/debian-testing-$BUILDER_ARCH $OSTREE_REF +fi + +ostree commit --repo=$OSTREE_REPO --branch $OSTREE_REF --skip-if-unchanged -s "Debian testing build $(date --utc +%Y-%m-%dT%H:%M%Z)" "$rootfs_work" + +ostree log --repo=$OSTREE_REPO $OSTREE_REF + +ostree summary --update --repo=$OSTREE_REPO + +ostree summary --view --repo=$OSTREE_REPO + +tar --directory $OSTREE_REPO --create --mtime="@$BUILDER_TIMESTAMP" --sort name --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime . | gzip > "$output" diff --git a/debian/features/ostreeRepo/info.yaml b/debian/features/ostreeRepo/info.yaml new file mode 100644 index 0000000..6bc5f79 --- /dev/null +++ b/debian/features/ostreeRepo/info.yaml @@ -0,0 +1,2 @@ +description: 'image-based system using OSTree' +type: platform diff --git a/debian/features/ostreeRepo/pkg.include b/debian/features/ostreeRepo/pkg.include new file mode 100644 index 0000000..1e99267 --- /dev/null +++ b/debian/features/ostreeRepo/pkg.include @@ -0,0 +1,173 @@ +adduser +apparmor +base-files +base-passwd +bash +bash-completion +bind9-host +bind9-libs +bsdextrautils +bsdutils +ca-certificates +chrony +cloud-image-utils +cloud-init +cloud-utils +coreutils +cpio +curl +dash +dbus +dbus-bin +dbus-daemon +dbus-session-bus-common +dbus-system-bus-common +debconf +debian-archive-keyring +debianutils +diffutils +distro-info-data +dmsetup +dpkg +dracut +e2fsprogs +eject +ethtool +fdisk +file +findutils +gcc-13-base +gdisk +genisoimage +gettext-base +gpgv +grep +groff-base +gzip +hostname +hyperv-daemons +init +init-system-helpers +iproute2 +iptables +iputils-ping +isc-dhcp-client +klibc-utils +kmod +less +linux-base +linux-image-$arch +locales +login +logsave +lsb-release +man-db +manpages +mawk +media-types +mokutil +mount +nano +ncurses-base +ncurses-bin +netbase +netplan.io +network-manager +openssh-server +openssh-sftp-server +openssl +ostree +ostree-boot +passwd +pci.ids +pciutils +perl-base +podman +polkitd +procps +psmisc +python-apt-common +python3 +python3-apt +python3-attr +python3-blinker +python3-certifi +python3-cffi-backend +python3-chardet +python3-charset-normalizer +python3-configobj +python3-cryptography +python3-dbus +python3-debconf +python3-debian +python3-debianbts +python3-distro +python3-distro-info +python3-httplib2 +python3-idna +python3-jinja2 +python3-json-pointer +python3-jsonpatch +python3-jsonschema +python3-jwt +python3-markdown-it +python3-markupsafe +python3-mdurl +python3-minimal +python3-netifaces +python3-oauthlib +python3-pkg-resources +python3-pycurl +python3-pygments +python3-pyparsing +python3-pyrsistent +python3-pysimplesoap +python3-reportbug +python3-requests +python3-rich +python3-serial +python3-six +python3-urllib3 +python3-yaml +python3.11 +python3.11-minimal +qemu-utils +readline-common +reportbug +runit-helper +screen +sed +sensible-utils +sgml-base +shim-signed +shim-signed-common +shim-unsigned +socat +sudo +systemd +systemd-boot +systemd-dev +systemd-resolved +systemd-sysv +sysvinit-utils +tar +tcpdump +traceroute +tzdata +tzdata-legacy +ucf +udev +usr-is-merged +util-linux +util-linux-extra +uuid-runtime +vim +vim-common +vim-runtime +vim-tiny +wget +whiptail +xml-core +xz-utils +zlib1g +zstd diff --git a/debian/get_commit b/debian/get_commit new file mode 100755 index 0000000..09ec10c --- /dev/null +++ b/debian/get_commit @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +dir="$(dirname "${BASH_SOURCE[0]}")" +cd "$dir" +[ -z "$(git status --porcelain 2> /dev/null)" ] && git rev-parse HEAD 2> /dev/null || echo local diff --git a/debian/get_repo b/debian/get_repo new file mode 100755 index 0000000..6a0070a --- /dev/null +++ b/debian/get_repo @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +echo "http://deb.debian.org/debian" diff --git a/debian/get_timestamp b/debian/get_timestamp new file mode 100755 index 0000000..b91fb68 --- /dev/null +++ b/debian/get_timestamp @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +echo 0 diff --git a/debian/get_version b/debian/get_version new file mode 100755 index 0000000..8f8f53b --- /dev/null +++ b/debian/get_version @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +echo trixie diff --git a/debian/keyring.gpg b/debian/keyring.gpg new file mode 100644 index 0000000000000000000000000000000000000000..645117818b1dfe2e727fd7debeaae88f0b4b60c3 GIT binary patch literal 56156 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

k;vR>CnQb8x5h>K$$Vqc}4pq9Fe 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%0D}K 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 by%>saepKu{E~Vi_$;NA`0SdA_1&q8}$J0{o literal 0 HcmV?d00001 diff --git a/gardenlinux/.gitignore b/gardenlinux/.gitignore new file mode 100644 index 0000000..247d2e6 --- /dev/null +++ b/gardenlinux/.gitignore @@ -0,0 +1,39 @@ +/.build +/.buildlogs +/.packages +/.repository +/repository/reprepro/db +/repository/reprepro/logs +/features/_dev/id_dev +/features/_dev/id_dev.pub +/features/_dev/file.include/authorized_keys +/cert/cfssl +/cert/*.key +/cert/*.arn +/cert/*.crt +/cert/*.pub +/cert/*.p12 +/cert/*.csr +/cert/*.full +/cert/*.chain +/cert/*.esl +/cert/*.auth +/cert/*.aws-efivars +/cert/GUID.txt +!/cert/gardenlinux-*.crt +!/cert/gardenlinux-*.chain +!/cert/gardenlinux-*.auth +!/cert/gardenlinux-*.aws-efivars +!/cert/gardenlinux.kernel.sign.crt +!/cert/gardenlinux.sign.pub +/features/metal/file.include/lib/firmware/ +/container/*/_pipfiles +.idea +__pycache__ +/dev +/test.log +/test_results.json +/tests/*.log +/tests/*.xml +make_targets.cache +/bin/*.vars diff --git a/gardenlinux/VERSION b/gardenlinux/VERSION new file mode 100644 index 0000000..aad82bc --- /dev/null +++ b/gardenlinux/VERSION @@ -0,0 +1,4 @@ +# the version defined in this file specifies the _next_ release version of gardenlinux, as well as +# (implicitly) the gardenlinux epoch (days since 2020-04-01) to use as dependency timestamp. +# use `today` to build against latest +today diff --git a/gardenlinux/VERSION.md b/gardenlinux/VERSION.md new file mode 100644 index 0000000..0089763 --- /dev/null +++ b/gardenlinux/VERSION.md @@ -0,0 +1,14 @@ +# Versioning + +Garden Linux is versioned by the day of the created binary release (image): + +The Release 1 would have been when the project was created on March 31st 2020. +Since there was much more coding to be done the first stable release was 27 -> +created on April 27th 2020. + +Release day (as in Date) - April 1st 2020 (+1) = version number + + 27.04.2020 - 01.04.2020 + 1 = 27 + 01.06.2020 - 01.04.2020 + 1 = 62 + +All this calculation is done in `bin/garden-version`. diff --git a/gardenlinux/bin/.constants.sh b/gardenlinux/bin/.constants.sh new file mode 100644 index 0000000..193c180 --- /dev/null +++ b/gardenlinux/bin/.constants.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash + +# constants of the universe +export TZ='UTC' LC_ALL='C' +umask 0022 +scriptsDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +featureDir="$scriptsDir/../features" +self="$(basename "$0")" +build_os="$(uname -s)" + +# Determinate the OS for GNU tools +if [ "Darwin" == $build_os ]; then + + getopt_gnu="unset" + # Test for possible pathes of getopt + # (Home)Brew legacy path + if [[ -f "/usr/local/opt/gnu-getopt/bin/getopt" ]]; then + getopt_gnu="/usr/local/opt/gnu-getopt/bin/getopt" + fi + + # (Home)Brew path + if [[ -f "/opt/homebrew/opt/gnu-getopt/bin/getopt" ]]; then + getopt_gnu="/opt/homebrew/opt/gnu-getopt/bin/getopt" + fi + + # MacPorts path + if [[ -f "/opt/local/bin/getopt" ]]; then + getopt_gnu="/opt/local/bin/getopt" + fi + + # Fail when we can not find any GNU getopt package + if [ "unset" == $getopt_gnu ]; then + echo "No GNU getopt binary for macOS found. Please make sure to install it by Brew or MacPorts." + exit 1 + fi + +else + getopt_gnu="getopt" +fi + +options=$($getopt_gnu -n "$BASH_SOURCE" -o '+' --long 'flags:,flags-short:,help:,usage:,sample:' -- "$@") +dFlags='help,version' +dFlagsShort='h?' +dHelp= +dUsage= + +__cgetopt() { + eval "set -- $options" # in a function since otherwise "set" will overwrite the parent script's positional args too + unset options + local usagePrefix='usage:' + local samplePrefix=' eg.:' + + while true; do + local flag="$1"; shift + case "$flag" in + --flags) dFlags="${dFlags:+$dFlags,}$1"; shift ;; + --flags-short) dFlagsShort="${dFlagsShort}$1"; shift ;; + --help) dHelp+="$1"$'\n'; shift ;; + --usage) dUsage+="$usagePrefix $self${1:+ $1}"$'\n' + usagePrefix=' ' + samplePrefix=' eg.:' + shift + ;; + --sample) dUsage+="$samplePrefix $self${1:+ $1}"$'\n' + samplePrefix=' ' + usagePrefix=$'\n''usage:' + shift + ;; + --) break ;; + *) echo >&2 "error: unexpected $BASH_SOURCE flag '$flag'"; exit 1 ;; + esac + done + local dup=$(sort <<< ${dFlags//,/$'\n'} | uniq -d) + [ -n "$dup" ] && { echo "error: duplicate in flags definition \"${dup//$'\n'/\" \"}\""; exit 1; } + dup=$(grep -o . <<< ${dFlagsShort} | sort | uniq -d) + [ -n "$dup" ] && { echo "error: duplicate in flags-short definition \"${dup//$'\n'/\" \"}\""; exit 1; } + + return 0 +} +__cgetopt + +usage() { + echo -n "${dUsage:+$dUsage$'\n'}" + echo "$self: gardenlinux build version $($scriptsDir/garden-version)" +} + +xusage() { + echo "$self: gardenlinux build version $($scriptsDir/garden-version)"$'\n' + echo -n "${dUsage:+$dUsage$'\n'}" + echo "$dHelp" +} + +eusage() { + if [ "$#" -gt 0 ]; then + echo >&2 "error: $*" + fi + echo >&2 + usage >&2 + exit 1 +} + +_dgetopt() { + $getopt_gnu -n "error" \ + -o "+$dFlagsShort" \ + --long "$dFlags" \ + -- "$@" \ + || eusage +} + +dgetopt='options="$(_dgetopt "$@")"; eval "set -- $options"; unset options' +dgetopt-case() { + local flag="$1"; shift + + case "$flag" in + -h|'-?'|--help) xusage; exit 0 ;; + --version) echo "version: $($scriptsDir/garden-version)"; exit 0 ;; + esac +} + +filter_comment () { + sed "s/#.*$//;/^$/d;s/^[[:space:]]*//;s/[[:space:]]*$//" +} + +filter_variables () { + if [ "${1+defined}" ]; then + if [ "$1" == "" ]; then + echo "can't filter the variables, empty arch provided via arg!" 1>&2 + exit 1 + fi + arch=$1 /usr/bin/envsubst + elif [ "${dpkgArch+defined}" ]; then + arch=$dpkgArch /usr/bin/envsubst + if [ "$dpkgArch" == "" ]; then + echo "can't filter the variables, empty dpkgArch!" 1>&2 + exit 1 + fi + elif [ "${arch+defined}" ]; then + arch=$arch /usr/bin/envsubst + if [ "$arch" == "" ]; then + echo "can't filter the variables, empty arch!" 1>&2 + exit 1 + fi + else + echo "can't filter the variables, nothing defined!" 1>&2 + exit 1 + fi +} + +filter_if() { + awk -F ']' ' + { + for(i=1;i<=NF-1;i++) { + a=$i + gsub(/ /,"",a) + gsub(/[\[\]]/,"",a) + split(a,arn,"!=") + if (length(arn) > 1) { + if (arn[1] == arn[2]) {next} + } + else { + split(a,are,"=") + if (length(are) > 1) { + if (are[1] != are[2]) {next} + } + } + } + gsub(/ /,"",$NF) + print $NF + }' +} diff --git a/gardenlinux/bin/.dpkg-arch.sh b/gardenlinux/bin/.dpkg-arch.sh new file mode 100755 index 0000000..a23b90f --- /dev/null +++ b/gardenlinux/bin/.dpkg-arch.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +thisDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +source "$thisDir/.constants.sh" \ + '' \ + 'rootfs' + +eval "$dgetopt" +while true; do + flag="$1"; shift + dgetopt-case "$flag" + case "$flag" in + --) break ;; + *) eusage "unknown flag '$flag'" ;; + esac +done + +targetDir="${1:-}"; shift || eusage 'missing target-dir' +[ -n "$targetDir" ] + +arch="$("$thisDir/garden-chroot" "$targetDir" dpkg --print-architecture)" + +echo "$arch" | awk -F- '{ print $NF }' diff --git a/gardenlinux/bin/.fix-apt-comments.sh b/gardenlinux/bin/.fix-apt-comments.sh new file mode 100755 index 0000000..1e8b545 --- /dev/null +++ b/gardenlinux/bin/.fix-apt-comments.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +thisDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +source "$thisDir/.constants.sh" \ + ' [file ...]' \ + '0.7.22 rootfs/etc/apt/apt.conf.d/example' + +eval "$dgetopt" +while true; do + flag="$1"; shift + dgetopt-case "$flag" + case "$flag" in + --) break ;; + *) eusage "unknown flag '$flag'" ;; + esac +done + +aptVersion="${1:-}"; shift || eusage 'missing apt-version' +[ "$#" -gt 0 ] || eusage 'missing file(s)' + +# support for "apt.conf" comments of the style "# xxx" was added in 0.7.22 +# (https://salsa.debian.org/apt-team/apt/commit/81e9789b12374073e848c73c79e235f82c14df44) +if dpkg --compare-versions "$aptVersion" '>=' '0.7.22~'; then + exit +fi + +sed -ri -e 's!^#!//!' "$@" diff --git a/gardenlinux/bin/.tar-exclude b/gardenlinux/bin/.tar-exclude new file mode 100644 index 0000000..fcf3ea0 --- /dev/null +++ b/gardenlinux/bin/.tar-exclude @@ -0,0 +1,44 @@ +# the file we store the "epoch" of a given rootfs in +./garden-epoch + +# "/dev" is a special case in "garden-tar" +#./dev/** +./proc/** +./sys/** +./run/** +./tmp/** + +# targeted exclusions to get rid of everything except "/var/cache/apt/archives/partial" and "/var/lib/apt/lists/partial" +# (https://salsa.debian.org/apt-team/apt/commit/1cd1c398d18b78f4aa9d882a5de5385f4538e0be) +./var/cache/apt/*.bin +./var/cache/apt/archives/*.deb +./var/cache/apt/archives/lock +./var/lib/apt/lists/*Packages* +./var/lib/apt/lists/*Release* +./var/lib/apt/lists/lock +# https://salsa.debian.org/apt-team/apt/commit/5555ef9850b7e66aa02d39bb7d624fdf3e43edb2 (APT 0.9.14 removed support for /var/state/apt) +./var/state/apt/lists/*Packages* +./var/state/apt/lists/*Release* +./var/state/apt/lists/lock + +# ends up with host-kernel info +./etc/apt/apt.conf.d/01autoremove-kernels + +# useful data in these, but includes timestamps too +./var/log/apt/history.log +./var/log/apt/term.log + +# wheezy-only file which contains host-kernel info +./run/motd.dynamic + +# wheezy-only file which APT doesn't even use (and has timestamp embedded in binary data) +# see https://github.com/debuerreotype/debuerreotype/issues/7 +./etc/apt/trustdb.gpg + +# a wheezy-only file which only shows up when building via debootstrap in buster+ for some reason +./run/shm/.run-transition + +# Debian creates this file reproducibly, but Ubuntu does not +# (according to "man 1 journalctl", this is automatically recreated by "journalctl --update-catalog") +# Tails also removes this file to achieve reproducibility (https://labs.riseup.net/code/projects/tails/repository/revisions/b1e05c8aac12fc79293f6a220b40a538d4f38c51/diff/config/chroot_local-hooks/99-zzzzzz_reproducible-builds-post-processing) +./var/lib/systemd/catalog/database diff --git a/gardenlinux/bin/README.md b/gardenlinux/bin/README.md new file mode 100644 index 0000000..c19b064 --- /dev/null +++ b/gardenlinux/bin/README.md @@ -0,0 +1,225 @@ +# Garden Linux Binary Set +## General +This directory contains many scripts to manage the Garden Linux build process. While most of them are used in a Docker container during the Garden Linux build (e.g. `garden-build`, `garden-feat`, `makepart`, ...) there are also several useful scripts (e.g. `start-vm`, `inject-sshkey`, ...) that may be used afterwards. + +## Overview +By the given directory, we distinguish between scripts that are mandatory for the Garden Linux build process and scripts that are optional but very useful for handling a final Garden Linux artifact. Therefore, we describe them by the given two sections and only describe the standalone usable ones in detail. + +### Mandatory for Garden Linux Build Process +| Name | Descriptions | +|--|--| +| check-pkgs-availability.py | Scans the `features/*/pkg.include` and checks if all pkgs are available in the garden linux apt repository | +| check-pkgs-pipelines.py | Generates a status report for all package pipelines. By default it only includes packages that have a failed pipeline status. Utilizes the public gitlab API. | +| garden-apt-get | Handles `apt-get` commands in `chroot` env | +| garden-build | Main script to build Garden Linux (creates a `chroot` env in a Docker container) | +| garden-chroot | Performs all necessary steps to integrate Garden Linux features | +| garden-config | Performs adjustments by activated `features` | +| garden-feat.go | Helper script to validate defined `features` (include, exclude, depends) | +| garden-fixup | Helper script to clean up uneeded files after build process | +| garden-fs | Creates the filesystem layout by `features` (creates: `fstab`) | +| garden-init | Debootstraps Garden Linux | +| garden-slimify | Creates a slimifyed version of Garden Linux | +| garden-tar | Creates a `.tar.xf` image of the build env from `chroot` env | +| garden-version | Creates a version schema | +| gen_make_targets | Generates `make` targets from the `flavour.yaml` | +| get-arch | Evaluates the base arch of the build host | +| makedisk | Creates the disks during the build process in the `chroot` env | +| makepart | Creates the partitions during the build process in the `chroot` env | +| shrink.sh | Shrinks the filesystem of a image | + +### Standalone usable scripts +| Name | Descriptions | +|--|--| +| convert_raw_to_vhd | Script to convert a `.raw` image file to a `.vhd` (VMWare) | +| inject-sshkey | Injects a SSH publickey (for a `specific user` or `root`) into a Garden Linux artifact | +| start-vm | Starts a QEMU/KVM based VM in text mode with activated SSH `hostfwd` by a given image file | +| make-ali-ami | Integrate/orchestrate `Aliyun` platform by a given image | +| make-azure-ami | Integrate/orchestrate `Azure` platform by a given image | +| make-ec2-ami | Integrate/orchestrate `EC2` platform by a given image | +| make-gcp-ami | Integrate/orchestrate `GCP` platform by a given image | +| make-ova | Converts image to `.ova` file | +| make-vhd | Converts image to `.vhd` file | + + +## Detail +### General +This section covers the standalone tools in detail and provides a small overview of their options and how to use them. + +### convert_raw_to_vhd +This script converts a given `.raw` image file into a `.vhd` image file. + +**Usage:** + +`convert_raw_to_vhd /path/to/$image_name.raw` + +### inject-sshkey +This script allows to inject a SSH pubkey to a final Garden Linux image to ensure a SSH login is possible. + +**Options:** +| Option (short) | Descriptions | +|--|--| +| image (-i) | Path to image | +| key (-k) | Path to SSH pubkey file | +| user (-u) | Username that should use the SSH pubkey *(Default: `root`)* | +| homedir (-d) | Path to users homedirectory *(Default: `/home/$user`)* | +| type (-t) | Imagetype *(Default: .raw)* | + +**Usage:** + +`inject-sshkey -i .build/kvm-cis-cloud.raw -u dev -k /home/vagrant/id_rsa.pub` + + +### start-vm +This script starts a given `.raw` or `.qcow2` image in a local QEMU/KVM VM and supports `amd64` and `arm64 builds`. Keep in mind, that running different architectures may be very slow. However, it may still be useful for validating and running unit tests. A spawned VM runs in `textmode` which a `hostfwd` (portforward) for SSH on `tcp/2222`. By the given options this allows the user to user copy/paste in the terminal, as well as connecting to the sshd. *(Hint: Custom SSH pubkeys can be injected with `inject-sshkey`.)* + +**Acceleration Support:** + +Currently, `start-vm` supports `KVM` and `HVF` acceleration. While `HVF` is only supported on macOS, `KVM` will mostly be used. When using `KVM` acceleration you need to ensure that `/dev/kvm` can be used by your user account. However, if `/dev/kvm` is not usable it will fallback to a non accelerated support that may still work but may be slower. Setting permissions on `/dev/kvm` can be don is several ways; for example: + +``` +# Adding specific user to related groups +sudo usermod -G -a kvm "$username" +sudo usermod -G -a libvirtd "$username" +``` + +``` +# Setting permissions for all users +sudo chmod +666 /dev/kvm +``` + +**Network Bridge:** + +Creating a network bridge requires `root` access and additional packages like `virsh`. + +`Debian`: `apt-get install libvirt-clients libvirt` + +`Ubuntu`: `apt-get install libvirt-clients libvirt` + +`CentOS`: `yum install libvirt-client libvirt-daemon libvirt-daemon-driver-qemu libvirt-daemon-config-network` + +`macOS`: `unsupported` + + +Creating a bridge with `virsh`: +``` +# You may need to start unit `libvirtd` + +# By default, it comes with a default profile +$> virsh net-list --all + Name State Autostart Persistent +---------------------------------------------- + default inactive no yes + +# Start the default profile +$> virsh net-start --network default + +# A new device virbr0 is up +$> ip addr show virbr0 +12: virbr0: mtu 1500 qdisc noqueue state DOWN group default qlen 1000 + link/ether 52:54:00:d4:dd:c1 brd ff:ff:ff:ff:ff:ff + inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0 + valid_lft forever preferred_lft forever + +# Stat QEMU bridge with bridge interface +$> bin/start-vm --bridge virbr0 .build/kvm_dev-amd64-today-local.raw +``` + +**Options:** +| Option (short) | Descriptions | +|--|--| +| --arch | Architecture to use [`amd64`, `arm64`] | +| --cpu | CPU type to emulate if an specific should be used | +| --mem | Memory size to use for VM | +| --uuefi | Run in `UEFI` mode instead of legacy `Bios`| +| --ueficode | Path to custom UEFI Code file | +| --uefivars | Path to custom UEFI Vars file | +| --skipkp |Skip keypress (boots directly the image) | +| --vnc | Starts a VNC server session | +| --bridge | Uses a network bridge with a defined interface (needs root) | + +**Usage:** + +Running same arch: `start-vm /path/to/$image_name` + +Running specific arch (e.g. `aarch64`): `start-vm --arch aarch64 /path/to/$image_name` + +### make-ali-ami +This script orchestrates the upload/integration of a Garden Linux image for the `ALI` platform. + +**Options:** +| Option | Descriptions | +|--|--| +| Platform | Defines the platform type | +| ImageName | Image to use | +| DiskDeviceMapping.1.OSSBucket | Custom OSSBocket (mostly equal to Bucketname) | +| DiskDeviceMapping.1.Format | Image format (mostly `.qcow2`) +| RegionId | Region ID to use | + +**Usage:** + +`make-ali-ami --Platform=Others Linux --ImageName= --DiskDeviceMapping.1.OSSBucket= --DiskDeviceMapping.1.OSSObject= --DiskDeviceMapping.1.Format=qcow2 --DiskDeviceMapping.1.DiskImSize=5 --RegionId=EU` + +### make-azure-ami +This script orchestrates the upload/integration of a Garden Linux image for the `Azure` platform. + +| Option | Descriptions | +|--|--| +| resource-group | Name of the storage group to use | +| storage-account-name | Name of the storage account to use | +| image-name | Name of the image to use | +| image-path | Path to the image to use | +| subscription | Subscription to use | + +**Usage:** + +`make-azure-ami --storage-account-name STORAGE_ACCOUNT_NAME --image-name IMAGE_NAME --image-path IMAGE_PATH --subscription SUBSCRIPTION` + +### make-ec2-ami +This script orchestrates the upload/integration of a Garden Linux image for the `EC2` platform. + +| Option | Descriptions | +|--|--| +| bucket | Name of the bucket to use | +| permission-public | Option to declare as public / private | +| region | Region to use | +| image-name | Name of the image to use | +| tags | Add custom tags | + + +**Usage:** + +`make-ec2-ami --bucket BUCKET [--permission-public PERMISSION_PUBLIC] [--distribute DISTRIBUTE] --region REGION --image-name IMAGE_NAME` + +### make-gcp-ami +This script orchestrates the upload/integration of a Garden Linux image for the `GCP` platform. + +| Option | Descriptions | +|--|--| +| bucket | Name of the bucket to use | +| permission-public | Option to declare as public / private | +| raw-image-path | Name of the image to use | +| region | Region to use | +| image-name | Name of the image to use | +| project | Name of the project to use | +| tags | Add custom tags | + +**Usage:** + +`make-gcp-ami [--region REGION] --bucket BUCKET --raw-image-path RAW_IMAGE_PATH --image-name IMAGE_NAME [--permission-public PERMISSION_PUBLIC] [--project PROJECT] [--debug] [--labels LABELS]` + +### make-ova +This script converts a given `.raw` image file into a `.ova` image file. + +*(Hint: This needs the Python module `mako.template`)* + +**Usage:** + +`make-ova /path/to/$image_name.raw` + + +### make-vhd +This script converts a given `.raw` image file into a `.vhd` image file. + +**Usage:** + +`make-vhd /path/to/$image_name.raw` diff --git a/gardenlinux/bin/check-pkgs-availability.py b/gardenlinux/bin/check-pkgs-availability.py new file mode 100755 index 0000000..349ed67 --- /dev/null +++ b/gardenlinux/bin/check-pkgs-availability.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 + +import argparse +import re +import requests +import re +import glob +from pprint import pprint + +import yaml + +def read_pkg_files(arch): + regex_package='echo ([\w-]+)' + regex_arch='arch=([\w-]+)' + packages = [] + + for filename in glob.glob('features/*/pkg.include'): + with open(filename, 'r') as file: + for line in file: + line = line.strip() + if line.startswith('#'): + continue + if not line: + continue + if line.startswith("$(if"): + # If line matches '$(if', then extract the package name + match_pkg = re.search(regex_package, line) + match_arch = re.search(regex_arch, line) + if match_pkg and match_arch and match_arch.group(1) == arch: + package_name = match_pkg.group(1).replace('$arch', arch) + packages.append(package_name) + else: + package_name = line.replace('$arch', arch) + packages.append(package_name) + + return packages + +def get_available_pkgs_from_repo(url) -> list(): + response = requests.get(url) + response.raise_for_status() + + packages = [] + for line in response.text.split('\n'): + match = re.match(r'^Package: ([\w.-]+)', line) + if match: + packages.append(match.group(1)) + + return packages + +def get_unavailable_packages(available_packages, required_packages): + unavailable_packages = set(required_packages) - set(available_packages) + return list(unavailable_packages) + + +def check_packages(arch, dist) -> list(): + available_packages = get_available_pkgs_from_repo(f"http://repo.gardenlinux.io/gardenlinux/dists/{dist}/main/binary-{arch}/Packages") + required_packages = read_pkg_files(arch) + missing_packages = get_unavailable_packages(available_packages, required_packages) + return missing_packages + + +def check_pkgs_pipelines(full=False): + gitlab_url = 'https://gitlab.com' + group_name = 'gardenlinux' + + response = requests.get(f'{gitlab_url}/api/v4/groups/{group_name}', headers="") + group_id = response.json()['id'] + + response = requests.get(f'{gitlab_url}/api/v4/groups/{group_id}/projects?visibility=public', headers="") + projects = response.json() + + report = {} + + for project in projects: + if project['archived']: + continue + + project_id = project['id'] + last_activity_at = project['last_activity_at'] + project_web_url = project['web_url'] + + response = requests.get(f'{gitlab_url}/api/v4/projects/{project_id}/pipelines?ref=main', headers="") + pipelines = response.json() + + if pipelines: + pipeline_status = pipelines[0]['status'] + else: + pipeline_status = None + + if not full and pipeline_status == 'success': + continue + + # Get open issues + response = requests.get(f'{gitlab_url}/api/v4/projects/{project_id}/issues?state=opened', headers="") + open_issues = len(response.json()) + + # Use the project name as the key, and the value is another dict containing the information for that project + report[project['name']] = { + 'last_activity_at': last_activity_at, + 'pipeline_status': pipeline_status, + 'web_url': project_web_url, + 'open_issues': open_issues, + } + + sorted_report = sorted(report.items(), key=lambda x: x[1]['pipeline_status'] != 'failed', ) + + if sorted_report: + print(yaml.dump(dict(sorted_report))) + else: + print("None") + + +def main(dist): + for arch in ["arm64", "amd64"]: + + missing_packages = check_packages(arch, dist) + if missing_packages: + print(f"Missing Packages ({arch}):") + pprint(missing_packages) + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Check package availability') + parser.add_argument('-d', '--dist', required=True, help='Distribution of the apt repository (e.g. today for gardenlinux repo)') + args = parser.parse_args() + main(args.dist) \ No newline at end of file diff --git a/gardenlinux/bin/check-pkgs-pipelines.py b/gardenlinux/bin/check-pkgs-pipelines.py new file mode 100755 index 0000000..32c47b8 --- /dev/null +++ b/gardenlinux/bin/check-pkgs-pipelines.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +import argparse +import re +import requests +import re +import glob +from pprint import pprint + +import yaml + + +def check_pkgs_pipelines(full=False): + gitlab_url = 'https://gitlab.com' + group_name = 'gardenlinux' + + response = requests.get(f'{gitlab_url}/api/v4/groups/{group_name}', headers="") + group_id = response.json()['id'] + + response = requests.get(f'{gitlab_url}/api/v4/groups/{group_id}/projects?visibility=public', headers="") + projects = response.json() + + report = {} + + for project in projects: + if project['archived']: + continue + + project_id = project['id'] + last_activity_at = project['last_activity_at'] + project_web_url = project['web_url'] + + response = requests.get(f'{gitlab_url}/api/v4/projects/{project_id}/pipelines?ref=main', headers="") + pipelines = response.json() + + if pipelines: + pipeline_status = pipelines[0]['status'] + else: + pipeline_status = None + + if not full and pipeline_status == 'success': + continue + + # Get open issues + response = requests.get(f'{gitlab_url}/api/v4/projects/{project_id}/issues?state=opened', headers="") + open_issues = len(response.json()) + + # Use the project name as the key, and the value is another dict containing the information for that project + report[project['name']] = { + 'last_activity_at': last_activity_at, + 'pipeline_status': pipeline_status, + 'web_url': project_web_url, + 'open_issues': open_issues, + } + + sorted_report = sorted(report.items(), key=lambda x: x[1]['pipeline_status'] != 'failed', ) + + return sorted_report + + +def main(full): + report = check_pkgs_pipelines(full) + if report: + print("Failed Gitlab Pipelines:") + print(yaml.dump(dict(report))) + exit(1) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Check package availability') + parser.add_argument("--full", help="include all projects, not just those with non-successful pipeline status", action="store_true") + args = parser.parse_args() + main(args.full) \ No newline at end of file diff --git a/gardenlinux/bin/clean-build-result-repository b/gardenlinux/bin/clean-build-result-repository new file mode 120000 index 0000000..31c7d36 --- /dev/null +++ b/gardenlinux/bin/clean-build-result-repository @@ -0,0 +1 @@ +.cicd-cli.py \ No newline at end of file diff --git a/gardenlinux/bin/garden-chroot b/gardenlinux/bin/garden-chroot new file mode 100755 index 0000000..5f4e686 --- /dev/null +++ b/gardenlinux/bin/garden-chroot @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +# Contains sources from https://github.com/debuerreotype/debuerreotype + +set -Eeuo pipefail + +thisDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +source "$thisDir/.constants.sh" \ + ' [args...]' \ + 'rootfs apt-get update' + +eval "$dgetopt" +while true; do + flag="$1"; shift + dgetopt-case "$flag" + case "$flag" in + --) break ;; + *) eusage "unknown flag '$flag'" ;; + esac +done + +targetDir="${1:-}"; shift || eusage 'missing target-dir' +cmd="${1:-}"; shift || eusage 'missing command' +[ -n "$targetDir" ] +epoch="$(< "$targetDir/garden-epoch")" +[ -n "$epoch" ] + +export targetDir epoch +unshare -f --mount bash -Eeuo pipefail -c ' + [ -n "$targetDir" ] # just to be safe + for dir in dev proc sys; do + if [ -e "$targetDir/$dir" ]; then + mount --rbind "/$dir" "$targetDir/$dir" + fi + done + if [[ ! -e "$targetDir/etc/resolv.conf" && -L "$targetDir/etc/resolv.conf" ]]; then + mv "$targetDir/etc/resolv.conf" "$targetDir/etc/resolv.conf.orig" + touch $targetDir/etc/resolv.conf + fi + cp /etc/resolv.conf "$targetDir/etc/resolv.conf" + exec chroot "$targetDir" /usr/bin/env -i PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" TZ="$TZ" LC_ALL="$LC_ALL" SOURCE_DATE_EPOCH="$epoch" "$@" +' -- "$cmd" "$@" +if [[ -e "$targetDir/etc/resolv.conf.orig" || -L "$targetDir/etc/resolv.conf.orig" ]]; then + mv "$targetDir/etc/resolv.conf.orig" "$targetDir/etc/resolv.conf" +fi diff --git a/gardenlinux/bin/garden-debian-sources-list b/gardenlinux/bin/garden-debian-sources-list new file mode 100755 index 0000000..2ade569 --- /dev/null +++ b/gardenlinux/bin/garden-debian-sources-list @@ -0,0 +1,146 @@ +#!/usr/bin/env bash +# Contains sources from https://github.com/debuerreotype/debuerreotype +set -Eeuo pipefail + +thisDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +source "$thisDir/.constants.sh" \ + --flags 'ports' \ + --flags 'deb-src' \ + --flags 'debian-mirror' \ + --flags 'local-pkgs:' \ + -- \ + '[--deb-src] [--ports] ' \ + 'rootfs stretch 520.0' + +eval "$dgetopt" +gardenlinuxOnly=1 +ports= +debianMirror=0 +debSrc= +local_pkgs= + +# Update all param flags +while true; do + flag="$1"; shift + dgetopt-case "$flag" + case "$flag" in + --gardenlinux-only) gardenlinuxOnly=1 ;; + --ports) ports=1 ;; + --debian-mirror) debianMirror=1 ;; + --deb-src) debSrc=1 ;; + --local-pkgs) local_pkgs="$1"; shift ;; + --) break ;; + *) eusage "unknown flag '$flag'" ;; + esac +done + +targetDir="${1:-}"; shift || eusage 'missing target-dir' +suite="${1:-}"; shift || eusage 'missing suite' +version="${1:-}"; shift || eusage 'missing version' + +[ -n "$targetDir" ] + +epoch="$(< "$targetDir/garden-epoch")" + + +# Garden Linux Repository configuration +gardenlinuxMirror='https://repo.gardenlinux.io/gardenlinux' +gardenlinuxMirrorKey='/etc/apt/trusted.gpg.d/gardenlinux.asc' + +if [ -z "$ports" ]; then + standardMirror='https://cdn-aws.deb.debian.org/debian' +else + standardMirror='https://cdn-aws.deb.debian.org/debian-ports' +fi + +securityMirror='https://security.debian.org/debian-security' + +comp='main' +arch="$("$thisDir/.dpkg-arch.sh" "$targetDir")" + +deb() { + local suite="$1"; shift + local comp="$1"; shift + local target="$1"; shift # "standard" or "security" + + case "$target" in + standard) mirror="$standardMirror" ;; + security) mirror="$securityMirror" ;; + gardenlinux) mirror="[arch=$arch signed-by=$gardenlinuxMirrorKey] $gardenlinuxMirror" ;; + *) echo >&2 "error: unknown 'deb' line target: '$target'"; exit 1 ;; + esac + + echo "deb $mirror $suite $comp" + if [ -n "$debSrc" ]; then + echo "deb-src $mirror $suite $comp" + fi +} + +# https://github.com/tianon/go-aptsources/blob/e066ed9cd8cd9eef7198765bd00ec99679e6d0be/target.go#L16-L58 +{ + # Add local packages + if [ -n "$local_pkgs" ]; then + echo "deb [trusted=yes] file:$local_pkgs ./" + if [ -n "$debSrc" ]; then + echo "deb-src [trusted=yes] file:$local_pkgs ./" + fi + fi + + # Add Debian native mirror + if [ $debianMirror -eq 1 ]; then + echo "deb https://cdn-aws.deb.debian.org/debian testing main" + fi + + + printf "# Gardenlinux Repo\n" + deb "$version" main gardenlinux + + if [[ -z "$gardenlinuxOnly" ]]; then + printf "\n# Debian Repo for backup\n" + case "$suite" in + sid|unstable|testing) + deb "$suite" "$comp" standard + ;; + + *) + # https://salsa.debian.org/installer-team/apt-setup/tree/d7a642fb5fc76e4f0b684db53984bdb9123f8360/generators + deb "$suite" "$comp" standard # "50mirror" + deb "$suite-security" "$comp" security # "91security" + deb "$suite-updates" "$comp" standard # "92updates" + # https://wiki.debian.org/SourcesList#Example_sources.list + + if [ "$suite" = 'squeeze' ]; then + # https://wiki.debian.org/DebianSqueeze#FAQ + deb "$suite-lts" "$comp" standard + fi + ;; + esac + fi +} > "$targetDir/etc/apt/sources.list" +# Adjust file permission for sources file +chmod 0644 "$targetDir/etc/apt/sources.list" + + +## Adjust repository priorities +## Highest priority (600): Local packages +## Regular priority (500/550): Garden Linux repository (550 when Debian Mirror is activated) +## Lowest priority (500): Additional repositories like native Debian repository + +# Adjust repository priority for local packages +if [ -n "$local_pkgs" ]; then + printf 'Package: *\nPin: origin ""\nPin-Priority: 600\n' > "$targetDir/etc/apt/preferences.d/local_pkgs" +else + rm -f "$targetDir/etc/apt/preferences.d/local_pkgs" +fi + +# Adjust repository priority for Garden Linux +if [ $debianMirror -eq 1 ]; then + printf 'Package: *\nPin: origin repo.gardenlinux.io\nPin-Priority: 550\n' > "$targetDir/etc/apt/preferences.d/gardenlinux" +fi + + +# Validate sources.list file +if [ ! -s "$targetDir/etc/apt/sources.list" ]; then + echo >&2 "error: sources.list ended up empty -- something is definitely wrong" + exit 1 +fi diff --git a/gardenlinux/bin/garden-integration-test-config b/gardenlinux/bin/garden-integration-test-config new file mode 100755 index 0000000..1326c03 --- /dev/null +++ b/gardenlinux/bin/garden-integration-test-config @@ -0,0 +1,70 @@ +#!/bin/bash +set -Eeuo pipefail + +# helper script for the integration tests called in build.sh +# create a config directory and create the configuration for the integration test chroot or kvm +# +# NOTE: The args for this script are positional!!! +# +# usage: garden-integration-test-config +# test: name of the test to create the configuration for, possible values are chroot or kvm +# prefix: name of the image/archive before the file suffix (default: kvm_dev-amd64-dev-local) +# features: list of enabled features (default: base) +# outputDir: directory where the outcome of the build is stored (default: .build) +# arch: architecture of the build target, defaults the arch of the image/archive to test + +if [[ ! "$1" == "chroot" ]] && [[ ! "$1" == "kvm" ]]; then + echo "The first argument must be 'chroot' or 'kvm'." + exit 1 +fi + +# define more or less sane defaults +workDir="$(dirname "$(readlink -f "$BASH_SOURCE")")" +features="base" +outputDir=".build" +prefix="kvm_dev-amd64-dev-local" +arch="amd64" + +# use positional arguments if given else use the defaults +test=$1 +prefix=${2:-$prefix} +features=($(echo ${3:-features} | tr "," "\n")) +outputDir=${4:-$outputDir} +arch=${5:-$arch} + +# create directory for the test configs +configDir=$(mktemp -d) + +# Pytest parameters are documented here: +# https://github.com/gardenlinux/gardenlinux/blob/main/tests/README.md + +# write config for the chroot integration test +if [[ "${test}" == "chroot" ]]; then + cat > ${configDir}/config.yaml << EOF +chroot: + image: /gardenlinux/${outputDir##*/}/${prefix}.tar.xz + ip: 127.0.0.1 + port: 2222 + features: $(echo; for str in "${features[@]}"; do echo " - \"$str\"";done) + ssh: + ssh_key_filepath: /tmp/ssh_priv_key + user: root +EOF +fi + +# write config for the kvm integration test +if [[ "${test}" == "kvm" ]]; then + cat > ${configDir}/config.yaml << EOF +kvm: + image: /gardenlinux/${outputDir##*/}/${prefix}.raw + port: 2223 + features: $(echo; for str in "${features[@]}"; do echo " - \"$str\"";done) + arch: ${arch} + ssh: + ssh_key_generate: true + ssh_key_filepath: /tmp/ssh_priv_key + user: root +EOF +fi + +echo ${configDir} diff --git a/gardenlinux/bin/garden-version b/gardenlinux/bin/garden-version new file mode 100755 index 0000000..733982f --- /dev/null +++ b/gardenlinux/bin/garden-version @@ -0,0 +1,106 @@ +#!/usr/bin/env bash + +# Contains sources from https://github.com/debuerreotype/debuerreotype + +set -Eeuo pipefail +thisDir="$(dirname "$(readlink -f "${BASH_SOURCE}")")" +versionfile="$(readlink -f "${thisDir}/../VERSION")" +startdate="Mar 31 00:00:00 UTC 2020" +build_os="$(uname -s)" + +# Use "gdate" & sed (GNU) which must be installed by "Homebrew" +# on macOS systems to have a normalized date interface +if [ "Darwin" == $build_os ]; then + date_gnu="gdate" + sed_gnu="gsed" +else + date_gnu="date" + sed_gnu="sed" +fi + +source "${thisDir}/.constants.sh" \ + --flags 'major,minor,date,datefull,epoch,git' \ + --usage '[ --major | --minor | --date | --datefull | --epoch | --git ] [|]' \ + --sample '20200427' \ + --sample '--date 27.1' \ + --help "Generates version dependent values according to the file $versionfile or to the parameter handed over. Versions can be converted adequately + +--major prints only the major version +--minor prints only the minor version +--date prints the date according to the version e.g. 20200427 if garden-version=27 +--datefull prints a full date deteministically usable by debootstrap +--epoch prints the seconds sinc 19700101 till garden-version +--git prints the version plus the last git-hash + +If no parameter is specified the full version . e.g. 27.5 is printed. The version is taken from $versionfile. On HEAD this should always evaluate to 'today', on branch versions this always should resolve to the next version that will be build e.g. 27.1. This implies there is no branch with a .0 in the $versionfile file. +The version calculated expresses the days since $startdate. Calculation is always UTC based." + +eval "${dgetopt}" +typeout="default" +while true; do + flag="$1"; shift + dgetopt-case "${flag}" + case "${flag}" in + --major|--minor|--date|--datefull|--epoch|--git) + typeout="${flag}" ;; + --) break ;; + *) eusage "unknown flag '${flag}'" ;; + esac +done + +# Checks repo.gardenlinux.io for the highest available suite minor for the given major +function get_minor_from_repo { + minor=0 # running index + limit=100 # hard limit the search in case of unexpected curl results + major=$1 # major to check the latest minor for + repo_url="http://repo.gardenlinux.io/gardenlinux/dists/$major.__MINOR__/Release" + while [ $minor -le $limit ] + do + check_url=${repo_url//__MINOR__/$minor} + if curl -s $check_url| grep -q "Error"; then + ((minor--)) + echo $minor + return + fi + ((minor++)) + done +} + +input="${1:-$($sed_gnu -e "s/#.*\$//" -e "/^$/d" "${versionfile}")}"; shift || true +input=$($sed_gnu "s/^[[:space:]]*//;s/[[:space:]]*\$//" <<< ${input}) + +# no version / timestamp on versionfile +[ -z ${input} ] && input="today" + +minor=0 +if [[ ${input} =~ ^[0-9\.]*$ && $(cut -d. -f1 <<< ${input}) -lt 10000000 ]]; +then [ $(cut -d. -sf3 <<< ${input}) ] && eusage "invalid version format ${input}. should be [.]" + + major="$(cut -d. -f1 <<< ${input})" + if [ $(cut -d. -sf2 <<< ${input}) ]; then + minor="$(cut -d. -f2 <<< ${input})" + else + minor=$(get_minor_from_repo $major) + fi + version="${major}.${minor}" +else + if [[ ${input} = today ]]; + then indate=$($date_gnu --date "today" +%s 2>/dev/null) + major="$(( (${indate} - $($date_gnu --date "${startdate}" +%s)) / (60*60*24) ))" + version=today + else indate=$($date_gnu --date "${input}" +%s 2>/dev/null) || eusage "invalid date ${input}" + major="$(( (${indate} - $($date_gnu --date "${startdate}" +%s)) / (60*60*24) ))" + version="${major}.${minor}" + fi +fi + +case "${typeout}" in + --major) echo ${major} ;; + --minor) echo ${minor} ;; + --date) $date_gnu --date "${startdate} + ${major} days" +%Y%m%d ;; + --datefull) $date_gnu --date "${startdate} + ${major} days" +%Y%m%dT%H%M%SZ ;; + --epoch) $date_gnu --date "${startdate} + ${major} days" +%s ;; + --git) echo "${version}-$(git -C "${scriptsDir}" rev-parse --short 'HEAD^{commit}')" ;; + *) echo "${version}" ;; +esac + diff --git a/gardenlinux/bin/get_arch.sh b/gardenlinux/bin/get_arch.sh new file mode 100755 index 0000000..bfe200f --- /dev/null +++ b/gardenlinux/bin/get_arch.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -Eeufo pipefail + +case "$(uname -m)" in + "x86_64"|"amd64") + echo "amd64" + ;; + "aarch64"|"arm64") + echo "arm64" + ;; + *) + echo "unsupported architecture" >&2 + exit 1 + ;; +esac diff --git a/gardenlinux/bin/get_filename b/gardenlinux/bin/get_filename new file mode 100755 index 0000000..254caea --- /dev/null +++ b/gardenlinux/bin/get_filename @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +import urllib.parse +import sys + +if len(sys.argv) == 1 or len(sys.argv) > 2: + sys.exit(1) + +parsed = urllib.parse.urlparse(sys.argv[1]) +print(parsed.path.split('/')[-1]) diff --git a/gardenlinux/bin/inject-sshkey b/gardenlinux/bin/inject-sshkey new file mode 100755 index 0000000..c42bc13 --- /dev/null +++ b/gardenlinux/bin/inject-sshkey @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse +import subprocess +import shutil +import tempfile +import logging +import shlex +import re + + +def main(): + """ Inject SSH pubkey to Garden Linux image""" + # Configure logger + logging.basicConfig(format='%(asctime)s %(message)s', + datefmt='%Y-%m-%d %I:%M:', stream=sys.stdout, level=logging.DEBUG) + + # Configure argparse + argparser = argparse.ArgumentParser(description='Inject SSH pubkey to image.') + argparser.add_argument('-i', '--image', type=str, help='Path to image', required=True) + argparser.add_argument('-k', '--key', type=str, help='Path to SSH pubkey', required=True) + argparser.add_argument('-u', '--user', type=validate_user, help='PAM user account to use', required=True) + argparser.add_argument('-d', '--homedir', type=str, help='PAM user account to use', default=None) + argparser.add_argument('-t', '--type', type=str, choices=['raw', 'qcow2'], help='Image type [raw, qcow2]', default="raw") + args = argparser.parse_args() + + image = shlex.quote(args.image) + key = shlex.quote(args.key) + user = args.user + home = args.homedir + type = args.type + + logging.info("Start injecting SSH pubkey to image...") + + # Validate if files are present + test_files = [ + image, + key + ] + eval_files_present(test_files) + + # Validate if a custom homedir for a specific user is given + if home is None: + path_home = eval_user_path(user) + logging.info(f"No custom home directory defined. Using: {path_home} for user {user}") + else: + path_home = shlex.quote(home) + logging.info(f"Custom home directory defined. Using: {path_home} for user {user}") + + # Inject SSH pubkey into specific image type + if type == "raw" or "qcow2": + logging.info(f"Using image format: {type}") + # Eveluate userid from image + userid = raw_eval_userid(user,image) + ssh_dir = raw_validate(image,path_home) + raw_inject(image,path_home,user,key,ssh_dir,userid) + + logging.info("SSH pubkey sucessfully injected.") + +def validate_user(user, pat=re.compile(r"^[a-z_][a-z0-9_-]*[$]?$")): + if not pat.match(user): + raise argparse.ArgumentTypeError("invalid user provided") + return user + +def eval_files_present(files): + """ Evaluates if the files are present """ + logging.info("Validating that file(s) is/are present.") + for i in files: + if os.path.isfile(i): + logging.info(f"File is present: {i}") + else: + logging.error(f"File is absent: {i}. Can NOT proceed.") + sys.exit(1) + + +def eval_user_path(user): + """ Evaluates the home directory path by a given username """ + if user == "root": + return "/root/" + else: + return f"/home/{user}/" + + +def raw_eval_userid(user,image): + """ Evaluates the userid for a given user from a .raw image """ + userid = None + if user == "root": + logging.info(f"User is root, not checking for userid in the image, just using 0") + return 0 + + # Get passwd from given image + logging.info(f"Getting userid for user {user} from image") + cmd = f"guestfish -a {image} -i cat /etc/passwd" + p = subprocess.run(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True) + + # Get userid for given user + lst_decode = p.stdout.decode() + passwd_lst = lst_decode.splitlines() + for i in passwd_lst: + # Make sure we compare userfield + i_split = i.split(":") + if i_split[0] == user: + # Return the userid + userid = i_split[2] + logging.info(f"User is present within image. Using userid: {userid}") + return userid + + # We need to exit when the user is not present by the + # given image. + if userid is None: + logging.info(f"User is NOT present within image. We can not proceed.") + sys.exit(1) + + +def raw_validate(image,path_home): + """ Validates if a .ssh directory is already present """ + # Validate for a .ssh directory in path_home + logging.info(f"Validating for an already present .ssh directory") + cmd = f"guestfish --ro -a {image} -i is-dir {path_home}/.ssh" + p = subprocess.run(shlex.split(cmd), shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + # Unfortunately we get a byte like object returned + if "true" in str(p.stdout): + logging.info(f".ssh directory already present in {path_home}") + return True + else: + logging.info(f".ssh directory not present in {path_home}. This will be created.") + return False + + +def raw_inject(image,path_home,user,key,ssh_dir,userid): + """ Injects a SSH pubkey into a RAW image """ + cmds = [] + # Create .ssh directory if not already present + if not ssh_dir: + cmds.append(f"guestfish -a {image} -i mkdir {path_home}/.ssh") + if user == "root": + cmds.append(f"guestfish -a {image} -i chown 0 0 {path_home}/.ssh") + else: + cmds.append(f"guestfish -a {image} -i chown {userid} {userid} {path_home}/.ssh") + cmds.append(f"guestfish -a {image} -i chmod 0700 {path_home}/.ssh") + + # Copy authorized_keys file into image + tmp = tmp_dir("tmp") + tmp_key = f"{tmp.name}/authorized_keys" + logging.info(f"Created tmp directory: {tmp.name}") + + try: + logging.info(f"Copy {key} as a copy to a temporary directory") + shutil.copyfile(key,tmp_key) + except PermissionError: + logging.error("Permission denied.") + except IsADirectoryError: + logging.error("Destination is a directory.") + + # Get all needed commands to inject pubkey + cmds.append(f"virt-copy-in -a {image} {tmp_key} {path_home}/.ssh/") + cmds.append(f"guestfish -a {image} -i chmod 0600 {path_home}/.ssh/authorized_keys") + if user == "root": + cmds.append(f"guestfish -a {image} -i chown 0 0 {path_home}/.ssh/authorized_keys") + else: + cmds.append( + f"guestfish -a {image} -i chown {userid} {userid} {path_home}/.ssh/authorized_keys") + + # Execute all commands to inject SSH pubkey into image + logging.info("Injecting SSH pubkey...") + for i in cmds: + p = subprocess.run(shlex.split(i), shell=False, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, check=True) + rc = p.returncode + logging.info("SSH pubkey injected.") + + # Remove tmp dir + logging.info("Removing tmp files.") + tmp_dir(tmp, remove=True) + + +def tmp_dir(tmp_dir, remove=False): + """ Creates/Removes a tmp directory """ + if remove: + # Ensure that all Py versions delete the tmp file + shutil.rmtree(tmp_dir.name) + else: + tmp_dir = tempfile.TemporaryDirectory() + return tmp_dir + + +if __name__ == '__main__': + main() diff --git a/gardenlinux/bin/make-ali-ami b/gardenlinux/bin/make-ali-ami new file mode 100755 index 0000000..4e2445a --- /dev/null +++ b/gardenlinux/bin/make-ali-ami @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -x + +bucket=$1 +region=$2 +ossobject=$3 # gardenlinux/garden-linux-dev-ali-${VERSION}.qcow2 +image_name=$4 + +aliyun ecs ImportImage \ + --Description=${image_name} \ + --Platform='Others Linux' \ + --ImageName=${image_name} \ + --DiskDeviceMapping.1.OSSBucket=${bucket} \ + --DiskDeviceMapping.1.OSSObject=${ossobject} \ + --DiskDeviceMapping.1.Format=qcow2 \ + --DiskDeviceMapping.1.DiskImSize=5 \ + --RegionId=${region} diff --git a/gardenlinux/bin/make-gcp-ami b/gardenlinux/bin/make-gcp-ami new file mode 100755 index 0000000..289ed53 --- /dev/null +++ b/gardenlinux/bin/make-gcp-ami @@ -0,0 +1,235 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys +import subprocess +import json +import time +import logging + +logger = logging.getLogger(__name__) +logger.setLevel(logging.INFO) + +handler = logging.StreamHandler(sys.stderr) +handler.setLevel(logging.INFO) +formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s") +handler.setFormatter(formatter) +logger.addHandler(handler) + + +class GcpImageBuild: + def __init__(self, args): + self.raw_image_path = args.raw_image_path + self.image_name = args.image_name + self.permission = args.permission_public + self.project = args.project + self.bucket = args.bucket + self.region = args.region + self.debug = args.debug + self.labels = args.labels + if self.debug: + handler.setLevel(logging.DEBUG) + logger.setLevel(logging.DEBUG) + + self.gcloud_configure() + + def gcloud_configure(self): + if self.project != "": + logger.debug(f"Setting project to {self.project}") + result = subprocess.run( + ["gcloud", "config", "set", "project", self.project], + capture_output=True, + ) + if result.returncode != 0: + sys.exit( + "Unable to configure project %s: %s" + % (self.project, result.stderr.decode("utf-8")) + ) + + if self.region != "": + logger.debug(f"Setting region to {self.region}") + result = subprocess.run( + ["gcloud", "config", "set", "compute/region", self.region], + capture_output=True, + ) + if result.returncode != 0: + sys.exit( + "Unable to configure region %s: %s" + % (self.region, result.stderr.decode("utf-8")) + ) + + def logged_on_bucket_available(self, bucket): + bucket_name = "gs://" + bucket + "/" + logger.debug(f"Checking whether bucket {bucket} exists.") + result = subprocess.run( + ["gsutil", "ls", "-L", "-b", bucket_name], capture_output=True + ) + if result.returncode != 0: + sys.exit( + "Bucket " + + bucket + + " does not exist or not logged on: " + + result.stderr.decode("utf-8") + ) + logger.debug(f"Bucket {bucket} exists") + + def upload_image(self, bucket, raw_image_path, image_name): + bucket_image = "gs://" + bucket + "/" + image_name + ".tar.gz" + logger.debug( + f"Checking wheter image file {image_name}.tar.gz exists in bucket." + ) + result = subprocess.run( + ["gsutil", "-q", "stat", bucket_image], capture_output=True + ) + if result.returncode == 0: + sys.exit(f"Image {image_name}.tar.gz exists in bucket {bucket}.") + + logger.info(f"Uploading {raw_image_path} to bucket {bucket}") + result = subprocess.run( + ["gsutil", "cp", raw_image_path, bucket_image], capture_output=True + ) + if result.returncode != 0: + sys.exit( + "Unable to upload image " + + raw_image_path + + ": " + + result.stdout.decode("utf-8") + + "\n" + + result.stderr.decode("utf-8") + ) + logger.info(f"{raw_image_path} successfully uploaded.") + + def create_image(self, bucket, image_name): + # sanitze image name, names must match '(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)' + gcp_image_name = image_name.replace(".", "-") + result = subprocess.run( + ["gcloud", "compute", "images", "describe", gcp_image_name, self.project], + capture_output=True, + ) + if result.returncode == 0: + sys.exit("Image " + gcp_image_name + " does already exist.") + + bucket_image = "gs://" + bucket + "/" + image_name + ".tar.gz" + logger.info(f"Creating image {gcp_image_name} from {bucket_image}") + result = subprocess.run( + [ + "gcloud", + "compute", + "images", + "create", + gcp_image_name, + "--source-uri", + bucket_image, + ], + capture_output=True, + ) + if result.returncode != 0: + sys.exit( + "Unable to create image " + + image_name + + ": " + + result.stdout.decode("utf-8") + + " " + + result.stderr.decode("utf-8") + ) + logger.info(f"Image {gcp_image_name} successfully created") + if self.labels != "": + logger.debug(f"Adding labels {self.labels} to {gcp_image_name}") + result = subprocess.run( + [ + "gcloud", + "compute", + "images", + "add-labels", + gcp_image_name, + "--labels=" + self.labels, + ], + capture_output=True, + ) + if result.returncode != 0: + print(result) + logger.warning( + f"Failed to add labels to {gcp_image_name}, ignoring error: " + + result.stderr.decode("utf-8") + ) + else: + logger.info( + f"Successfully added labels {self.labels} to image {gcp_image_name}" + ) + + if self.permission == True: + logger.debug(f"Making image {gcp_image_name} public.") + result = subprocess.run( + [ + "gcloud", + "compute", + "images", + "add-iam-policy-binding", + image_name, + "--member", + "allAuthenticatedUsers", + "--role", + "roles/compute.imageUser", + ], + capture_output=True, + ) + if result.returncode != 0: + sys.exit( + f"Failed to make {gcp_image_name} public: " + + result.stdout.decode("utf-8") + + "\n" + + result.stderr.decode("utf-8") + ) + logger.info(f"Image {gcp_image_name} made public.") + + def run(self): + self.logged_on_bucket_available(self.bucket) + self.upload_image(self.bucket, self.raw_image_path, self.image_name) + self.create_image(self.bucket, self.image_name) + + @classmethod + def _argparse_register(cls, parser): + + parser.add_argument( + "--region", type=str, dest="region", help="Region", default="" + ) + parser.add_argument( + "--bucket", type=str, dest="bucket", help="Upload bucket", required=True + ) + parser.add_argument( + "--raw-image-path", + type=str, + help="RAW image file tar.gz file", + required=True, + ) + parser.add_argument( + "--image-name", type=str, help="Image name on GCP", required=True + ) + parser.add_argument( + "--permission-public", + type=bool, + default=False, + help="Make snapshot and image public", + ) + parser.add_argument( + "--project", + default="", + type=str, + help="Project name", + ) + parser.add_argument("--debug", action="store_true", help="Verbose debug output") + parser.add_argument("--labels", type=str, default="", help="Labels") + + @classmethod + def _main(cls): + parser = argparse.ArgumentParser() + cls._argparse_register(parser) + args = parser.parse_args() + + gcp_img_build = cls(args=args) + gcp_img_build.run() + + +if __name__ == "__main__": + GcpImageBuild._main() diff --git a/gardenlinux/bin/make-vhd b/gardenlinux/bin/make-vhd new file mode 100755 index 0000000..2df4764 --- /dev/null +++ b/gardenlinux/bin/make-vhd @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +qemu-img convert -f raw -O vpc $@ diff --git a/gardenlinux/bin/shrink.sh b/gardenlinux/bin/shrink.sh new file mode 100755 index 0000000..0715446 --- /dev/null +++ b/gardenlinux/bin/shrink.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -Eeuo pipefail +set -x + +loopback=$(losetup -f --partscan --show rootfs.raw) +trap "losetup -d $loopback 2>/dev/null" EXIT + + +blocks_fs=0 +blocksize=0 +sectors_part=0 +sectorsize=512 +sectors_gpt=34 +resizereport= +i=0 +while [ -z "$(echo $resizereport | grep "Nothing to do")" ]; do + let i+=1 + resizereport="$(resize2fs -M ${loopback}p3 2>&1 | grep "^The filesystem")" + echo "$resizereport" + [ $i -gt 10 ] && break +done +resizereport=$(echo $resizereport | grep "Nothing to do") + +read -r blocks_fs blocksize <<< $(echo "$resizereport" | sed 's/The filesystem is already \([0-9]*\) (\([^)]*\)) blocks long. Nothing to do!/\1 \2/') + +if [ ! $blocks_fs -gt 0 ]; then echo "ERROR: cannot determine shrinked size"; exit 1; fi +if [ "$blocksize" != "4k" ]; then echo "ERROR: no standard blocksize 4k!=$blocksize"; exit 1; else blocksize=4096; fi +sectors_part=$(( $blocks_fs*$blocksize/$sectorsize )) +startsector=$(sfdisk $loopback --dump | grep "${loopback}p3" | sed "s+^${loopback}p3 : start= *\(.*\), size=.*, type=.*$+\1+") +sectors_end=$(( $startsector+$sectors_part-1 )) +sectors_final=$(( $sectors_end+$sectors_gpt )) +bytes_end=$(( $sectors_end*$sectorsize )) +bytes_final=$(( $sectors_final*$sectorsize )) + +echo "blocks by last FS: $blocks_fs($blocksize) -> Sectors $sectors_part($sectorsize)" +echo "part: start $startsector end $sectors_end" +echo "finalsize: $bytes_final" + +echo ",$sectors_part" | sfdisk -N 3 $loopback --no-reread --no-tell-kernel +sfdisk $loopback --dump > final.sfdisk +losetup -d $loopback +sync + +dd if=rootfs.raw of=gpt.bak bs=$sectorsize count=$sectors_gpt seek=$sectors_end + +truncate rootfs.raw -s $bytes_end +sync + +dd if=gpt.bak of=rootfs.raw bs=$sectorsize count=$sectors_gpt seek=$sectors_end diff --git a/gardenlinux/bin/start-vm b/gardenlinux/bin/start-vm new file mode 100755 index 0000000..f1c6ca4 --- /dev/null +++ b/gardenlinux/bin/start-vm @@ -0,0 +1,752 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + + +# Constants +readonly CURR_DIR="$(dirname "$(readlink -f "$BASH_SOURCE")")" +readonly TMP_DIR="${TMP_DIR:-/tmp}" +QEMU_OPTS=( ) + + +# Main function +main () { + get_os + get_arch + set_defaults + get_opts "$@" + set_binaries + qemu_opt_ignition + qemu_opt_pxe + qemu_opt_disk + qemu_opt_uefi + qemu_opt_cpu + qemu_opt_memory + qemu_opt_monitor + qemu_opt_remote_control + qemu_opt_network + qemu_opt_misc + qemu_status + qemu_execute +} + + +# Evaluate operating system +get_os () { + # Obtain the currently used OS + host_os="$(uname -s)" + + if [ $host_os = "Darwin" ]; then + os="macos" + elif [ $host_os = "Linux" ]; then + os="linux" + + # Validate if this process is running inside a + # a container (like Docker) by checking what + # init process is used and set a default OS in + # case it is not systemd and a container then. + # Otherwise, If this is nativly running + # on a host, evaluate the running OS + init_system="$(cat /proc/1/sched | awk 'NR==1{print $1}')" + if [ $init_system != "systemd" ]; then + host_dist="debian" + else + host_dist="$(hostnamectl | grep "Operating System:" | awk {'print tolower($3)}')" + fi + + # Evaluate Distribution + if [ $host_dist = "centos" ]; then + os="centos" + elif [ $host_dist = "debian" ] || [ $host_dist = "garden" ]; then + os="debian" + elif [ $host_dist = "ubuntu" ]; then + os="ubuntu" + elif [ $host_dist = "arch" ] || [ "$host_dist" = "manjaro" ]; then + os="arch" + else + echo "Error: Unsupported Linux distribution." + exit 1 + fi + + else + echo "Error: Unsupported operating system." + exit 1 + fi +} + + +# Evaluate system architecture +get_arch () { + # Include "get_arch.sh" for only maintaining a single + # file that evaluates the current used arch + arch=$(${CURR_DIR}/get_arch.sh) + # Create an immutable var including the original + # system architecture of the host system + readonly arch_orig=$arch +} + + +# Set default values +set_defaults () { + # Set CRE + gardenlinux_build_cre=${GARDENLINUX_BUILD_CRE:-"sudo podman"} + + # Set default vars + cpu="2" + memory="2Gi" + mac="$(printf '02:%02x:%02x:%02x:%02x:%02x' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))" + mac="$(printf "%012s" | tr -d ":" <<< ${mac,,})" + macfull="$(sed 's/../&:/g;s/:$//' <<< $mac)" + uuid="12345678-0000-0000-0000-${mac}" + monitor=1 + bridge= + daemonize= + no_watchdog=0 + pxe= + pxe_binary= + pxefile=${CURR_DIR}/../examples/ipxe/start-vm.ipxe + ignfile= + pidfile= + uefi=1 + uefi_code= + uefi_vars= + tpm=1 + vnc= + keypress=1 + port_dest=22 + port_base=2223 + vnc_base=5900 + + # Evaluate free and usable ports + if [ $os != "macos" ]; then + # Set port(s) on Linux based systems + port=$port_base; while ss -tul | grep :$port &> /dev/null; do (( ++port )); done + vnc_port=0; while ss -tul | grep :$(( vnc_port + $vnc_base )) &> /dev/null; do (( ++vnc_port )); done + else + # Set port(s) on macOS based systems + port=$port_base; while netstat -an -ptcp | grep LISTEN | awk {'print $4'} | grep :$port &> /dev/null; do (( ++port )); done + vnc_port=0; while netstat -an -ptcp | grep LISTEN | awk {'print $4'} | grep :$(( vnc_port + $vnc_base )) &> /dev/null; do (( ++vnc_port )); done + fi + + # Set QEMU defaults + # Run as user "nobody" when started as root user + if [ $(id -u) == 0 ]; then + QEMU_OPTS+=("-runas nobody") + fi + + # Do not use defaults on QEMU + QEMU_OPTS+=("-nodefaults") + # UUID is expected by systemd and Garden Linux + QEMU_OPTS+=("-uuid $uuid" ) +} + + +# Get (external) options that are +# passed to this script +get_opts () { +source "${CURR_DIR}/.constants.sh" \ + --flags 'daemonize,no-watchdog,uefi,legacy-bios,skipkp,vnc' \ + --flags 'cpu:,mem:,bridge:,port:,mac:,arch:' \ + --flags 'pxe:' \ + --flags 'ignfile:' \ + --flags 'pidfile:' \ + --flags 'ueficode:,uefivars:' \ + --flags 'destport:' \ + --usage '[ --daemonize ] [--no-watchdog] [ --cpu:<#> ] [ --mem: ] [ --pxe:

] [[,]]*' \ + --sample '.build/rootfs.raw' \ + --sample ',1G ubuntu-16.04.7-server-amd64.iso' \ + --help "Starts a virtual machine with the most basic environment, needed. Perfect use for test cases and running samples of GardenLinux. This script can run unprivileged but it should have the possibility to use kvm (group kvm) otherwise it will be super slow. If not being root, the network will be slow and no ICMP (ping) capabilities are with the VM. + +--cpu Number of vCPUs to start the VM. (default: $cpu) +--arch Set the emulated platform. i386, x86_64, x86_64-microvm, arm, aarch64 are supported. (default: $arch) +--mem Memory provided to the virtual machine (default: $memory) in MB if unit is omitted. +--uefi Boot with uefi bios enabled, needs \`apt-get install ovmf\`. (default: yes) +--legacy-bios Boot with uefi bios disabled, can't be used with uefi. (default: no) +--ueficode Defines the uefi code used. The file is readonly. (default: $uefi_code) +--uefivars Defines the uefi variables used. The file will be !modified! if vartiables are se. (default: $uefi_vars) +--port Specifies the local ssh port. This port is mounted to the running machine, not if --bridge is specified. (default: $port_base) +--destport Specifies the remote ssh port. This port is used to connect, to a specific sshd. (default: $port_dest) +--mac The mac address is usually randomized. It is used to identify the monitoring port, the mac address of the machine and the UUID. Can be set to this value. +--pxe Enables pxe boot on the vm. Minimum one image file must be a directory. +--ignfile Provide an ignition file when pxe booting. Can be used standalone. +--pidfile Provide a file where to store the pid of the started qemu vm. +--daemonize Start the virtual machine in background, console deactivated (default: no). +--no-watchdog Disables the watchdog qemu option. Used for kvm tests on podman +--skipkp Skip the keypress to the verify status before execute. +--vnc Sitches from serial console to vnc graphics console / enables vnc in --daemonize. + A file containing the image to boot. Format is determined by the extension (raw, vdi, vpc, vhd, vhdx, vmdk, qcow2, iso)." + +eval "$dgetopt" +while true; do + flag="$1"; shift + dgetopt-case "$flag" + case "$flag" in + --cpu) cpu="$1"; shift ;; + --arch) arch="$1"; shift ;; + --mem) memory="$1"; shift ;; + --daemonize) daemonize=1; keypress=; ;; + --no-watchdog) no_watchdog=1; ;; + --vnc) vnc=1; ;; + --uefi) uefi=1; ;; + --legacy-bios) uefi=; ;; + --mac) mac="$1"; shift ;; + --ueficode) uefi_code="$1"; shift ;; + --uefivars) uefi_vars="$1"; shift ;; + --bridge) bridge="$1"; shift ;; + --pxe ) pxe="$(realpath $1)"; shift ;; + --ignfile ) ignfile="$(realpath $1)"; shift ;; + --pidfile) pidfile="$(realpath $1)"; shift ;; + --port) port="$1"; shift ;; + --destport) port_dest="$1"; shift ;; + --skipkp) keypress=; ;; + --) break ;; + *) eusage "Unknown flag '$flag'" ;; + esac +done + +# Get positional args in a dedicated var for +# processing positional arguments in "qemu_opt_disk" +images=$@ +} + + +# Set dedicated binaries for supported +# Operating Systems +set_binaries () { + # Retranslate the architecture to match + # the QEMU binaries if needed and support legacy inputs + if [ $arch = "amd64" ] || [ $arch = "x86_64" ]; then + # Depending on the used distribution QEMU may ship + # architecture related binaries + qemu_arch="x86_64" + arch="amd64" + elif [ $arch = "arm64" ] || [ $arch = "aarch64" ]; then + # Depending on the used distribution QEMU may ship + # architecture related binaries + qemu_arch="aarch64" + arch="arm64" + else + echo "Error: Unsupported architecture $arch" + exit 1 + fi + + # Binaries + bin_head="head" + bin_stat="stat" + bin_sed="sed" + bin_uuid="cat /proc/sys/kernel/random/uuid" + bin_qemu="qemu-system-$qemu_arch" + + # CentOS specific binaries + if [ $os = "centos" ]; then + # Binary naming: https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/8.0_release_notes/index#virtualization + bin_qemu="/usr/libexec/qemu-kvm" + fi + + # macOS specific binaries + if [ $os = "macos" ]; then + bin_head="ghead" + bin_stat="gstat" + bin_sed="gsed" + bin_uuid="/usr/bin/uuidgen" + fi +} + + +# Setup ignition to prepare PXE boot or standalone ignition usage +qemu_opt_ignition () { + if [ $pxe ]; then + + # Check for ignition file + if [ "$ignfile" ]; then + pxefile=$CURR_DIR/../examples/ipxe/start-vm-ignition.ipxe + fi + + [ -e "$CURR_DIR/$mac.ipxe" ] || cp $pxefile $CURR_DIR/$mac.ipxe + + glbuilds=() + glbuild="" + + # Get all vmlinuz files + for v in $(find "$pxe" -type f -name '*.vmlinuz' -exec basename {} \; | cut -d. -f 1); do + glbuilds+=(${v}) + done + + if [ "${#glbuilds[@]}" == "0" ]; then + echo "Error: No vmlinuz found!" 1>&2 + exit 1 + elif [ "${#glbuilds[@]}" == "1" ]; then + glbuild="${glbuilds[0]}" + else + echo "Error: Multiple builds found, which one should be used?" + + for i in ${!glbuilds[@]}; do + echo "[$i] ${glbuilds[i]}" + done + + echo -n "ENTER entry number : " + read n + + if [[ ! "$n" =~ ^[0-9]+$ ]] ; then + echo "Error: Not a valid entry number" + exit 1 + fi + + if [[ "$n" -gt "${#glbuilds[@]}" ]]; then + echo "Error: Not a valid entry" + exit 1 + fi + + glbuild="${glbuilds[${n}]}" + + fi + + # modify the boot.ipxe to load the proper kernel and initramfs + ${bin_sed} -i "s/PATHGOESHERE//g;s/IPADDRESSGOESHERE/10.0.2.2/g" $CURR_DIR/$mac.ipxe + QEMU_OPTS+=( -boot order=nc ) + else + # add neccessary ignfile config for qemu start + # https://docs.fedoraproject.org/en-US/fedora-coreos/provisioning-qemu/#_setting_up_a_new_vm + if [ "$ignfile" ]; then + QEMU_OPTS+=( -fw_cfg name=opt/com.coreos/config,file=${ignfile} ) + fi + fi +} + + +# Validate and set the defined PXE options +# and spawn a helper container within a CRE +qemu_opt_pxe () { + if [ "$pxe" ] && [ "$uefi" ]; then + pxe_binary="$pxe" + pxe= + fi + + if [ $pxe ]; then + container_name=$($bin_uuid) + function stop(){ + rm "$pxe/root."{vmlinuz,initrd,squashfs} + ${gardenlinux_build_cre} stop -t 0 $1 + [[ ! -s "$pxe/ignition.json" ]] && rm -f "$pxe/ignition.json" + echo "INFO: PXE container stopped." + } + echo + + # Link VMLinuz + if [[ -f "${pxe}/${glbuild}.vmlinuz" ]]; then + ln -sf "$glbuild.vmlinuz" "$pxe/root.vmlinuz" + else + echo "Error: Missing ${glbuild}.vmlinuz, exiting"; exit 1 + fi + + # Link Initrd + if [[ -f "${pxe}/${glbuild}.initrd" ]]; then + ln -sf "$glbuild.initrd" "$pxe/root.initrd" + else + echo "Error: Missing ${glbuild}.initrd, exiting"; exit 1 + fi + + # Link squashFS + if [[ -f "${pxe}/${glbuild}.squashfs" ]]; then + ln -sf "$glbuild.squashfs" "$pxe/root.squashfs" + else + echo "Error: Missing ${glbuild}.squashfs, exiting"; exit 1 + fi + + trap 'stop $container_name' EXIT + echo "INFO: PXE container starting." + + if [ "$ignfile" ]; then + ${gardenlinux_build_cre} run -it --rm -d -p 127.0.0.1:8888:80 --name ${container_name} -v ${pxe}:/usr/share/nginx/html -v ${ignfile}:/usr/share/nginx/html/ignition.json nginx + else + ${gardenlinux_build_cre} run -it --rm -d -p 127.0.0.1:8888:80 --name ${container_name} -v ${pxe}:/usr/share/nginx/html:ro nginx + fi + + if [ "$pxe_binary" ]; then + cp "$pxe_binary" "$CURR_DIR/$mac.efi" + QEMU_OPTS+=( -boot order=nc ) + fi + + fi +} + + +# Validate and set the defined image disk to +# start +qemu_opt_disk () { + inflate_list=( ) + disk_count=0 + + if [ "$arch" = "amd64" ]; then + [ "$images" == "" ] || QEMU_OPTS+=("-device virtio-scsi-pci,id=scsi0") + fi + + # Iterate over all user defined images / disks + # and add them to QEMU + for i in $images; do + image_file=$i + image_size=0 + image_direct="" + if [[ $image_file == *","* ]]; then + image_size=${image_file##*,} + image_file=${image_file%%,$image_size} + fi + + # If no filename is left point to tmp + [ "$image_file" == "" ] && image_file=$(mktemp --suff=.raw) + [[ $image_file == *"."* ]] && image_ext=${image_file##*.}; + [ -e $image_file ] || eusage "Error: File \"$image_file\" does not exist." + + # Calculate in bytes + image_size_bytes=$(numfmt --from=iec --suffix="B" --format "%f" ${image_size} | $bin_head -c-2) + image_ext=${image_ext/^vhd$/vpc} + + # Validate for .iso file as CD rom image + if [ "$image_ext" == "iso" ]; then + QEMU_OPTS+=( "-drive media=cdrom,file=$image_file,readonly" ) + elif [ -d $image_file ]; then + targetDir=$image_file + else + # Test if direct access is possible and image is not already in use + dd if=$image_file of=/dev/null count=1 iflag=direct 2> /dev/null && imagedirect="aio=native,cache.direct=on," + + # if there is a bigger size, we need to inflate + [ $($bin_stat --printf="%s" $image_file) -lt ${image_size_bytes} ] && inflate_list+=( "${image_file},${image_size_bytes}" ) + if [ "$arch" = amd64 ]; then + QEMU_OPTS+=("-device scsi-hd,drive=drive${disk_count},bus=scsi0.0" + "-drive format=${image_ext},if=none,discard=unmap,${image_direct}id=drive${disk_count},file=${image_file}") + else + QEMU_OPTS+=("-drive if=virtio,format=${image_ext},file=$image_file") + fi + (( ++disk_count )) + fi + done + + [ $disk_count -gt 0 -o "$pxe" -o "$pxe_binary" ] || eusage "Error: Missing bootdisk. boot via --pxe, provide tmpdisk via --disk or provide bootdisk image file." + + # inflating selected files + for i in "${inflate_list[@]}"; do + dd if=/dev/zero of=$(cut -d, -f1 <<< "$i") count=0 bs=1 seek=$(cut -d, -f2 <<< "$i") 2> /dev/null + done +} + + +# Validate and set UEFI related options +qemu_opt_uefi () { + # Evaluate UEFI code and vars files for realted architecture and OS + if [ "$arch" = amd64 ]; then + # UEFI is optional on x86_64 + if [ $uefi ]; then + if [ "$os" = macos ]; then + # Test for HomeBrew path + homebrew_prefix=$(brew info qemu | awk 'NR==4{print $1}') + uefi_code="${uefi_code:-$homebrew_prefix/share/qemu/edk2-x86_64-code.fd}" + uefi_vars="${uefi_vars:-$homebrew_prefix/share/qemu/edk2-i386-vars.fd}" + elif [ "$os" = debian ] || [ "$os" = ubuntu ] ; then + uefi_code="${uefi_code:-/usr/share/OVMF/OVMF_CODE.fd}" + uefi_vars="${uefi_vars:-/usr/share/OVMF/OVMF_VARS.fd}" + elif [ "$os" = centos ]; then + uefi_code="${uefi_code:-/usr/share/OVMF/OVMF_CODE.secboot.fd}" + uefi_vars="${uefi_vars:-/usr/share/OVMF/OVMF_VARS.secboot.fd}" + elif [ "$os" = arch ]; then + uefi_code="${uefi_code:-/usr/share/OVMF/x64/OVMF_CODE.fd}" + uefi_vars="${uefi_vars:-/usr/share/OVMF/x64/OVMF_VARS.fd}" + else + echo "Error: Could not find UEFI code and vars file." + exit 1 + fi + fi + + elif [ "$arch" = arm64 ]; then + # Always enable UEFI mode on aarch64 + if [[ -z "$uefi" ]]; then + echo "WARNING: --legacy-boot is not supported on AARCH64. Enabling UEFI." + fi + uefi=1 + + # Obtain UEFI code and vars from + # Homebrew path on macOS + if [ "$os" = macos ]; then + # Test for HomeBrew path + homebrew_prefix=$(brew info qemu | awk 'NR==4{print $1}') + uefi_code="${uefi_code:-$homebrew_prefix/share/qemu/edk2-aarch64-code.fd}" + uefi_vars="${uefi_vars:-$homebrew_prefix/share/qemu/edk2-arm-vars.fd}" + elif [ "$os" = debian ] || [ "$os" = ubuntu ] ; then + uefi_code="${uefi_code:-/usr/share/AAVMF/AAVMF_CODE.fd}" + uefi_vars="${uefi_vars:-/usr/share/AAVMF/AAVMF_VARS.fd}" + elif [ "$os" = centos ]; then + # Note: Running `AARCH64` on `x86_64` requires `qemu-system-aarch64` + # package which is not present in official repositories + uefi_code="${uefi_code:-/usr/share/AAVMF/AAVMF_CODE.fd}" + uefi_vars="${uefi_vars:-/usr/share/AAVMF/AAVMF_VARS.fd}" + elif [ "$os" = arch ]; then + # Note: Running `AARCH64` on `x86_64` requires `qemu-system-aarch64` and `edk2-armvirt` + # package which is *present* in official repositories + uefi_code="${uefi_code:-/usr/share/AAVMF/AAVMF_CODE.fd}" + uefi_vars="${uefi_vars:-/usr/share/AAVMF/AAVMF_VARS.fd}" + else + echo "Error: Could not find UEFI code and vars file." + exit 1 + fi + + fi + + # Define UEFI options for QEMU + if [ $uefi ]; then + + [ -r $uefi_code ] || eusage "Missing uefi code at $uefi_code.\n Run: apt-get install ovmf" + [ -r $uefi_vars ] || eusage "Missing uefi vars at $uefi_vars.\n Run: apt-get install ovmf" + + [ -e $CURR_DIR/$mac.vars ] || cp $uefi_vars $CURR_DIR/$mac.vars + + # Add support for amd64 architecture + if [ "$arch" = amd64 ]; then + QEMU_OPTS+=("-global driver=cfi.pflash01,property=secure,value=on") + fi + + # Set UEFI code and vars files + QEMU_OPTS+=("-drive if=pflash,format=raw,unit=0,file=${uefi_code},readonly=on" + "-drive if=pflash,format=raw,unit=1,file=$CURR_DIR/$mac.vars") + + fi +} + + +# Validate and set the CPU architecture +# cores and chipsets +qemu_opt_cpu () { + # Set CPU count + QEMU_OPTS+=("-smp $cpu") + + # Validate amd64 arch + if [ "$arch" = amd64 ]; then + + # Validate for native or emulated arch + if [ "$arch" = "$arch_orig" ]; then + + # Running on native arch + # Check for acceleration support + if [ "$os" = macos ]; then + # macOS HVF support is always given on all new Intel Macs + # that are supported by the current HomeBrew version + QEMU_OPTS+=("-cpu host" "-accel hvf") + + else + + # Validate for KVM support on AMD64 + # If no KVM support is present QEMU handles + # the -cpu option itself + if [ -w "/dev/kvm" ]; then + QEMU_OPTS+=("-enable-kvm" "-cpu host" "-machine q35,smm=on") + else + echo -e "WARNING: Can not use KVM acceleration. Please see:\n https://github.com/gardenlinux/gardenlinux/tree/main/bin#start-vm \n\n" + fi + + fi + + else + # Running on emulated arch + # Set default CPU on emulated arch + QEMU_OPTS+=("-cpu Broadwell") + + fi + + fi + + # Validate AARCH64/ARM64 arch + if [ "$arch" = arm64 ]; then + + # Validate for native or emulated arch + if [ "$arch" = "$arch_orig" ]; then + + # Running on native arch + # Check for acceleration support + if [ "$os" = macos ]; then + # macOS HVF support is always given on all new Intel Macs + # that are supported by the current HomeBrew version + QEMU_OPTS+=("-cpu host" "-machine virt" "-accel hvf") + + else + + # Validate for KVM support on AMD64 + if [ -w "/dev/kvm" ]; then + QEMU_OPTS+=("-cpu host" "-machine virt" "-accel kvm") + else + echo -e "WARNING: Can not use KVM acceleration. Please see:\nhttps://github.com/gardenlinux/gardenlinux/tree/main/bin#start-vm \n\n" + QEMU_OPTS+=("-cpu max" "-machine virt") + fi + + fi + + else + # Running on emulated arch + # Set default CPU on emulated arch + QEMU_OPTS+=("-cpu max" "-machine virt") + + fi + + fi +} + + +# Validate and define memory options +# for VM +qemu_opt_memory () { + [[ "${memory: -1}" =~ [0-9] ]] && memory="${memory}Mi" + memory=$(numfmt --from=auto --suffix="B" --format "%f" ${memory} | $bin_head -c-2) + QEMU_OPTS+=("-m $(( $memory / 1048576 ))") +} + + +# Validate and define monitor port for +# extended commands +qemu_opt_monitor () { + # Add Monitor + if [ $monitor ]; then + [ -e $TMP_DIR/$mac.monitor ] && eusage "Error: Monitor to this MAC address already exists $CURR_DIR/$mac.monitor" + QEMU_OPTS+=("-monitor unix:$TMP_DIR/$mac.monitor,server,nowait") + fi + + # Add Watchdog + if [ $os = debian ] && [ "$no_watchdog" = 0 ]; then + # add a watchdog to maintain automatic reboots + QEMU_OPTS+=("-watchdog i6300esb") + fi +} + +# Validate and define commands for remote +# control of VM +qemu_opt_remote_control () { + # VNC + if [ $vnc ]; then + ( while [ ! -e $CURR_DIR/$mac.monitor ]; do sleep 0.1; done + echo "printf "WARNING: Change VNC password\n%s\n" MYPASSWORD | socat - UNIX-CONNECT:$CURR_DIR/$mac.monitor &> /dev/null )&" + exit1 + printf "WARNING: Change VNC password\n%s\n" MYPASSWORD | socat - UNIX-CONNECT:$CURR_DIR/$mac.monitor &> /dev/null )& + QEMU_OPTS+=("-vnc :${vnc_port},password") + fi + + # IPMI + if [ "centos" != $os ]; then + if [ "$arch" = amd64 ]; then + # Add a BMC simulator + QEMU_OPTS+=("-device ipmi-bmc-sim,id=bmc0" + "-device isa-ipmi-kcs,bmc=bmc0,ioport=0xca2") + + # Add QEMU guest agent support + QEMU_OPTS+=("-chardev socket,path=$TMP_DIR/$mac.guest,server=on,wait=off,id=qga0" + "-device virtio-serial" + "-device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0") + fi + fi +} + + +# Validate and define monitor port for +# extended commands +qemu_opt_network () { + # Create network bridge (requires root). + # (currently unsupported on macOS) + if [ $os != macos ]; then + if [ $bridge ]; then + [ $(id -u) == 0 ] || eusage "Error: For bridging you must be root. More information can be found on: https://github.com/gardenlinux/gardenlinux/tree/main/bin#start-vm" + if [ ! -d /etc/qemu ]; then + mkdir -p /etc/qemu + chown root:kvm /etc/qemu + fi + if [ ! -e /etc/qemu/bridge.conf ]; then + touch /etc/qemu/bridge.conf + chown root:kvm /etc/qemu/bridge.conf + chmod 0640 /etc/qemu/bridge.conf + fi + + printf "%s\nallow %s\n" "$(cat /etc/qemu/bridge.conf)" $bridge > $CURR_DIR/$mac.bridge + awk '!seen[$0]++' < $CURR_DIR/$mac.bridge > /etc/qemu/bridge.conf + rm -f $CURR_DIR/$mac.bridge + fi + fi + + # Set rom file on PXE boot + if [ $pxe_binary ]; then + QEMU_OPTS+=("-device virtio-net-pci,romfile=,netdev=net0,mac=$macfull") + else + QEMU_OPTS+=("-device virtio-net-pci,netdev=net0,mac=$macfull") + fi + + # Set bridge or host fwd network + if [ $bridge ]; then + QEMU_OPTS+=("-netdev bridge,id=net0,br=${bridge}") + elif [ $pxe ]; then + QEMU_OPTS+=("-netdev user,id=net0,hostfwd=tcp::$port-:$port_dest,hostname=garden,tftp=$CURR_DIR,bootfile=$mac.ipxe") + elif [ $pxe_binary ]; then + QEMU_OPTS+=("-netdev user,id=net0,hostfwd=tcp::$port-:$port_dest,hostname=garden,tftp=$CURR_DIR,bootfile=$mac.efi") + else + QEMU_OPTS+=("-netdev user,id=net0,hostfwd=tcp::$port-:$port_dest,hostname=garden") + fi +} + + +qemu_opt_misc () { + if [ "$arch" = "amd64" ]; then + # Use minimal memory + QEMU_OPTS+=("-device virtio-balloon") + + # Add random number generator (RNG) to the host + QEMU_OPTS+=("-device virtio-rng-pci,rng=rng0" + "-object rng-random,id=rng0,filename=/dev/random") + fi + + if [ $pidfile ]; then + QEMU_OPTS+=("-pidfile $pidfile") + fi + + # Set QEMU start options depending on + # daemonizing the VM + if [ $daemonize ]; then + QEMU_OPTS+=("-daemonize" + "-display none") + else + QEMU_OPTS+=("-nographic" + "-serial mon:stdio") + fi +} + + +# Print out status of QEMU execution command including +# all defined options +qemu_status () { + # Print status + printf "Status:\n" + printf " starting VM(UUID:%s) with MAC:%s in %s\n" $uuid $macfull $CURR_DIR + [ $monitor ] && printf " monitor: %s.monitor\tconnect: socat - UNIX-CONNECT:%s\n" $mac $CURR_DIR/$mac.monitor + [ $pxe ] && printf " pxeboot: %s.ipxe\n" $mac + [ $pxe ] && printf " pxeboot: files served from %s\n" "$pxe" + [ $bridge ] && printf " interface: %s bridged\n" $bridge + [ $bridge ] || printf " sshport: (host) tcp/%s -> (vm) tcp/%s (unbridged)\n" $port $port_dest + [ $vnc ] && printf " vncport: %s\n" $(( vnc_port + 5900 )) + [ $uefi ] && printf " uefi boot enabled. %s.vars stores efivars\n" $mac + + # Print inflated disks + for i in "${inflatelist[@]}"; do + printf " file: %s will be inflated to %s\n" $(cut -d, -f1 <<< "$i") $(cut -d, -f2 <<< "$i") + done + + # Print QEMU command line output + ( printf "\n commandline: %s " $bin_qemu + printf '%s ' "${QEMU_OPTS[@]}";printf "\n" ) | sed 's/ /!/g;s/!-/ -/g' | fold -s -w $(( $(tput cols) - 4 )) | sed 's/!/ /g;3,$ s|^| |' + + # Wait for input in not --skipkp is set + if [ $keypress ]; then + read -n 1 -r -s -p $'Press any key to continue...\n' + fi +} + + +# Start QEMU with QEMU_OPTS +qemu_execute () { + # Execute Arch/Distro realted binary with according QEMU opts + $bin_qemu ${QEMU_OPTS[@]} +} + + +# Main +main "$@" diff --git a/gardenlinux/bin/upload-openstack b/gardenlinux/bin/upload-openstack new file mode 100755 index 0000000..daf5a5f --- /dev/null +++ b/gardenlinux/bin/upload-openstack @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Create OpenStack Image for CC EE + +openstack image create \ + --container-format bare \ + --disk-format vmdk \ + --min-disk 16 \ + --min-ram 1008 \ + -f json\ + --property vmware_disktype="streamOptimized" \ + --property vmware_adaptertype="paraVirtual" \ + --property vmware_ostype="debian10_64Guest" \ + --property architecture="x86_64" \ + --property hw_vif_model="VirtualVmxnet3" \ + --property hw_disk_bus="scsi" \ + --property hw_video_ram="16" \ + --property hypervisor_type="vmware" \ + --file=$1 \ + $2 diff --git a/gardenlinux/bin/urlescape b/gardenlinux/bin/urlescape new file mode 100755 index 0000000..984cbc0 --- /dev/null +++ b/gardenlinux/bin/urlescape @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import urllib.parse +import sys + +GARDENLINUX_SNAPSHOT_CACHE_URL = 'snapshot-cache.ci.gardener.cloud' + +if len(sys.argv) == 1 or len(sys.argv) > 2: + sys.exit(1) + +parsed = urllib.parse.urlparse(sys.argv[1]) + +# only modify URLs directed at our snapshot cache +if parsed.netloc != GARDENLINUX_SNAPSHOT_CACHE_URL: + print(urllib.parse.urlunparse(parsed)) + sys.exit(0) + +parsed = parsed._replace(path=urllib.parse.quote(parsed.path.replace('+', ''))) + +print(urllib.parse.urlunparse(parsed)) +sys.exit(0) diff --git a/gardenlinux/bin/uuid_hash b/gardenlinux/bin/uuid_hash new file mode 100755 index 0000000..56dc3ab --- /dev/null +++ b/gardenlinux/bin/uuid_hash @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -Eeufo pipefail + +hash=$(sha256sum) +echo "${hash:0:8}-${hash:8:4}-${hash:12:4}-${hash:16:4}-${hash:20:12}" diff --git a/gardenlinux/build b/gardenlinux/build new file mode 100755 index 0000000..6d56def --- /dev/null +++ b/gardenlinux/build @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -euo pipefail +shopt -s nullglob + +container_image=ghcr.io/gardenlinux/builder:301ce9f70045c001c5d724c2f9d1a9503e1d5ccc +container_engine=podman +target_dir=.build + +container_run_opts=( + --security-opt seccomp=unconfined + --security-opt apparmor=unconfined + --security-opt label=disable + --read-only +) + +container_cmd=() + +use_kms=0 +resolve_cname=0 + +while [ $# -gt 0 ]; do + case "$1" in + --container-image) + container_image="$2" + shift 2 + ;; + --container-engine) + container_engine="$2" + shift 2 + ;; + --container-run-opts) + declare -a "container_run_opts=($2)" + shift 2 + ;; + --privileged) + container_run_opts+=(--privileged) + container_cmd=(--second-stage) + shift + ;; + --kms) + use_kms=1 + shift + ;; + --print-container-image) + printf '%s\n' "$container_image" + exit 0 + ;; + --resolve-cname) + resolve_cname=1 + shift + ;; + --target) + target_dir="$2" + shift 2 + ;; + *) + break + ;; + esac +done + +[ -d "$target_dir" ] || mkdir "$target_dir" + +container_mount_opts=( + -v "$PWD/keyring.gpg:/builder/keyring.gpg:ro" + -v "$(realpath "$target_dir"):/builder/.build" +) + +for feature in features/*; do + if [ -d "$feature" ]; then + container_mount_opts+=(-v "$(realpath -- "$feature"):/builder/$feature:ro") + fi +done + +if [ "$container_image" = localhost/builder ]; then + dir="$(dirname -- "$(realpath -- "${BASH_SOURCE[0]}")")" + "$container_engine" build -t "$container_image" "$dir" +fi + +repo="$(./get_repo)" +commit="$(./get_commit)" +timestamp="$(./get_timestamp)" +default_version="$(./get_version)" + + +if [ "$resolve_cname" = 1 ]; then + arch="$("$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" dpkg --print-architecture)" + cname="$("$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" /builder/parse_features --feature-dir /builder/features --default-arch "$arch" --default-version "$default_version" --cname "$1")" + short_commit="$(head -c 8 <<< "$commit")" + echo "$cname-$short_commit" + exit 0 +fi + +make_opts=( + REPO="$repo" + COMMIT="$commit" + TIMESTAMP="$timestamp" + DEFAULT_VERSION="$default_version" +) + +if [ "$use_kms" = 1 ]; then + for e in AWS_DEFAULT_REGION AWS_REGION AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN; do + if [ -n "${!e-}" ]; then + make_opts+=("$e=${!e}") + fi + done +fi + +if [ -d cert ]; then + container_mount_opts+=(-v "$PWD/cert:/builder/cert:ro") +fi + +"$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" ${container_cmd[@]+"${container_cmd[@]}"} make --no-print-directory -C /builder "${make_opts[@]}" "$@" diff --git a/gardenlinux/cert/Containerfile b/gardenlinux/cert/Containerfile new file mode 100644 index 0000000..a2174da --- /dev/null +++ b/gardenlinux/cert/Containerfile @@ -0,0 +1,10 @@ +ARG base=debian:bookworm +FROM $base +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends awscli efitools gettext git gnupg libcurl4 libengine-pkcs11-openssl libjson-c5 make openssl python3 python3-venv uuid-runtime + +# Prepare virtual environment +ENV VIRTUAL_ENV=/opt/venv +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +RUN pip install git+https://github.com/awslabs/python-uefivars diff --git a/gardenlinux/cert/Makefile b/gardenlinux/cert/Makefile new file mode 100644 index 0000000..566ea21 --- /dev/null +++ b/gardenlinux/cert/Makefile @@ -0,0 +1,69 @@ +CERT_C=DE +CERT_L=Walldorf +CERT_O=SAP SE +CERT_OU=Garden Linux +CERT_E=contact@gardenlinux.io + +GPG_KEY_TYPE=RSA +GPG_KEY_LENGTH=4096 +GPG_NAME=Garden Linux Maintainers +GPG_EMAIL=contact@gardenlinux.io + +export CERT_C CERT_L CERT_O CERT_OU CERT_E GPG_KEY_TYPE GPG_KEY_LENGTH GPG_NAME GPG_EMAIL + +ifdef USE_KMS +export AWS_DEFAULT_REGION AWS_REGION AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN +GENCERT_OPTS=--aws-kms-key-spec RSA_4096 +endif + +.PHONY: default clean distclean +.PRECIOUS: %.crt + +ifndef CA +default: $(PREFIX)kernel-sign.crt $(PREFIX)secureboot.pk.auth $(PREFIX)secureboot.null.pk.auth $(PREFIX)secureboot.kek.auth $(PREFIX)secureboot.db.auth $(PREFIX)secureboot.aws-efivars $(PREFIX)sign.pub + +$(PREFIX)intermediate-ca.crt: $(PREFIX)root-ca.crt +$(PREFIX)kernel-sign.crt: $(PREFIX)intermediate-ca.crt + +gardenlinux.io.crt: $(PREFIX)intermediate-ca.crt + +$(PREFIX)secureboot.pk.crt: $(PREFIX)intermediate-ca.crt +$(PREFIX)secureboot.kek.crt: $(PREFIX)secureboot.pk.crt +$(PREFIX)secureboot.db.crt: $(PREFIX)secureboot.kek.crt + +$(PREFIX)secureboot.pk.auth: $(PREFIX)secureboot.pk.crt +$(PREFIX)secureboot.kek.auth: $(PREFIX)secureboot.pk.crt +$(PREFIX)secureboot.db.auth: $(PREFIX)secureboot.kek.crt +$(PREFIX)secureboot.key.auth: $(PREFIX)secureboot.pk.crt +$(PREFIX)secureboot.null.pk.auth: $(PREFIX)secureboot.pk.crt +else +default: + @echo "Not building default targets when custom CA specified. Please build explicit certificate or omit CA argument." >&2 + @exit 1 +endif + +GUID.txt: + uuidgen --random > '$@' + +%.crt: %.conf $(CA) + @./gencert --conf '$(word 1,$+)' --ca '$(word 2,$+)' $(GENCERT_OPTS) '$@' + +%.auth: %.crt GUID.txt $(CA) + @./genefiauth --guid-file '$(word 2,$+)' --ca '$(word 3,$+)' '$<' '$@' + +%.null.pk.auth: GUID.txt $(CA) + @./genefiauth --guid-file '$(word 1,$+)' --ca '$(word 2,$+)' --null '$@' + +%.aws-efivars: %.pk.auth %.kek.auth %.db.auth + @uefivars.py -i none -o aws -O '$@' -P '$*.pk.esl' -K '$*.kek.esl' -b '$*.db.esl' + +%.pub: gpg.conf + @./gengpg --conf '$<' '$@' + +clean: + @echo "Removing *.pub *.crt *.csr *.key *.arn *.chain GUID.txt *.esl *.auth *.aws-efivars but nothing starting with gardenlinux-" + @find . \( -name '*.pub' -o -name '*.crt' -o -name '*.csr' -o -name '*.key' -o -name '*.arn' -o -name '*.chain' -o -name 'GUID.txt' -o -name '*.esl' -o -name '*.auth' -o -name '*.aws-efivars' \) -a -not -name 'gardenlinux-*' -delete + +distclean: + @echo "Removing *.pub *.crt *.csr *.key *.arn *.chain GUID.txt *.esl *.auth *.aws-efivars" + @find . \( -name '*.pub' -o -name '*.crt' -o -name '*.csr' -o -name '*.key' -o -name '*.arn' -o -name '*.chain' -o -name 'GUID.txt' -o -name '*.esl' -o -name '*.auth' -o -name '*.aws-efivars' \) -delete diff --git a/gardenlinux/cert/build b/gardenlinux/cert/build new file mode 100755 index 0000000..1d657ad --- /dev/null +++ b/gardenlinux/cert/build @@ -0,0 +1,59 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +dir="$(dirname "${BASH_SOURCE[0]}")" + +container_image= +container_engine=podman + +container_run_opts=( + --read-only +) + +container_mount_opts=( + -v "$(realpath "$dir"):/cert" +) + +use_kms=0 + +while [ $# -gt 0 ]; do + case "$1" in + --container-engine) + container_engine="$2" + shift 2 + ;; + --container-run-opts) + declare -a "container_run_opts=($2)" + shift 2 + ;; + --kms) + use_kms=1 + shift + ;; + *) + break + ;; + esac +done + +make_opts=() + +if [ "$use_kms" = 1 ]; then + make_opts+=(USE_KMS=1) + for e in AWS_DEFAULT_REGION AWS_REGION AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN; do + if [ -n "${!e-}" ]; then + make_opts+=("$e=${!e}") + fi + done +fi + +if [ -z "$container_image" ]; then + base_image="$("$dir/../build" --print-container-image)" + image_file="$(mktemp)" + "$container_engine" build --iidfile "$image_file" --build-arg base="$base_image" "$dir" + container_image="$(cat "$image_file")" + rm "$image_file" +fi + +"$container_engine" run --rm "${container_run_opts[@]}" "${container_mount_opts[@]}" "$container_image" make --no-print-directory -C /cert "${make_opts[@]}" "$@" diff --git a/gardenlinux/cert/gardenlinux-intermediate-ca.chain b/gardenlinux/cert/gardenlinux-intermediate-ca.chain new file mode 100644 index 0000000..e69de29 diff --git a/gardenlinux/cert/gardenlinux-intermediate-ca.conf b/gardenlinux/cert/gardenlinux-intermediate-ca.conf new file mode 100644 index 0000000..7c64b3f --- /dev/null +++ b/gardenlinux/cert/gardenlinux-intermediate-ca.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU intermediate CA +days=1825 diff --git a/gardenlinux/cert/gardenlinux-intermediate-ca.crt b/gardenlinux/cert/gardenlinux-intermediate-ca.crt new file mode 100644 index 0000000..00aa91f --- /dev/null +++ b/gardenlinux/cert/gardenlinux-intermediate-ca.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFoTCCA4kCCEKlN7ZX7wMVMA0GCSqGSIb3DQEBCwUAMIGOMR0wGwYDVQQDDBRH +YXJkZW4gTGludXggcm9vdCBDQTELMAkGA1UEBhMCREUxETAPBgNVBAcMCFdhbGxk +b3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsMDEdhcmRlbiBMaW51eDElMCMG +CSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51eC5pbzAeFw0yMjA5MDYyMjE3 +MjJaFw0yNzA5MDUyMjE3MjJaMIGWMSUwIwYDVQQDDBxHYXJkZW4gTGludXggaW50 +ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzAN +BgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExpbnV4MSUwIwYJKoZIhvcN +AQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAuMKbbw5RYzsKLGs90pLfsKdbUCEhL/wu/uT2n/L7GX8LZUH2 +BtKhIj0aQ/LCcDfbQlH7XAzeDXPAkOAcQhx9h/9pTnNVyuPFoVjBs3bI1+33rEtz +RODlwdGXDaQjLo4YyA5Yu4jYTn2978KCxF6Gm0e2uVfXYnEruy8nSX0hDALyifPb +YQRp5WCtXn8tGm5h++lH6FTkiLO/NMzXR3GwiIPFyDiyDz7WUEdS4gdMWLnbV0Ym +nAXGB1dxlJYSZUW4EDJ2MWiKJ6tJ4CjrFPAa59LfM41ZCkFO9O6zQBlXOJNOFb1g +aRTeEzoS+DlSo8QSerU4W7rOlXBIFmkZjvnBIInnmlphNqWMh6sfQZshb0Y/UbU1 +9NqbJC2vFAJCAxkVxG1PvEKKMlJQGRp+Ri4qHgenWfkIws+mwcNE+KPhPNWHYXw+ +6KAHeJ7ZVQmf8cIkgNWEdCVSMuWn9nmf4aqVJY8ChV//mcDmUTSYtK1WRL+3Di1+ +IrIMiXDj4jNGsPq81xtoW9CNMv2qt0nA3/Zb+/xj4+dY1j8EjuzF07oWjllgf8ez +XmasqNJ/i+c5IK+iN4IdW/zrSSzdww4T0dICvA3ouLCKJESzYoBCR/RoimyDLEAA +XOQurupWcvPVgHLONhMHKkG9/e9MuKr6utVQjkWqWoBYBuSGjMRwRrJI/xMCAwEA +ATANBgkqhkiG9w0BAQsFAAOCAgEAV2/g7v7EQLqAyDywWhz2tJ8NgJYdjLo+ylHu +AHR9/cWQPQ4HSeD6qSLuYoaW0JR9bvvu5SqlAymMk2UIO3t2Gqp5coQELhr9qk+b +sYwv6ga77W2lU4EMFvVhWLEs7ST+1CNmOnAbAShcke247S47Kfs+MMevWR5HN4ok +vrTwcoiTnyJzQLjT4ZgSBS9R7GA5r9r0a/vR60398kjOmqHzB9WUmR/0m3YwwBcz +rprS3AilG8X9htJ4y6MmIEEMEQJC+ySzV5JRFhECshefSLR/U8qPOcznugK6hyN9 +EPqCDX1r8wGz6834WShGAvYYcDx6ItRGvv5SiaiIXRmYmgit1E3/0yLSXvV68dT/ +kXcsqx1xA6HLSH69Dfe2U1x3oc7e+1g3XZlWdTjMGXNchCZC5bXKHi0/MDcXhq8A +sXtX6mZ547LD+gYWYlvTuJpBH/xmXWuLfQ8YrXIUPPS+UuDvV5O2dWQsdIRLU/tz +SGFpmqIfyU94IDX6K07mJYidhJ9i4gMHywXRPiqPjemU4bm0xDojAdKtRIKFVadU +P54Tar+GCHuSIzcnvf0By4CgHUEgWr2tfxxRcMdCG2NRr3L34uVc+oPWujj37zPO +wNKs5jUjQFUb98zrFweAPgAT0x8SvjKKeDIVMK4Eb7Mxj0VNlYVGu9wLwXcNY45W +VSNYXbQ= +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-kernel-sign.chain b/gardenlinux/cert/gardenlinux-kernel-sign.chain new file mode 100644 index 0000000..00aa91f --- /dev/null +++ b/gardenlinux/cert/gardenlinux-kernel-sign.chain @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFoTCCA4kCCEKlN7ZX7wMVMA0GCSqGSIb3DQEBCwUAMIGOMR0wGwYDVQQDDBRH +YXJkZW4gTGludXggcm9vdCBDQTELMAkGA1UEBhMCREUxETAPBgNVBAcMCFdhbGxk +b3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsMDEdhcmRlbiBMaW51eDElMCMG +CSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51eC5pbzAeFw0yMjA5MDYyMjE3 +MjJaFw0yNzA5MDUyMjE3MjJaMIGWMSUwIwYDVQQDDBxHYXJkZW4gTGludXggaW50 +ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzAN +BgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExpbnV4MSUwIwYJKoZIhvcN +AQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAuMKbbw5RYzsKLGs90pLfsKdbUCEhL/wu/uT2n/L7GX8LZUH2 +BtKhIj0aQ/LCcDfbQlH7XAzeDXPAkOAcQhx9h/9pTnNVyuPFoVjBs3bI1+33rEtz +RODlwdGXDaQjLo4YyA5Yu4jYTn2978KCxF6Gm0e2uVfXYnEruy8nSX0hDALyifPb +YQRp5WCtXn8tGm5h++lH6FTkiLO/NMzXR3GwiIPFyDiyDz7WUEdS4gdMWLnbV0Ym +nAXGB1dxlJYSZUW4EDJ2MWiKJ6tJ4CjrFPAa59LfM41ZCkFO9O6zQBlXOJNOFb1g +aRTeEzoS+DlSo8QSerU4W7rOlXBIFmkZjvnBIInnmlphNqWMh6sfQZshb0Y/UbU1 +9NqbJC2vFAJCAxkVxG1PvEKKMlJQGRp+Ri4qHgenWfkIws+mwcNE+KPhPNWHYXw+ +6KAHeJ7ZVQmf8cIkgNWEdCVSMuWn9nmf4aqVJY8ChV//mcDmUTSYtK1WRL+3Di1+ +IrIMiXDj4jNGsPq81xtoW9CNMv2qt0nA3/Zb+/xj4+dY1j8EjuzF07oWjllgf8ez +XmasqNJ/i+c5IK+iN4IdW/zrSSzdww4T0dICvA3ouLCKJESzYoBCR/RoimyDLEAA +XOQurupWcvPVgHLONhMHKkG9/e9MuKr6utVQjkWqWoBYBuSGjMRwRrJI/xMCAwEA +ATANBgkqhkiG9w0BAQsFAAOCAgEAV2/g7v7EQLqAyDywWhz2tJ8NgJYdjLo+ylHu +AHR9/cWQPQ4HSeD6qSLuYoaW0JR9bvvu5SqlAymMk2UIO3t2Gqp5coQELhr9qk+b +sYwv6ga77W2lU4EMFvVhWLEs7ST+1CNmOnAbAShcke247S47Kfs+MMevWR5HN4ok +vrTwcoiTnyJzQLjT4ZgSBS9R7GA5r9r0a/vR60398kjOmqHzB9WUmR/0m3YwwBcz +rprS3AilG8X9htJ4y6MmIEEMEQJC+ySzV5JRFhECshefSLR/U8qPOcznugK6hyN9 +EPqCDX1r8wGz6834WShGAvYYcDx6ItRGvv5SiaiIXRmYmgit1E3/0yLSXvV68dT/ +kXcsqx1xA6HLSH69Dfe2U1x3oc7e+1g3XZlWdTjMGXNchCZC5bXKHi0/MDcXhq8A +sXtX6mZ547LD+gYWYlvTuJpBH/xmXWuLfQ8YrXIUPPS+UuDvV5O2dWQsdIRLU/tz +SGFpmqIfyU94IDX6K07mJYidhJ9i4gMHywXRPiqPjemU4bm0xDojAdKtRIKFVadU +P54Tar+GCHuSIzcnvf0By4CgHUEgWr2tfxxRcMdCG2NRr3L34uVc+oPWujj37zPO +wNKs5jUjQFUb98zrFweAPgAT0x8SvjKKeDIVMK4Eb7Mxj0VNlYVGu9wLwXcNY45W +VSNYXbQ= +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-kernel-sign.conf b/gardenlinux/cert/gardenlinux-kernel-sign.conf new file mode 100644 index 0000000..0d752cb --- /dev/null +++ b/gardenlinux/cert/gardenlinux-kernel-sign.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU kernel signing certificate +days=365 diff --git a/gardenlinux/cert/gardenlinux-kernel-sign.crt b/gardenlinux/cert/gardenlinux-kernel-sign.crt new file mode 100644 index 0000000..43bb065 --- /dev/null +++ b/gardenlinux/cert/gardenlinux-kernel-sign.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFtDCCA5wCCGynxMjL+TpQMA0GCSqGSIb3DQEBCwUAMIGWMSUwIwYDVQQDDBxH +YXJkZW4gTGludXggaW50ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UE +BwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExp +bnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMB4XDTIy +MDkwNjIyMTcyOFoXDTIzMDkwNjIyMTcyOFowgaExMDAuBgNVBAMMJ0dhcmRlbiBM +aW51eCBrZXJuZWwgc2lnbmluZyBjZXJ0aWZpY2F0ZTELMAkGA1UEBhMCREUxETAP +BgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsMDEdhcmRl +biBMaW51eDElMCMGCSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51eC5pbzCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCkHakx9/TIrhTJ4/HdN+wF +mw7U5ZGRDo/3fBV8p7mna7L4TQD7qPk6qsKPf0lIlaaGImi+1O6vM5aPexGKNwKi +D+C0CMQGFOo/9TauQmyaPrM39KfiTRVL3+jUZVBj8uSzQRRLQCUTDtCVaoCrdBT+ +cZtVDLTCRLyydk0akRQWeylDsmtGdkfnRDTIcv8he2Y5O8JMSYPXkehDwlY0Q2dX +rfYGOBT9vpC76mYBCYcLE3Bpk9G4LXPlnev9zWD2qyaIXqxZVnaxZGdkiGbLSgHi +T34GLvLsNZpboz9eSvit+OBy1t+qY3OVcMZ/LfqwzJbRh44A7sZkKAl+5Tfoe8DM +PsV5aRiq4PQe6WxixozbnTTHtu5ZYdmTz/mbZoTMiTWxZvrfmegJajoF4poD7J+N +h1Xl4Scaht7qN2aqV1e7fLL/Xmyz3I3OSUfgtWLe4rmdiDpbSoMvSi30wyIbkWiL +kLSUhTpONjHPteOHo7RZaLTXvL1l7Bnxfw0r9NiIEK19e9syBLpM77x56re8+6s6 +Ikt2um8lK6KZ5BGjWvWxV61UXnBEN5mWUQCDq57gWRTs4WarWaxOKaqSbDGmdD8S +65VbKpJmDZFC5+UIIFE+T0K+9zskrvxEDyv0vMMaw6dutALxQQCjeJF6NbI+Kff0 +uMUB2Sc3eF8j5RYzfdxtAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAAjG93c/Nqce +X2D7UHzsMkeDl2+mTdPv/b7gnU13LhB61dhaBrzFQf90EJaFMPFBIklIpk3qOJJ4 +G8ED2vLVPZwoRhYLiVCWOiH1I4AJJHGNwl2Id7AEQlJNhgWf9SMLcAY7L/KM37yP +u6b8HeNCWqpXd/oBeStAJL+25U4vnKdgNMddlrxKcy49xYOF93Puv6THpSBeTKBN +gn1x1AVcmkZjQQXTkfWRYM8xAEErikHq6N5d+goIQ+6C3IfrETI2zp8WgCvo5Bv4 ++VyQtwc/HY1w/ps+YfcArgh26wOwfzGXMlvnn5Cnq0V3ZrWZqCD+V+JrcycczZjt +krHE53XjYT5xsTTOmeZqTA8pai2tCMQ9H/hJ9Vtzaflbk42d5oawBBBfScFk0HXc +o3ONWKmmL5TktqtplPRIprLy9wogNOKbwi2dnh7WYp1esHI7rfKehPyFkHzU7o4D +Rffyaon4czuuaOzddKFLHNyNc8ok7zoi+fZygpFj8ZJjoVbGML8oC06qcn1MSO7X +gyCdjFeh3504mgwwqsc2+utjSEDj/6woG7u7nAktFOfj3OuCk68iGUXqajL0IbU5 +qWo5q8umcVlD273qtHZD5Ltnf2NjNQCuo+zWctPVnFXoTMvbLRdz6Z8tz6gNzoKu +lxM2ui+C1prV4m3ewHa0R4TKNy96YvO8 +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-root-ca.conf b/gardenlinux/cert/gardenlinux-root-ca.conf new file mode 100644 index 0000000..b792247 --- /dev/null +++ b/gardenlinux/cert/gardenlinux-root-ca.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU root CA +days=1825 diff --git a/gardenlinux/cert/gardenlinux-root-ca.crt b/gardenlinux/cert/gardenlinux-root-ca.crt new file mode 100644 index 0000000..5de5256 --- /dev/null +++ b/gardenlinux/cert/gardenlinux-root-ca.crt @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +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 +CZIR +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-secureboot.aws-efivars b/gardenlinux/cert/gardenlinux-secureboot.aws-efivars new file mode 100644 index 0000000..563b186 --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.aws-efivars @@ -0,0 +1 @@ +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 \ No newline at end of file diff --git a/gardenlinux/cert/gardenlinux-secureboot.db.auth b/gardenlinux/cert/gardenlinux-secureboot.db.auth new file mode 100644 index 0000000000000000000000000000000000000000..cc961c2404ad6d3acdd092f733b725dc71c9f7eb GIT binary patch literal 3807 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 R!@XV}`F`(*&rQ5e{0q&zK1Kil literal 0 HcmV?d00001 diff --git a/gardenlinux/cert/gardenlinux-secureboot.db.chain b/gardenlinux/cert/gardenlinux-secureboot.db.chain new file mode 100644 index 0000000..1e66d0c --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.db.chain @@ -0,0 +1,99 @@ +-----BEGIN CERTIFICATE----- +MIIFoTCCA4kCCEKlN7ZX7wMVMA0GCSqGSIb3DQEBCwUAMIGOMR0wGwYDVQQDDBRH +YXJkZW4gTGludXggcm9vdCBDQTELMAkGA1UEBhMCREUxETAPBgNVBAcMCFdhbGxk +b3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsMDEdhcmRlbiBMaW51eDElMCMG +CSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51eC5pbzAeFw0yMjA5MDYyMjE3 +MjJaFw0yNzA5MDUyMjE3MjJaMIGWMSUwIwYDVQQDDBxHYXJkZW4gTGludXggaW50 +ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzAN +BgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExpbnV4MSUwIwYJKoZIhvcN +AQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAuMKbbw5RYzsKLGs90pLfsKdbUCEhL/wu/uT2n/L7GX8LZUH2 +BtKhIj0aQ/LCcDfbQlH7XAzeDXPAkOAcQhx9h/9pTnNVyuPFoVjBs3bI1+33rEtz +RODlwdGXDaQjLo4YyA5Yu4jYTn2978KCxF6Gm0e2uVfXYnEruy8nSX0hDALyifPb +YQRp5WCtXn8tGm5h++lH6FTkiLO/NMzXR3GwiIPFyDiyDz7WUEdS4gdMWLnbV0Ym +nAXGB1dxlJYSZUW4EDJ2MWiKJ6tJ4CjrFPAa59LfM41ZCkFO9O6zQBlXOJNOFb1g +aRTeEzoS+DlSo8QSerU4W7rOlXBIFmkZjvnBIInnmlphNqWMh6sfQZshb0Y/UbU1 +9NqbJC2vFAJCAxkVxG1PvEKKMlJQGRp+Ri4qHgenWfkIws+mwcNE+KPhPNWHYXw+ +6KAHeJ7ZVQmf8cIkgNWEdCVSMuWn9nmf4aqVJY8ChV//mcDmUTSYtK1WRL+3Di1+ +IrIMiXDj4jNGsPq81xtoW9CNMv2qt0nA3/Zb+/xj4+dY1j8EjuzF07oWjllgf8ez +XmasqNJ/i+c5IK+iN4IdW/zrSSzdww4T0dICvA3ouLCKJESzYoBCR/RoimyDLEAA +XOQurupWcvPVgHLONhMHKkG9/e9MuKr6utVQjkWqWoBYBuSGjMRwRrJI/xMCAwEA +ATANBgkqhkiG9w0BAQsFAAOCAgEAV2/g7v7EQLqAyDywWhz2tJ8NgJYdjLo+ylHu +AHR9/cWQPQ4HSeD6qSLuYoaW0JR9bvvu5SqlAymMk2UIO3t2Gqp5coQELhr9qk+b +sYwv6ga77W2lU4EMFvVhWLEs7ST+1CNmOnAbAShcke247S47Kfs+MMevWR5HN4ok +vrTwcoiTnyJzQLjT4ZgSBS9R7GA5r9r0a/vR60398kjOmqHzB9WUmR/0m3YwwBcz +rprS3AilG8X9htJ4y6MmIEEMEQJC+ySzV5JRFhECshefSLR/U8qPOcznugK6hyN9 +EPqCDX1r8wGz6834WShGAvYYcDx6ItRGvv5SiaiIXRmYmgit1E3/0yLSXvV68dT/ +kXcsqx1xA6HLSH69Dfe2U1x3oc7e+1g3XZlWdTjMGXNchCZC5bXKHi0/MDcXhq8A +sXtX6mZ547LD+gYWYlvTuJpBH/xmXWuLfQ8YrXIUPPS+UuDvV5O2dWQsdIRLU/tz +SGFpmqIfyU94IDX6K07mJYidhJ9i4gMHywXRPiqPjemU4bm0xDojAdKtRIKFVadU +P54Tar+GCHuSIzcnvf0By4CgHUEgWr2tfxxRcMdCG2NRr3L34uVc+oPWujj37zPO +wNKs5jUjQFUb98zrFweAPgAT0x8SvjKKeDIVMK4Eb7Mxj0VNlYVGu9wLwXcNY45W +VSNYXbQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFszCCA5sCCAE7poNeT3GwMA0GCSqGSIb3DQEBCwUAMIGWMSUwIwYDVQQDDBxH +YXJkZW4gTGludXggaW50ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UE +BwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExp +bnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMB4XDTIy +MDkwNjIyMTczNFoXDTI3MDkwNTIyMTczNFowgaAxLzAtBgNVBAMMJkdhcmRlbiBM +aW51eCBzZWN1cmVib290IFBLIGNlcnRpZmljYXRlMQswCQYDVQQGEwJERTERMA8G +A1UEBwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVu +IExpbnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAy4uT6AYNkASZBh9tBMUAR1Li +spIHlTrR1UFPcFTUF9puolZHvSbuyjIPyhuztguYp0G2tl/XQjwH3bii2gC37ru+ +r3belOYkedg1HSbHn6wIxHJo2+OsycpDUyggDK2fIbsUs4wZvtwzYxqlMGEdZvRt +STtRVm0U70rASI+9m4CvvvwwXo0fMn/RclcCGEp5G7N0MK6dZm5fHPG6UDtDvJih +3F3fhWFXhDOw3xiPCwcrDsY8e5cVOIynYi3y0qOQX947ARQdkHHKfyU55IMqUvV2 +5vvzJPr+BcG/SZZrjnG2VI8Vix4ssiR/QWCFHi2+8HrRvC0ozP/tA4CU5t/CpF0q +I9fxM9zEysbBeGM+QLTFwD//ChN4XxgIdQBvYIkf0xifYKHr7nvR6yANGe2fMcFV +kpoco5q7jFwcIe3St0OZut7H/S1NxGCBEBu8o+jYwlVijtTqmMA9fW9Od1l+Crqd +/MLlSqOqEg3CQe0p0H1jU8AiScpe35VkJj/LTqqFwuh6VBF+znDtIio5eEmgChN9 +DIasws70g2VMlwnSJTALMyHF9DQqdS39VKEU+iCA5qsUOpD1D2WbSG2/DMbXDc0Z +6zzRWXvwyyy4JmnLp3YgcrLM5WO4X7m8kR9+kPRLQU4RsggHHeLzeQUEOpB9AJ4Y +tysl1i+32EkWTiVT58UCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEASyO+thlcCAAZ +Dd0dURVPC5fIAsMxHc6SsXu9csiJ7kzdQPyHXt2BbAn0MGhmJq1PuAuzCtIZiA7K +ScwV6tAfdvhypnvIVEpbDLcb0q0zwvoxisfeMFRt/lNFm2DXjixsG2Nd9lIxFa+E +yg1qvUZppoxWL6Z54z8DXQv5zCXmiW/XNvjTMgUKWtLVUbILQoNPV7YMt7MsF3Km +LtTDNidTweJJY1GpkhlmAJz707SRss4/7dfNydVxJ3WaHI4xHFF9IiGFKhxpDW9S +9N1+MffFDKqn0je4AHj2iv+ORLzgIanK1cZAAwMsCiqfUIjuKZAM9MhXT/i/Y2Xk +Hlf8kMOM+XlNwnxo8/M2hiEHLho9689cXySfSfU6Mj7LPHnfpwxrDM5r4GwYaSIp +GtIl2wcZKHfeR/JWSY68Ma/DPtzg3zwPUZyb5GB7NwztET+7pQ4mgwph92KOxHJB +qZSZY/xfWjX8OV2fS7GVmAUP3qAMNZjbfqtUcGcDwhOtbzUyiMqiMy95zFV/4Tnp +NiMTp9ZBCAFertv7EUteAYJ/oj4LuSpwQ9XPREaOXMHlnQVjSeh/vGd+sxSagIq+ +GpKfRV1RDTFP7yzIgPNAT6r/6eKiHvZoDwTXDPa6dKUTIANq3x720fpBEqjpgPft +iktik/34sn7vTLiwYM8ABjF+yNeUEo0= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFvjCCA6YCCENrGr0WZy0qMA0GCSqGSIb3DQEBCwUAMIGgMS8wLQYDVQQDDCZH +YXJkZW4gTGludXggc2VjdXJlYm9vdCBQSyBjZXJ0aWZpY2F0ZTELMAkGA1UEBhMC +REUxETAPBgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsM +DEdhcmRlbiBMaW51eDElMCMGCSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51 +eC5pbzAeFw0yMjA5MDYyMjE3NDRaFw0yNzA5MDUyMjE3NDRaMIGhMTAwLgYDVQQD +DCdHYXJkZW4gTGludXggc2VjdXJlYm9vdCBLRUsgY2VydGlmaWNhdGUxCzAJBgNV +BAYTAkRFMREwDwYDVQQHDAhXYWxsZG9yZjEPMA0GA1UECgwGU0FQIFNFMRUwEwYD +VQQLDAxHYXJkZW4gTGludXgxJTAjBgkqhkiG9w0BCQEWFmNvbnRhY3RAZ2FyZGVu +bGludXguaW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDE7tVPnsEr +YWECQHHOHB+UxpLLkWKhIedcd9uc/NjSj1s8FGxDvlFoRkFkzp9dx06wPxrptrPI +xm1ogArfEijSAUSsBkYydUpA8kAuNEYLuGuefVgiA2JZdIknif8RYTL4QE9tpAdg +C742ri+8X143k4RQ7Fv8QAv+AngHrHQT/PgMDHQZFUafXQMHid4XKak3uc/TID4o +UFuLFV1X1cgzcxV5UuyrGnFu8eLuvk1ZiVkgwx778kLXvBTxXHb3ONaORkorVJ4e +uEUZAo/eNjQGlRNEKmTU3BGYZdL9/UUV3d2stfB9E6WoyxvvBk3Xnv0M7/QZPxr6 +kDSgIc+nf6o5QgtWfs8WDCow14DAroYLqw5+LuOhXhY/mtj3aPkE7pUXHfNIFwmk +pYhr4BmT9Auv89bm/yrVqdf5qF0hI1ri8369R7qfIGvV2JOtr2UK0/9EYvBLClck +2d1WHFysb6l2lzE6/xvWDayKtSrG7UJ8I/fzJp96hHvRnQDSLP+zbJxK2MKZu5JZ +szblsdDuO0YRQ2KZPX1AgAz2lH4137djXd8bxMk07n+MD7clB8lGoUn60R8sJcc5 +gQ4Ybt3gd/F1PtC5VwLyMka0XBYo/c5KyIW+TBqkKXwOXyqE0tnH0iqMOkWHI83Q +60fH2f2RMoZ+3t/Ado4hBm3VJqnAG8qOlQIDAQABMA0GCSqGSIb3DQEBCwUAA4IC +AQAug5jK1cHM80xeokTAhv2KTu1JNeg2Ap4qV20Hw383EOwdVL5zdv1sHoa78fWK +ey12Gm3x8SDg4irxqCtLR+qrI7+TEdRTcSC0u5LORrmuE8f8P+zeWULVUIsCAAzE +HlwdSPTv/OjHfk0hUbB6YKQG5D/DXE8o2CdtNnzNH0Pc880WKZRs1UTf1x2Kw3mS +dDNz3gws/QUDLePBmhFxwO6pz/CYDu5x/U7uzJ0KbuknjB3EI6z4mkAfaEu868X6 +qAVbVyUTBXYfKew6C8hHRpAqqO64sEEM0y5EZTHXLb9CTJU2AOayFf+5J2ODI1Ph +xHjLOU5/R1kV1CPUoFqOngM/jX7STgmu2f7Y/1+XeIQWOw+6SHRF+YHOpjmcDqVs +zQK4+AZoTul8zDPnhxXwzfMy3owAATKai2bmRR53FxzkkJ6aaACKLkCNuTOP0E2M +5lPQx9NGy1gfth1SaRADZmP+TukXnDOPzfeNk+zVDEUS2paUQ62XkIvz46GPvC+1 +5lzbkNpjY1mgHksSYEPdsjPXuUs9Jrpi/7YInvdexLMSgeWKh+mRhTcIKa27+br8 +m/4ol6qicTnzRr2XtRdXSyLvvsTji/4BxzG6C5FaB24QtAmFFudf5ehCux1mTfDx +D4uDvElNqQb9aSbkU1MQOhmWeNN5s/6S2sQqLMCrYcXECA== +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-secureboot.db.conf b/gardenlinux/cert/gardenlinux-secureboot.db.conf new file mode 100644 index 0000000..bfca198 --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.db.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU secureboot db certificate +days=365 diff --git a/gardenlinux/cert/gardenlinux-secureboot.db.crt b/gardenlinux/cert/gardenlinux-secureboot.db.crt new file mode 100644 index 0000000..742e12a --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.db.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFvzCCA6cCCQDijLDn1sA5xDANBgkqhkiG9w0BAQsFADCBoTEwMC4GA1UEAwwn +R2FyZGVuIExpbnV4IHNlY3VyZWJvb3QgS0VLIGNlcnRpZmljYXRlMQswCQYDVQQG +EwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMGA1UE +CwwMR2FyZGVuIExpbnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRlbmxp +bnV4LmlvMB4XDTIyMDkwNjIyMTc1MloXDTIzMDkwNjIyMTc1MlowgaAxLzAtBgNV +BAMMJkdhcmRlbiBMaW51eCBzZWN1cmVib290IGRiIGNlcnRpZmljYXRlMQswCQYD +VQQGEwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMG +A1UECwwMR2FyZGVuIExpbnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRl +bmxpbnV4LmlvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1rMqoIYk +JowClB58Iu/ehbp1DBLgZrtiFDOyv0mVw1Z5iE4r5nbRHaHYa2s98VTR8NuZQ6tK +JLvylIYJpINAKTNtGtei6RD3jcFgFzNdyvqr7Y/bZqFR6Cx0sVb0Oepi50MiY/fC +pfQff92pEWrTaZ4D8/ZFsbpsNfxn59RL+LTRIfD2uXOI9YBKC589akmjzlIjgWzx +6GAbEcvnnN4r+HOlQpuxiKCHKVLJMOdtZLwcY7jzEeUjnVO8AxDGSITRI6i0XyUt +YgjzqbNE3qrmXIO19k47SSlhh1h5A5vGOBYYsFk7kSfGM+ggLpqILa2Zrd/xvqhe +dF5Q2rlKubLKxniUEH81wc+zEvXfe1OU/z2XREYhQkkKXcKjyIfyXsPMjLXDKZZ7 +VGPuxi+COMplZ+Jdd1dmVUL+Re4OXnQA0QTd4KvK3LIr4p/oRoBRmMd0dgKcnHvP +0qZeIjdjI4ogq36kYtPiCLMgldsl5S9+XDgR72RGE8ITLcz3BrvrKEQ+7Cfea2G/ +EUujnzlFcIizuR7O1WOXnzuiJpYjVi55E0s/sygzKzyWjsDI6cmxCdIiRYDuxVhX +WwHodvUH/i7x8L6ORK105ADVH/EVxRLyZMw/Q9/18V+gXiaIdk05GB2H86H/Wu/k +cqLSee21LVV0w3v07BDP5xyQMO5L6feD/PUCAwEAATANBgkqhkiG9w0BAQsFAAOC +AgEAPkCBEO0fVGdgc0HrejTnhVJFOJmV7B66JyN7mYQNSwzkWzfx1/tMDBQR0HDL +gydYi/K7DRiJrm6LvrqB3gE/guh+gqg2rvgqzKpR0JkKQ3zHr96AF/Llveg+0juw +DkIh6IQh3j3JZ8KaOGynY5e+rL6EKN5KCpJjzPm4Dvskt/PztVpCUN1ZsvYRVa8N +j3vF9hRoiQAYnOehyz/9UX4jXdBiLJrXNbVQ7L7731ssfjg9LAwSDjHLjD2pRCYx +TYW2M7BwrfxNKLtWNOSL6Ij5uZBP6zdcBXAbWikQN6Z96JEJeMy0Lnli7G3VY2sr +m5mdiWGWlI6smYn7K7fm1D24uHRJBnH9hOrHJnf7jWvJgV7MWIjbor6O3B9IbpdN +Fzy5kIrx0eMEL91Bpcg/8Jb+VSHlTqbN4TKu8R+ZRq4yCDqbYhhoqKGuGxPRxe/R +/+pMbXPU+8Btw5jVqZTg2LGiazY6Qe2IpB1ddOqLBWwOqtU61W9xnlkCtDAjGxSg +nAkuqKI0sUKtkOySoUgxBHWBgfE0gqrSd/ESMg4mHjW+QA2zHx3TJ7tQf1dZNn3n +Qr0IMTMrDyn3/HiIq1f5LY70FVOMOF/0U+vzfyuc1lrAe6Q4OK7CQ43oMCc0vkfi +kCB6A6qH1f2etgnEjatmFYkf8Zx7cpLaj8cdOMtunbdTbRQ= +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-secureboot.kek.auth b/gardenlinux/cert/gardenlinux-secureboot.kek.auth new file mode 100644 index 0000000000000000000000000000000000000000..f601027002b713d4d1e23ef3fb899bf69d7fe0fe GIT binary patch literal 3785 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 X{I^3V!s_?ZU-ls7J07|B_AvhkaE%=8 literal 0 HcmV?d00001 diff --git a/gardenlinux/cert/gardenlinux-secureboot.kek.chain b/gardenlinux/cert/gardenlinux-secureboot.kek.chain new file mode 100644 index 0000000..521e4fb --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.kek.chain @@ -0,0 +1,66 @@ +-----BEGIN CERTIFICATE----- +MIIFoTCCA4kCCEKlN7ZX7wMVMA0GCSqGSIb3DQEBCwUAMIGOMR0wGwYDVQQDDBRH +YXJkZW4gTGludXggcm9vdCBDQTELMAkGA1UEBhMCREUxETAPBgNVBAcMCFdhbGxk +b3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsMDEdhcmRlbiBMaW51eDElMCMG +CSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51eC5pbzAeFw0yMjA5MDYyMjE3 +MjJaFw0yNzA5MDUyMjE3MjJaMIGWMSUwIwYDVQQDDBxHYXJkZW4gTGludXggaW50 +ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UEBwwIV2FsbGRvcmYxDzAN +BgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExpbnV4MSUwIwYJKoZIhvcN +AQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMIICIjANBgkqhkiG9w0BAQEFAAOC +Ag8AMIICCgKCAgEAuMKbbw5RYzsKLGs90pLfsKdbUCEhL/wu/uT2n/L7GX8LZUH2 +BtKhIj0aQ/LCcDfbQlH7XAzeDXPAkOAcQhx9h/9pTnNVyuPFoVjBs3bI1+33rEtz +RODlwdGXDaQjLo4YyA5Yu4jYTn2978KCxF6Gm0e2uVfXYnEruy8nSX0hDALyifPb +YQRp5WCtXn8tGm5h++lH6FTkiLO/NMzXR3GwiIPFyDiyDz7WUEdS4gdMWLnbV0Ym +nAXGB1dxlJYSZUW4EDJ2MWiKJ6tJ4CjrFPAa59LfM41ZCkFO9O6zQBlXOJNOFb1g +aRTeEzoS+DlSo8QSerU4W7rOlXBIFmkZjvnBIInnmlphNqWMh6sfQZshb0Y/UbU1 +9NqbJC2vFAJCAxkVxG1PvEKKMlJQGRp+Ri4qHgenWfkIws+mwcNE+KPhPNWHYXw+ +6KAHeJ7ZVQmf8cIkgNWEdCVSMuWn9nmf4aqVJY8ChV//mcDmUTSYtK1WRL+3Di1+ +IrIMiXDj4jNGsPq81xtoW9CNMv2qt0nA3/Zb+/xj4+dY1j8EjuzF07oWjllgf8ez +XmasqNJ/i+c5IK+iN4IdW/zrSSzdww4T0dICvA3ouLCKJESzYoBCR/RoimyDLEAA +XOQurupWcvPVgHLONhMHKkG9/e9MuKr6utVQjkWqWoBYBuSGjMRwRrJI/xMCAwEA +ATANBgkqhkiG9w0BAQsFAAOCAgEAV2/g7v7EQLqAyDywWhz2tJ8NgJYdjLo+ylHu +AHR9/cWQPQ4HSeD6qSLuYoaW0JR9bvvu5SqlAymMk2UIO3t2Gqp5coQELhr9qk+b +sYwv6ga77W2lU4EMFvVhWLEs7ST+1CNmOnAbAShcke247S47Kfs+MMevWR5HN4ok +vrTwcoiTnyJzQLjT4ZgSBS9R7GA5r9r0a/vR60398kjOmqHzB9WUmR/0m3YwwBcz +rprS3AilG8X9htJ4y6MmIEEMEQJC+ySzV5JRFhECshefSLR/U8qPOcznugK6hyN9 +EPqCDX1r8wGz6834WShGAvYYcDx6ItRGvv5SiaiIXRmYmgit1E3/0yLSXvV68dT/ +kXcsqx1xA6HLSH69Dfe2U1x3oc7e+1g3XZlWdTjMGXNchCZC5bXKHi0/MDcXhq8A +sXtX6mZ547LD+gYWYlvTuJpBH/xmXWuLfQ8YrXIUPPS+UuDvV5O2dWQsdIRLU/tz +SGFpmqIfyU94IDX6K07mJYidhJ9i4gMHywXRPiqPjemU4bm0xDojAdKtRIKFVadU +P54Tar+GCHuSIzcnvf0By4CgHUEgWr2tfxxRcMdCG2NRr3L34uVc+oPWujj37zPO +wNKs5jUjQFUb98zrFweAPgAT0x8SvjKKeDIVMK4Eb7Mxj0VNlYVGu9wLwXcNY45W +VSNYXbQ= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFszCCA5sCCAE7poNeT3GwMA0GCSqGSIb3DQEBCwUAMIGWMSUwIwYDVQQDDBxH +YXJkZW4gTGludXggaW50ZXJtZWRpYXRlIENBMQswCQYDVQQGEwJERTERMA8GA1UE +BwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVuIExp +bnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMB4XDTIy +MDkwNjIyMTczNFoXDTI3MDkwNTIyMTczNFowgaAxLzAtBgNVBAMMJkdhcmRlbiBM +aW51eCBzZWN1cmVib290IFBLIGNlcnRpZmljYXRlMQswCQYDVQQGEwJERTERMA8G +A1UEBwwIV2FsbGRvcmYxDzANBgNVBAoMBlNBUCBTRTEVMBMGA1UECwwMR2FyZGVu +IExpbnV4MSUwIwYJKoZIhvcNAQkBFhZjb250YWN0QGdhcmRlbmxpbnV4LmlvMIIC +IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAy4uT6AYNkASZBh9tBMUAR1Li +spIHlTrR1UFPcFTUF9puolZHvSbuyjIPyhuztguYp0G2tl/XQjwH3bii2gC37ru+ +r3belOYkedg1HSbHn6wIxHJo2+OsycpDUyggDK2fIbsUs4wZvtwzYxqlMGEdZvRt +STtRVm0U70rASI+9m4CvvvwwXo0fMn/RclcCGEp5G7N0MK6dZm5fHPG6UDtDvJih +3F3fhWFXhDOw3xiPCwcrDsY8e5cVOIynYi3y0qOQX947ARQdkHHKfyU55IMqUvV2 +5vvzJPr+BcG/SZZrjnG2VI8Vix4ssiR/QWCFHi2+8HrRvC0ozP/tA4CU5t/CpF0q +I9fxM9zEysbBeGM+QLTFwD//ChN4XxgIdQBvYIkf0xifYKHr7nvR6yANGe2fMcFV +kpoco5q7jFwcIe3St0OZut7H/S1NxGCBEBu8o+jYwlVijtTqmMA9fW9Od1l+Crqd +/MLlSqOqEg3CQe0p0H1jU8AiScpe35VkJj/LTqqFwuh6VBF+znDtIio5eEmgChN9 +DIasws70g2VMlwnSJTALMyHF9DQqdS39VKEU+iCA5qsUOpD1D2WbSG2/DMbXDc0Z +6zzRWXvwyyy4JmnLp3YgcrLM5WO4X7m8kR9+kPRLQU4RsggHHeLzeQUEOpB9AJ4Y +tysl1i+32EkWTiVT58UCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEASyO+thlcCAAZ +Dd0dURVPC5fIAsMxHc6SsXu9csiJ7kzdQPyHXt2BbAn0MGhmJq1PuAuzCtIZiA7K +ScwV6tAfdvhypnvIVEpbDLcb0q0zwvoxisfeMFRt/lNFm2DXjixsG2Nd9lIxFa+E +yg1qvUZppoxWL6Z54z8DXQv5zCXmiW/XNvjTMgUKWtLVUbILQoNPV7YMt7MsF3Km +LtTDNidTweJJY1GpkhlmAJz707SRss4/7dfNydVxJ3WaHI4xHFF9IiGFKhxpDW9S +9N1+MffFDKqn0je4AHj2iv+ORLzgIanK1cZAAwMsCiqfUIjuKZAM9MhXT/i/Y2Xk +Hlf8kMOM+XlNwnxo8/M2hiEHLho9689cXySfSfU6Mj7LPHnfpwxrDM5r4GwYaSIp +GtIl2wcZKHfeR/JWSY68Ma/DPtzg3zwPUZyb5GB7NwztET+7pQ4mgwph92KOxHJB +qZSZY/xfWjX8OV2fS7GVmAUP3qAMNZjbfqtUcGcDwhOtbzUyiMqiMy95zFV/4Tnp +NiMTp9ZBCAFertv7EUteAYJ/oj4LuSpwQ9XPREaOXMHlnQVjSeh/vGd+sxSagIq+ +GpKfRV1RDTFP7yzIgPNAT6r/6eKiHvZoDwTXDPa6dKUTIANq3x720fpBEqjpgPft +iktik/34sn7vTLiwYM8ABjF+yNeUEo0= +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-secureboot.kek.conf b/gardenlinux/cert/gardenlinux-secureboot.kek.conf new file mode 100644 index 0000000..430ddb4 --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.kek.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU secureboot KEK certificate +days=1825 diff --git a/gardenlinux/cert/gardenlinux-secureboot.kek.crt b/gardenlinux/cert/gardenlinux-secureboot.kek.crt new file mode 100644 index 0000000..024d8db --- /dev/null +++ b/gardenlinux/cert/gardenlinux-secureboot.kek.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFvjCCA6YCCENrGr0WZy0qMA0GCSqGSIb3DQEBCwUAMIGgMS8wLQYDVQQDDCZH +YXJkZW4gTGludXggc2VjdXJlYm9vdCBQSyBjZXJ0aWZpY2F0ZTELMAkGA1UEBhMC +REUxETAPBgNVBAcMCFdhbGxkb3JmMQ8wDQYDVQQKDAZTQVAgU0UxFTATBgNVBAsM +DEdhcmRlbiBMaW51eDElMCMGCSqGSIb3DQEJARYWY29udGFjdEBnYXJkZW5saW51 +eC5pbzAeFw0yMjA5MDYyMjE3NDRaFw0yNzA5MDUyMjE3NDRaMIGhMTAwLgYDVQQD +DCdHYXJkZW4gTGludXggc2VjdXJlYm9vdCBLRUsgY2VydGlmaWNhdGUxCzAJBgNV +BAYTAkRFMREwDwYDVQQHDAhXYWxsZG9yZjEPMA0GA1UECgwGU0FQIFNFMRUwEwYD +VQQLDAxHYXJkZW4gTGludXgxJTAjBgkqhkiG9w0BCQEWFmNvbnRhY3RAZ2FyZGVu +bGludXguaW8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDE7tVPnsEr +YWECQHHOHB+UxpLLkWKhIedcd9uc/NjSj1s8FGxDvlFoRkFkzp9dx06wPxrptrPI +xm1ogArfEijSAUSsBkYydUpA8kAuNEYLuGuefVgiA2JZdIknif8RYTL4QE9tpAdg +C742ri+8X143k4RQ7Fv8QAv+AngHrHQT/PgMDHQZFUafXQMHid4XKak3uc/TID4o +UFuLFV1X1cgzcxV5UuyrGnFu8eLuvk1ZiVkgwx778kLXvBTxXHb3ONaORkorVJ4e +uEUZAo/eNjQGlRNEKmTU3BGYZdL9/UUV3d2stfB9E6WoyxvvBk3Xnv0M7/QZPxr6 +kDSgIc+nf6o5QgtWfs8WDCow14DAroYLqw5+LuOhXhY/mtj3aPkE7pUXHfNIFwmk +pYhr4BmT9Auv89bm/yrVqdf5qF0hI1ri8369R7qfIGvV2JOtr2UK0/9EYvBLClck +2d1WHFysb6l2lzE6/xvWDayKtSrG7UJ8I/fzJp96hHvRnQDSLP+zbJxK2MKZu5JZ +szblsdDuO0YRQ2KZPX1AgAz2lH4137djXd8bxMk07n+MD7clB8lGoUn60R8sJcc5 +gQ4Ybt3gd/F1PtC5VwLyMka0XBYo/c5KyIW+TBqkKXwOXyqE0tnH0iqMOkWHI83Q +60fH2f2RMoZ+3t/Ado4hBm3VJqnAG8qOlQIDAQABMA0GCSqGSIb3DQEBCwUAA4IC +AQAug5jK1cHM80xeokTAhv2KTu1JNeg2Ap4qV20Hw383EOwdVL5zdv1sHoa78fWK +ey12Gm3x8SDg4irxqCtLR+qrI7+TEdRTcSC0u5LORrmuE8f8P+zeWULVUIsCAAzE +HlwdSPTv/OjHfk0hUbB6YKQG5D/DXE8o2CdtNnzNH0Pc880WKZRs1UTf1x2Kw3mS +dDNz3gws/QUDLePBmhFxwO6pz/CYDu5x/U7uzJ0KbuknjB3EI6z4mkAfaEu868X6 +qAVbVyUTBXYfKew6C8hHRpAqqO64sEEM0y5EZTHXLb9CTJU2AOayFf+5J2ODI1Ph +xHjLOU5/R1kV1CPUoFqOngM/jX7STgmu2f7Y/1+XeIQWOw+6SHRF+YHOpjmcDqVs +zQK4+AZoTul8zDPnhxXwzfMy3owAATKai2bmRR53FxzkkJ6aaACKLkCNuTOP0E2M +5lPQx9NGy1gfth1SaRADZmP+TukXnDOPzfeNk+zVDEUS2paUQ62XkIvz46GPvC+1 +5lzbkNpjY1mgHksSYEPdsjPXuUs9Jrpi/7YInvdexLMSgeWKh+mRhTcIKa27+br8 +m/4ol6qicTnzRr2XtRdXSyLvvsTji/4BxzG6C5FaB24QtAmFFudf5ehCux1mTfDx +D4uDvElNqQb9aSbkU1MQOhmWeNN5s/6S2sQqLMCrYcXECA== +-----END CERTIFICATE----- diff --git a/gardenlinux/cert/gardenlinux-secureboot.null.pk.auth b/gardenlinux/cert/gardenlinux-secureboot.null.pk.auth new file mode 100644 index 0000000000000000000000000000000000000000..16a72e2a18e75d604528617dc548151614440f8f GIT binary patch literal 2267 zcmd5-`8(U`7SCeOH;k?shy{tB1BYdA;MS&5#_e3sj4NV9owL# zMoUYmT2fnM8`N;ArCJ%vpBMnOJ0Z~BA+dnKUkU|ammW;q8 z0f@NcT+r@z7VnNUa5fw{&qVk!cX}_i^~a{R#xc?pSg5@?~BIw=M=6XM8B8pF;6UzKbBP$ zVDNFYJlk*f1XNBXJBpj4ef)Krp4)mXZ~K$h)>q-qc6@#)Gpfzb6shJ2dcI-?_X%9jpE`)#;q21P|%tp~w{t zz2kBC5((KPAmbiqXf2K8R3JX84I+%yyVs8D#Tb0?D3#kn(|I*=X4&gfq@q*d?Lf~2 zauD^_eB^B6`+j}44!OT77EP@mSR}Ijnp>}{C1r>@~YE}_0B(hXHvt&*TCQ)omyu$qCsL5bxD$oACQ0ZyISlfwKB2Kt**gn*gnxCnDo12!yBiGG2`gQ?~Jw;KK5Q)T}g+Bakbswc#NK~O6$(u*mf z!UZa{NkY=}dhGj<8#3(=!kKLMpvETIYtr67zq;$>KcDnyaJN*F-0W|YSZ3il|FtH; zw&$xyy=2`v3zrk&tf!rD#VM;8!?7}X;~l;YJz@1>yU>{Vd0qSWu`;{t=wtm(ttJ*2 z-PPWL*eA>UjR%Vrz5Vu;gt2HpqXvd<_ro&91(z!yferL-iFcljXZci%T#fLwhgM%& zJzABQz^Zt;eO;5GVaDm2woK66I26U+4fTU*ED!Js4;_%9TbS2W@Vs>wro>yZ6&e8{ zs0TZN&!lg*6=neADiq6sKWkra86LK2WG~Bm(W|dq$af9TIZDZe$+g+V`MtDXv;T;@ z(fDYpTK}2!EFTDY-r4?mEmdrD^Rx5VRNiy5^itW_@SFf8`j@L6UnhA~BJqC9VA#$o zg}L#IO|-9$y^?7u%8~d_TKCD=Xw&KCb0UMTla+@gqqGqEeC>DY@9FdwWh_XHH$eEa zg6wWxeblo`ns}3nG`84uxW{nqVcOqOTpZ@-!8L}<73gkQ+q0vcw_YZ>o22m7x;XjA z`N7bUe*=$QqhtU8 literal 0 HcmV?d00001 diff --git a/gardenlinux/cert/gardenlinux-secureboot.pk.auth b/gardenlinux/cert/gardenlinux-secureboot.pk.auth new file mode 100644 index 0000000000000000000000000000000000000000..7dfb98918c3680b08644eb4621048199499f41b2 GIT binary patch literal 3774 zcmeHJ`#;p#8=ng^!_1I|7}s&nAfFkx$YNGjLyXH{-7jOrx{M}jklTor#AH=UmTsif zh}NYf2BjfFQn`#pw$>tRBA04MyZd_i{sZ6F_xpNvemUnkuje`M=Q-y&&-0#vOTc!> zXo3D6-3ZYNvM5zLz;zTv%#(AgOj69f=#>B)!G(aq1Ze;blL*e<2c|>CAYgG2fP_il zv-f4Mpuuo(Ia|B|V2jrQY-kAt0p3%Q8Av%;vuW{<3K%J}$T&mL1~g$%PcbM;*&&1( zMvukqkBm)V;UZ(>>C6~9ievz)@@OIv zumH@6M1r}oiLXdB7m23-h(Ja;!3Z$?+i{((0JuYRS^|?E%3#FfT%B++>?JRU`#SS{qBLKrt{z!jNvf|88vfP_KGt~Y6{unUUJLi|c+rej;tNw1okwNW zhz@4N$W--XzMaQT9O`D7`aRjYJZ#ITF-`F*5TX(;j3MuF^NNvOaBSUIaK9v-+p+-! zo>U{I^)tO83XX|Nb@9Nh((u?I<;AI%8P^CD3XpJpsfSj{XXSS1 zRfZb=IdGX1G`e1;EZib&6nT2;CY7T zKl{D7^_vui%MqVc$-AW+yw{GGuWv{5NOO8mQ)nKo8f1Q;;B=UdO}ESSEZ&b*%$Kw z^>7&d1UqZh@00wl+n`Y=vb*vyj#=CDn${T9)O<=Ul_PW_xyaNa;3|_#iQ;0GoD|T{ z3Jv<&&y5;hkaxIfd;Hc3fr3Hctr;TvkfIC$gPb&5>aqR^5EebA;wDEyp6!CP6I6N& zZztbpb{(JJKW4k}W8hduw1g0d2-mquX++jZ4q$Vn_~ahB-=C@-{=lqA?xH&Sp&FD1 zZW{B}2)U2O0cy-A5Brkf;e3N=C0f9;J3)?njF0}|{&$fTd0s{piBmSv0OV?q_RMj{ zu-S(}qPV2*z>r%l^4m;`cRi}1&On}7vFlm8nXX6MB$?(`Rfr7-U05HybE>x2W^VY& zb=2f&s$A&<{gb#S~AO4hwoDSX132iNUMze-jZnDZIvjf zL`9)`qb8yiA~p0B2ee1v*qukl9scwp=Qk6$?bf3c0xM~^3nkOR$>yjz8Jl}orF1eS zLsmlbJD8-ZqI0y3AYaoBi-0nx+oy}grN_%rro|(vH>hz3pghb?hAAf8HWzJJ^6QgIsjOPo>A5Y6 zg=O{uZfF8!!JsRB$(C~c^V`Wws>>15V#BEAyYW{sIOq=o)#d&*()Md_(^uwlokD;5 z_@Op+VSi&ya32UpNbMRf+J2J2hV+T5M7OBM0~we9XKVC-c9pGtXiJk3RXs5_1a!kE zZ@d!0mX%7_aGTWl6_@}m?zTtX^?H%B+J^!~P1%>^3P+C;k&R{FTl&?;1##YaQ@PpF zu1QY*Qgut))Yn_WMhlqhwoQMe=U8!o>UI}$;Yf4=mf)$(o=T+gH%hR-Xb7#_pUb=g z%-lPCFC@C!drQf&&Xa!5@EM$tsAu2N8x(LUG6Tl(npXkfLfXX2sU~ZfUpjZ}(7f#t zs2V))eRSp_`3!}sb3E$0BGKrmiIFWfrfMWqtEoK6JG~1-^(l6aZ(;2&?-x4vFE}Lf zHC=Aw{Y(lKrda5m=dN6xQ(bF3F%#ge7 z!JtE*t~2l{=%pbZw_HCwSEi-6nsnQ7EqTdZ%SU)kakSN!Yni6MDD`qJ&SxI6zR&Fm z!~EC{o4i6&FT91xq|o_*uHT{1l9g#Eh^80IfZ{Mx6O-`$$` z<)GVnw)Ql~ewavOxXM2r6h5h0mge;jO&!(KS#nQzAL>d{v_cJDQw_5aSg%G2?%-AW zkjn-v{n662xsfgdXKwqU{Dc`fKCA*UFXO5Qm08Lzs0lAJ%)02)dUd*}((%V%ZpiuT wzhh2`|4nipiRMu2HQ(JxmR+@~M=d!#N#QGN`N~?pvX-x`&2 + exit 1 + fi + shift 2 + ;; + --algorithm) + algo="$2" + shift 2 + ;; + --algorithm-options) + algo_opt="$2" + shift 2 + ;; + --aws-kms-key-spec) + aws_kms_key_spec="$2" + shift 2 + ;; + --days) + days="$2" + shift 2 + ;; + --extensions) + ext_file="$2" + shift 2 + ;; + *) + break + ;; + esac +done + +if [ -n "$conf" ]; then + while IFS='=' read -r key value; do + declare "$key"="$value" + done < <(envsubst < "$conf") +fi + +if [ -z "$algo_opt" ]; then + case "$algo" in + RSA) + algo_opt=rsa_keygen_bits:4096 + ;; + *) + echo "algorithm options not set and can't be inferred for algorithm $algo" >&2 + exit 1 + ;; + esac +fi + +engine_params=() +if [ -n "$ca_base" ]; then + if [ ! -f "$ca_base.crt" ]; then + echo "$ca_base.crt does not exist" >&2 + exit 1 + fi + + if [ -f "$ca_base.key" ]; then + ca_key_params=(-CAkey "$ca_base.key") + elif [ -f "$ca_base.arn" ]; then + engine_params+=(-engine pkcs11) + ca_key_params=(-CAkeyform engine -CAkey "pkcs11:token=$(basename "$(cat "$ca_base.arn")" | cut -c -32)") + else + echo "neither $ca_base.key nor $ca_base.arn exists, but at least one is required" >&2 + exit 1 + fi +fi + +base="${1%.crt}" +if [ "$base.crt" != "$1" ]; then + echo "output file must end in .crt" >&2 + exit 1 +fi + +if [ -z "$aws_kms_key_spec" ]; then + echo "Generating $algo private key -> $base.key" + openssl genpkey -algorithm "$algo" -pkeyopt "$algo_opt" -out "$base.key" + + key_params=(-key "$base.key") +else + echo "Generating AWS KMS backed $aws_kms_key_spec private key -> $base.arn" + aws kms create-key \ + --description "$CERT_CN" \ + --key-usage SIGN_VERIFY \ + --customer-master-key-spec "$aws_kms_key_spec" \ + --query "KeyMetadata.Arn" \ + --output text > "$base.arn" + + engine_params+=(-engine pkcs11) + key_params=(-keyform engine -key "pkcs11:token=$(basename "$(cat "$base.arn")" | cut -c -32)") +fi + +subj="/CN=$CERT_CN/C=$CERT_C/L=$CERT_L/O=$CERT_O/OU=$CERT_OU/emailAddress=$CERT_E/" + +ext_opts=() +if [ -n "$ext_file" ]; then + ext_file_path="$(cd "$(dirname "$conf")" && realpath "$ext_file")" + while read -r ext; do + ext_opts+=(-addext "$ext") + done < "$ext_file_path" +fi + +if [ -z "$ca_base" ]; then + echo "Generating self signed certificate ($CERT_CN) -> $base.crt" + openssl req -new -sha256 "${engine_params[@]}" "${key_params[@]}" -x509 -days "$days" -subj "$subj" "${ext_opts[@]}" -out "$base.crt" +else + echo "Generating certificate ($CERT_CN), signed by $ca_base.crt -> $base.crt" + openssl req -new -sha256 "${engine_params[@]}" "${key_params[@]}" -subj "$subj" "${ext_opts[@]}" -out "$base.csr" + openssl x509 -sha256 "${engine_params[@]}" -CA "$ca_base.crt" "${ca_key_params[@]}" -set_serial "0x$(openssl rand -hex 8)" -days "$days" -req -in "$base.csr" -out "$base.crt" + rm "$base.csr" +fi + +if [ -n "$ca_base" ]; then + if [ -f "$ca_base.chain" ]; then + echo "Writting certificate chain for $base.crt ($CERT_CN) -> $base.chain" + cat "$ca_base.chain" "$ca_base.crt" > "$base.chain" + else + echo "Initialising empty certificate chain (direct root CA successor) for $base.crt ($CERT_CN) -> $base.chain" + touch "$base.chain" + fi +fi diff --git a/gardenlinux/cert/genefiauth b/gardenlinux/cert/genefiauth new file mode 100755 index 0000000..277c624 --- /dev/null +++ b/gardenlinux/cert/genefiauth @@ -0,0 +1,72 @@ +#!/usr/bin/env bash + +set -Eeufo pipefail + +export PKCS11_MODULE_PATH="/usr/lib/$(uname -m)-linux-gnu/pkcs11/aws_kms_pkcs11.so" + +guid= +ca_base= +null=0 + +while [ $# -gt 0 ]; do + case "$1" in + --guid) + guid="$2" + shift 2 + ;; + --guid-file) + guid="$(cat "$2")" + shift 2 + ;; + --ca) + ca_base="${2%.crt}" + if [ "$ca_base.crt" != "$2" ]; then + echo "CA file must end in .crt" >&2 + exit 1 + fi + shift 2 + ;; + --null) + null=1 + shift + ;; + *) + break + ;; + esac +done + +if [ "$null" = 0 ]; then + cert="$1" + shift +fi +base="${1%.auth}" + +type="${base##*.}" +case "$type" in + pk) type=PK ;; + kek) type=KEK ;; +esac + +if [ -n "$ca_base" ]; then + if [ ! -f "$ca_base.crt" ]; then + echo "$ca_base.crt does not exist" >&2 + exit 1 + fi + + if [ -f "$ca_base.key" ]; then + ca_key_params=(-k "$ca_base.key") + elif [ -f "$ca_base.arn" ]; then + ca_key_params=(-e pkcs11 -k "pkcs11:token=$(basename "$(cat "$ca_base.arn")" | cut -c -32)") + else + echo "neither $ca_base.key nor $ca_base.arn exists, but at least one is required" >&2 + exit 1 + fi +fi + +if [ "$null" = 0 ]; then + cert-to-efi-sig-list -g "$guid" "$cert" "$base.esl" + sign-efi-sig-list -g "$guid" -c "$ca_base.crt" "${ca_key_params[@]}" "$type" "$base.esl" "$base.auth" +else + sign-efi-sig-list -g "$guid" -c "$ca_base.crt" "${ca_key_params[@]}" "$type" /dev/null "$base.auth" +fi diff --git a/gardenlinux/cert/gengpg b/gardenlinux/cert/gengpg new file mode 100755 index 0000000..2202d2f --- /dev/null +++ b/gardenlinux/cert/gengpg @@ -0,0 +1,35 @@ +#!/bin/bash + +set -eufo pipefail + +conf=gpg.conf + +while [ $# -gt 0 ]; do + case "$1" in + --conf) + conf="$2" + shift 2 + ;; + *) + break + ;; + esac +done + +base="${1%.*}" + +tmp_gnupg_home="$(mktemp -d)" +chmod 700 "$tmp_gnupg_home" + +# using $tmp_gnupg_home as an ephemeral GNUPGHOME directory, since %secring parameter is a no-op in gpg >= 2.1 +export GNUPGHOME="$tmp_gnupg_home" + +echo "Generating GPG key ($GPG_EMAIL)" +envsubst < "$conf" | gpg --generate-key --batch +echo "Exporting GPG private key ($GPG_EMAIL) -> $base.key" +gpg --export-secret-key --armor "$GPG_EMAIL" > "$base.key" +echo "Exporting GPG public key ($GPG_EMAIL) -> $base.pub" +gpg --export --armor "$GPG_EMAIL" > "$base.pub" + +# using separate variable here instead of GNUPGHOME to ensure that under no circumstances (e.g. accidentally commenting out setting GNUPGHOME) the user GNUPGHOME gets deleted +rm -rf "$tmp_gnupg_home" diff --git a/gardenlinux/cert/gpg.conf b/gardenlinux/cert/gpg.conf new file mode 100644 index 0000000..61c6887 --- /dev/null +++ b/gardenlinux/cert/gpg.conf @@ -0,0 +1,7 @@ +%no-protection +Key-Type: $GPG_KEY_TYPE +Key-Length: $GPG_KEY_LENGTH +Key-Usage: sign +Name-Real: $GPG_NAME +Name-Email: $GPG_EMAIL +Expire-Date: 0 diff --git a/gardenlinux/cert/intermediate-ca.conf b/gardenlinux/cert/intermediate-ca.conf new file mode 100644 index 0000000..ec679c0 --- /dev/null +++ b/gardenlinux/cert/intermediate-ca.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU dev intermediate CA +days=1825 diff --git a/gardenlinux/cert/kernel-sign.conf b/gardenlinux/cert/kernel-sign.conf new file mode 100644 index 0000000..a8eecb4 --- /dev/null +++ b/gardenlinux/cert/kernel-sign.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU dev kernel signing certificate +days=365 diff --git a/gardenlinux/cert/root-ca.conf b/gardenlinux/cert/root-ca.conf new file mode 100644 index 0000000..eab5196 --- /dev/null +++ b/gardenlinux/cert/root-ca.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU dev root CA +days=1825 diff --git a/gardenlinux/cert/secureboot.db.conf b/gardenlinux/cert/secureboot.db.conf new file mode 100644 index 0000000..586c89c --- /dev/null +++ b/gardenlinux/cert/secureboot.db.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU dev secureboot db certificate +days=365 diff --git a/gardenlinux/cert/secureboot.kek.conf b/gardenlinux/cert/secureboot.kek.conf new file mode 100644 index 0000000..44bd47c --- /dev/null +++ b/gardenlinux/cert/secureboot.kek.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU dev secureboot KEK certificate +days=1825 diff --git a/gardenlinux/cert/secureboot.pk.conf b/gardenlinux/cert/secureboot.pk.conf new file mode 100644 index 0000000..71bf82e --- /dev/null +++ b/gardenlinux/cert/secureboot.pk.conf @@ -0,0 +1,2 @@ +CERT_CN=$CERT_OU dev secureboot PK certificate +days=1825 diff --git a/gardenlinux/container/Makefile b/gardenlinux/container/Makefile new file mode 100644 index 0000000..d2a80d3 --- /dev/null +++ b/gardenlinux/container/Makefile @@ -0,0 +1,80 @@ +VERSION=`../bin/garden-version` +VERSION_NUMBER_MAJOR=$(shell ../bin/garden-version --major) +VERSION_NUMBER_MINOR=$(shell ../bin/garden-version --minor) +VERSION_NUMBER=$(VERSION_NUMBER_MAJOR).$(VERSION_NUMBER_MINOR) +ALTNAME= +ALTNAME_INTERNAL=$(shell [ -n "$(ALTNAME)" ] && printf "%s %s" "-t" "$(ALTNAME)" ) + +PATH_KERNEL_PACKAGES="../.packages/main/l/linux" +GARDENLINUX_BUILD_CRE ?= sudo podman + +all: build-image build-cert build-integration-test + +.PHONY: needslim +needslim: + @./needslim + +.PHONY: build-image +build-image: needslim + cp -p ../gardenlinux.asc build-image/gardenlinux.asc + if [ -n "$$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build-image:$(VERSION_NUMBER) --format "{{.Repository}}:{{.Tag}}")" ]; then \ + $(GARDENLINUX_BUILD_CRE) image rm --force gardenlinux/build-image:$(VERSION_NUMBER) || true; \ + fi + @$(GARDENLINUX_BUILD_CRE) build --build-arg VERSION=$(VERSION) -t gardenlinux/build-image:$(VERSION) -t gardenlinux/build-image:$(VERSION_NUMBER) $(ALTNAME_INTERNAL) build-image + rm build-image/gardenlinux.asc + +.PHONY: build-cert +build-cert: needslim + cp -p ../gardenlinux.asc build-cert/gardenlinux.asc + @$(GARDENLINUX_BUILD_CRE) build --build-arg VERSION=$(VERSION) -t gardenlinux/build-cert:$(VERSION) $(ALTNAME_INTERNAL) build-cert + rm build-cert/gardenlinux.asc + +.PHONY: build +build: needslim + cp ../checksums.sha256 build/checksums.sha256 + @$(GARDENLINUX_BUILD_CRE) build --build-arg BUILDARCH="$$([ "$$(uname -m)" = "aarch64" ] && echo "arm64" || echo "amd64")" -t gardenlinux/build $(ALTNAME_INTERNAL) build + rm build/checksums.sha256 + +.PHONY: build-deb +build-deb: build + @$(GARDENLINUX_BUILD_CRE) build -t gardenlinux/build-deb $(ALTNAME_INTERNAL) build-deb + +.PHONY: build-base-test +build-base-test: needslim + cp -p ../gardenlinux.asc base-test/gardenlinux.asc + cp ../tests/Pipfile base-test/ + @$(GARDENLINUX_BUILD_CRE) build --build-arg VERSION=$(VERSION) -t gardenlinux/base-test:$(VERSION) base-test + rm base-test/gardenlinux.asc + rm base-test/Pipfile + +.PHONY: build-integration-test +build-integration-test: build-base-test + cp ../checksums.sha256 integration-test/checksums.sha256 + cat integration-test/cloud.google.gpg.base64 | base64 -d > integration-test/cloud.google.gpg + @$(GARDENLINUX_BUILD_CRE) build --build-arg VERSION=$(VERSION) -t gardenlinux/integration-test:$(VERSION) integration-test + rm integration-test/checksums.sha256 + rm integration-test/cloud.google.gpg + +.PHONY: build-kernelmodule +build-kernelmodule: + cp -p ../gardenlinux.asc build-kernelmodule/gardenlinux.asc + @$(GARDENLINUX_BUILD_CRE) build \ + --build-arg VERSION=$(VERSION) \ + --build-arg ARCH="arm64" \ + --build-arg GNU_TYPE_PACKAGE="aarch64-linux-gnu"\ + -t gardenlinux/build-kernelmodule-arm64:$(VERSION) build-kernelmodule + @$(GARDENLINUX_BUILD_CRE) build \ + --build-arg VERSION=$(VERSION) \ + --build-arg ARCH="amd64" \ + --build-arg GNU_TYPE_PACKAGE="x86-64-linux-gnu" \ + -t gardenlinux/build-kernelmodule-amd64:$(VERSION) build-kernelmodule + rm build-kernelmodule/gardenlinux.asc + +.PHONY: clean +clean: + rm -rf integration-test/_pipfiles + -@[ -n "$$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/integration-test --format "{{.ID}}")" ] && $(GARDENLINUX_BUILD_CRE) image rm --force $$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/integration-test --format "{{.Repository}}:{{.Tag}}"); true + -@[ -n "$$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build-image --format "{{.ID}}")" ] && $(GARDENLINUX_BUILD_CRE) image rm --force $$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build-image --format "{{.Repository}}:{{.Tag}}"); true + -@[ -n "$$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build-deb --format "{{.ID}}")" ] && $(GARDENLINUX_BUILD_CRE) image rm --force $$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build-deb --format "{{.Repository}}:{{.Tag}}"); true + -@[ -n "$$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build --format "{{.ID}}")" ] && $(GARDENLINUX_BUILD_CRE) image rm --force $$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/build --format "{{.Repository}}:{{.Tag}}"); true + -@[ -n "$$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/slim --format "{{.ID}}")" ] && $(GARDENLINUX_BUILD_CRE) image rm --force $$($(GARDENLINUX_BUILD_CRE) image ls gardenlinux/slim --format "{{.Repository}}:{{.Tag}}"); true diff --git a/gardenlinux/container/base-test/Dockerfile b/gardenlinux/container/base-test/Dockerfile new file mode 100644 index 0000000..7e40050 --- /dev/null +++ b/gardenlinux/container/base-test/Dockerfile @@ -0,0 +1,55 @@ +FROM gardenlinux/slim + +ENV DEBIAN_FRONTEND noninteractive +ENV SHELL /bin/bash +ENV PYTHONPATH /gardenlinux/bin:/gardenlinux/ci:/gardenlinux/ci/glci:/gardenlinux/tests:/gardenlinux/features +ARG GARDENLINUX_MIRROR_KEY=/etc/apt/trusted.gpg.d/gardenlinux.asc +ARG VERSION + +COPY gardenlinux.asc $GARDENLINUX_MIRROR_KEY +RUN chown root:root $GARDENLINUX_MIRROR_KEY \ + && chmod 644 $GARDENLINUX_MIRROR_KEY + +RUN echo "deb http://deb.debian.org/debian testing main contrib non-free" > /etc/apt/sources.list \ + && echo "deb [signed-by=$GARDENLINUX_MIRROR_KEY] http://repo.gardenlinux.io/gardenlinux $VERSION main" >> /etc/apt/sources.list \ + && sed -i 's/deb.debian.org/cdn-aws.deb.debian.org/g' /etc/apt/sources.list \ + && if [ -f "/etc/apt/sources.list.d/debian.sources"] ; then sed -i 's/deb.debian.org/cdn-aws.deb.debian.org/g' /etc/apt/sources.list.d/debian.sources ; else echo "file not present" ; fi \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + curl \ + unzip \ + ca-certificates \ + less \ + apt-transport-https \ + gnupg \ + python3-pip \ + python3-pytest \ + python3-venv \ + pipenv \ + vim \ + procps \ + openssh-client \ + inetutils-ping \ + wget \ + iproute2 \ + libguestfs-tools \ + qemu-system-x86 \ + qemu-system-arm \ + qemu-efi-aarch64 \ + shellcheck \ + build-essential \ + libpython3-dev \ + onmetal-image + +# Prepare virtual environment +# We need a virtual env to install packages via pip, and not via apt. +# See: https://peps.python.org/pep-0668/ +ENV VIRTUAL_ENV_PARENT=/opt/python-test-env +ENV VIRTUAL_ENV="$VIRTUAL_ENV_PARENT/.venv" +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +COPY Pipfile "$VIRTUAL_ENV_PARENT" +# Do not use --system, we want the pip from the virtual env +RUN cd "$VIRTUAL_ENV_PARENT" && pipenv install --dev --skip-lock +WORKDIR /gardenlinux/tests diff --git a/gardenlinux/container/base/create.sh b/gardenlinux/container/base/create.sh new file mode 100755 index 0000000..8436741 --- /dev/null +++ b/gardenlinux/container/base/create.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +../../build.sh --features=oci . diff --git a/gardenlinux/container/build-cert/Dockerfile b/gardenlinux/container/build-cert/Dockerfile new file mode 100644 index 0000000..4750e3f --- /dev/null +++ b/gardenlinux/container/build-cert/Dockerfile @@ -0,0 +1,22 @@ +ARG build_base_image=gardenlinux/slim + +FROM $build_base_image +ARG DEBIAN_FRONTEND=noninteractive +ARG GARDENLINUX_MIRROR_KEY=/etc/apt/trusted.gpg.d/gardenlinux.asc +ARG VERSION + +COPY gardenlinux.asc $GARDENLINUX_MIRROR_KEY +RUN chown root:root $GARDENLINUX_MIRROR_KEY \ + && chmod 644 $GARDENLINUX_MIRROR_KEY + +RUN sed -i 's/deb.debian.org/cdn-aws.deb.debian.org/g' /etc/apt/sources.list.d/debian.sources +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates wget make gettext openssl libengine-pkcs11-openssl gnupg golang-cfssl efitools uuid-runtime awscli python3 python3-pip python3-venv python3-crc32c git +RUN echo "deb [signed-by=$GARDENLINUX_MIRROR_KEY] https://repo.gardenlinux.io/gardenlinux $VERSION main" >> /etc/apt/sources.list && \ + apt-get update && apt-get install -y --no-install-recommends libaws-sdk-cpp-dev aws-kms-pkcs11 + +# Prepare virtual environment +ENV VIRTUAL_ENV=/opt/venv +RUN python3 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +RUN pip install git+https://github.com/awslabs/python-uefivars diff --git a/gardenlinux/container/build-deb/Dockerfile b/gardenlinux/container/build-deb/Dockerfile new file mode 100644 index 0000000..751cb69 --- /dev/null +++ b/gardenlinux/container/build-deb/Dockerfile @@ -0,0 +1,6 @@ +ARG build_base_image=gardenlinux/build +FROM $build_base_image + +RUN sudo apt-get update \ + && sudo apt-get install -y devscripts + diff --git a/gardenlinux/container/build-image/Dockerfile b/gardenlinux/container/build-image/Dockerfile new file mode 100644 index 0000000..f3d68e9 --- /dev/null +++ b/gardenlinux/container/build-image/Dockerfile @@ -0,0 +1,68 @@ +ARG build_base_image=gardenlinux/slim +FROM $build_base_image +ARG DEBIAN_FRONTEND=noninteractive +ARG GARDENLINUX_MIRROR_KEY=/etc/apt/trusted.gpg.d/gardenlinux.asc +ARG VERSION + +COPY gardenlinux.asc $GARDENLINUX_MIRROR_KEY +RUN chown root:root $GARDENLINUX_MIRROR_KEY \ + && chmod 644 $GARDENLINUX_MIRROR_KEY + +RUN if [ "$(dpkg --print-architecture)" != amd64 ]; then dpkg --add-architecture amd64; fi + +RUN : "Initialize the build container package installation" \ + && sed -i 's/deb.debian.org/cdn-aws.deb.debian.org/g' /etc/apt/sources.list.d/debian.sources \ + && arch="$(dpkg --print-architecture)" \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + wget \ + ca-certificates \ + && \ + : "Extend the sources with other required repos" \ + && echo "deb [signed-by=$GARDENLINUX_MIRROR_KEY] https://repo.gardenlinux.io/gardenlinux $VERSION main" >> /etc/apt/sources.list \ + && echo "deb https://cdn-aws.deb.debian.org/debian unstable main" >> /etc/apt/sources.list \ + && echo 'APT::Default-Release "testing";' > /etc/apt/apt.conf.d/default-testing \ + && apt-get update \ + && \ + : "Install all required build tools" \ + && apt-get install -y --no-install-recommends \ + debian-ports-archive-keyring \ + debootstrap \ + gettext-base \ + dosfstools mtools datefudge squashfs-tools e2fsprogs \ + fdisk mount cryptsetup gnupg xz-utils bsdextrautils \ + sbsigntool \ + libcap2-bin \ + python3 \ + python3-mako \ + python3-yaml \ + qemu-user-static \ + qemu-utils \ + cpio \ + syslinux:amd64 syslinux-common:amd64 isolinux:amd64 xorriso:amd64 \ + dpkg-dev \ + procps \ + iproute2 \ + rsync \ + openssh-client \ + qemu-system-arm \ + qemu-system-x86 \ + openssl \ + podman fuse-overlayfs \ + libengine-pkcs11-openssl \ + onmetal-image \ + oras \ + libaws-sdk-cpp-dev \ + aws-kms-pkcs11 \ + systemd \ + python3-networkx \ + && apt-get install -t unstable -y --no-install-recommends \ + binutils-x86-64-linux-gnu \ + binutils-aarch64-linux-gnu \ + && \ + : "Clean up repo files" \ + && rm -rf /var/lib/apt/lists/* + +ENV PATH=${PATH}:/opt/gardenlinux/bin +RUN echo "progress=bar:force:noscroll\nverbose=off" >> /etc/wgetrc +WORKDIR /tmp diff --git a/gardenlinux/container/build-kernelmodule/.gitignore b/gardenlinux/container/build-kernelmodule/.gitignore new file mode 100644 index 0000000..8d6e6f5 --- /dev/null +++ b/gardenlinux/container/build-kernelmodule/.gitignore @@ -0,0 +1 @@ +*.asc \ No newline at end of file diff --git a/gardenlinux/container/build-kernelmodule/Dockerfile b/gardenlinux/container/build-kernelmodule/Dockerfile new file mode 100644 index 0000000..307281d --- /dev/null +++ b/gardenlinux/container/build-kernelmodule/Dockerfile @@ -0,0 +1,58 @@ +# TODO: switch to gardenlinux-slim +FROM debian:bookworm-slim +ARG GARDENLINUX_MIRROR_KEY=/etc/apt/trusted.gpg.d/gardenlinux.asc +ARG VERSION +ARG GNU_TYPE_PACKAGE +ARG ARCH + + +RUN dpkg --add-architecture "$ARCH" + +# Setup Garden Linux Repository +COPY gardenlinux.asc $GARDENLINUX_MIRROR_KEY +RUN chown root:root $GARDENLINUX_MIRROR_KEY \ + && chmod 644 $GARDENLINUX_MIRROR_KEY + +# ca-certificates is required to verify repo.gardenlinux.io +RUN apt-get update && apt-get install -qy --no-install-recommends ca-certificates + +# Pin linux-* packages to the garden linux repo, while keeping build dependency tools in debian mirror repo +ADD ./gardenlinux-apt-preferences /etc/apt/preferences.d/gardenlinux + +RUN echo "deb [signed-by=$GARDENLINUX_MIRROR_KEY] https://repo.gardenlinux.io/gardenlinux $VERSION main" >> /etc/apt/sources.list.d/gardenlinux-$VERSION.list + +# Install garden linux kernel header and debug definitions +RUN apt-get update && \ + apt-get install -qy --no-install-recommends \ + linux-headers-6.1-${ARCH} \ + linux-image-6.1-${ARCH}-dbg \ + linux-kbuild-6.1 + +# Install general kernel module build dependencies +RUN apt-get update && \ + apt-get install -qy --no-install-recommends \ + bc \ + build-essential \ + cpio \ + curl \ + devscripts \ + dkms \ + dwarves \ + flex \ + git \ + gnupg \ + kernel-wedge \ + kmod \ + kpatch \ + kpatch-build \ + libelf-dev \ + libncurses5-dev \ + libssl-dev \ + pristine-lfs \ + python3 \ + python3-debian \ + quilt \ + rsync \ + sudo \ + vim \ + wget diff --git a/gardenlinux/container/build-kernelmodule/gardenlinux-apt-preferences b/gardenlinux/container/build-kernelmodule/gardenlinux-apt-preferences new file mode 100644 index 0000000..f78bd20 --- /dev/null +++ b/gardenlinux/container/build-kernelmodule/gardenlinux-apt-preferences @@ -0,0 +1,15 @@ +Package: * +Pin: release o=GardenLinux +Pin-Priority: 1000 + +Package: linux-* +Pin: release o=bookworm +Pin-Priority: -1 + +Package: linux-* +Pin: release o=sid +Pin-Priority: -1 + +Package: linux-* +Pin: release o=experimental +Pin-Priority: -1 diff --git a/gardenlinux/container/build/Dockerfile b/gardenlinux/container/build/Dockerfile new file mode 100644 index 0000000..acd4b62 --- /dev/null +++ b/gardenlinux/container/build/Dockerfile @@ -0,0 +1,43 @@ +ARG build_base_image=gardenlinux/slim +FROM $build_base_image + +ARG DEBIAN_FRONTEND=noninteractive +COPY checksums.sha256 /checksums.sha256 + +RUN mkdir /etc/sudoers.d \ + && echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/wheel \ + && echo "deb-src https://deb.debian.org/debian testing main" >> /etc/apt/sources.list \ + && echo "#deb https://deb.debian.org/debian unstable main" >> /etc/apt/sources.list \ + && echo "#deb-src https://deb.debian.org/debian unstable main" >> /etc/apt/sources.list \ + && sed -i 's/deb.debian.org/cdn-aws.deb.debian.org/g' /etc/apt/sources.list \ + && sed -i 's/deb.debian.org/cdn-aws.deb.debian.org/g' /etc/apt/sources.list.d/debian.sources \ + && echo "APT::Install-Recommends false;\nAPT::Install-Suggests false;\nApt::AutoRemove::SuggestsImportant false;\n" > /etc/apt/apt.conf.d/no-recommends \ + && echo "progress=bar:force:noscroll" >> /etc/wgetrc \ + && echo "force-confold\nforce-confdef" > /etc/dpkg/dpkg.cfg.d/forceold + +ARG BUILDARCH=amd64 + +ADD https://cdn-aws.deb.debian.org/debian/pool/main/g/gcc-defaults/gcc_10.2.1-1_${BUILDARCH}.deb / +ADD https://cdn-aws.deb.debian.org/debian/pool/main/g/gcc-defaults/g++_10.2.1-1_${BUILDARCH}.deb / +ADD https://cdn-aws.deb.debian.org/debian/pool/main/g/gcc-defaults/cpp_10.2.1-1_${BUILDARCH}.deb / + +RUN apt-get update \ + && apt-get install -y wget ca-certificates coreutils \ + && sha256sum -c --ignore-missing /checksums.sha256 \ + && if [ "${BUILDARCH}" = "amd64" ] ; then wget https://cdn-aws.deb.debian.org/debian/pool/main/g/gcc-defaults/gcc-multilib_10.2.1-1_${BUILDARCH}.deb; fi \ + && apt-get install -y -f /*.deb \ + && apt-mark auto gcc g++ cpp gcc-multilib \ + && rm -f /*.deb \ + && dpkg -P gcc-9-base \ + && apt-get install -y build-essential fakeroot sudo gettext uuid-runtime efitools \ + && addgroup --system wheel \ + && adduser dev --disabled-password --gecos dev \ + && adduser dev wheel + +COPY install-cfssl.sh /setup/ +RUN /setup/install-cfssl.sh + +USER dev +WORKDIR /home/dev + +RUN mkdir .gnupg && chmod 700 .gnupg diff --git a/gardenlinux/container/build/install-cfssl.sh b/gardenlinux/container/build/install-cfssl.sh new file mode 100755 index 0000000..787acea --- /dev/null +++ b/gardenlinux/container/build/install-cfssl.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# This script downloads cfssl binaries and moves them to /usr/bin/ (in PATH). +# With cfssl installed, our central build can just refer to the existing +# binaries instead of performing a fresh build every time. + +cfssl_version="1.5.0" + +cfssl_files=( \ + cfssl-bundle \ + cfssl-certinfo \ + cfssl-newkey \ + cfssl-scan \ + cfssljson \ + cfssl \ + mkbundle \ + multirootca \ +) + +for file in "${cfssl_files[@]}"; do + outname="/usr/bin/${file}" + url="https://github.com/cloudflare/cfssl/releases/download/v${cfssl_version}/${file}_${cfssl_version}_linux_amd64" + wget --no-verbose -O "${outname}" "${url}" + chmod +x "${outname}" +done \ No newline at end of file diff --git a/gardenlinux/container/integration-test/.gitignore b/gardenlinux/container/integration-test/.gitignore new file mode 100644 index 0000000..eb06dc3 --- /dev/null +++ b/gardenlinux/container/integration-test/.gitignore @@ -0,0 +1 @@ +cloud.google.gpg diff --git a/gardenlinux/container/integration-test/Dockerfile b/gardenlinux/container/integration-test/Dockerfile new file mode 100644 index 0000000..e3c42d4 --- /dev/null +++ b/gardenlinux/container/integration-test/Dockerfile @@ -0,0 +1,42 @@ +ARG VERSION +FROM gardenlinux/base-test:${VERSION} +ENV SHELL /bin/bash +COPY checksums.sha256 /gardenlinux/tests/checksums.sha256 +COPY cloud.google.gpg /usr/share/keyrings/cloud.google.gpg + +#Virtual Env Created in base-test +ENV VIRTUAL_ENV="/opt/python-test-env/.venv" +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +RUN : "Install AWS requirements" \ + && pip install awscliv2 \ + && awsv2 --install +RUN : "Install GCP requirements" \ + && echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list \ + && apt-get update \ + && apt-get install -y google-cloud-sdk +RUN : "Azure and OpenStack cli already installed in gardenlinux/base-test image" \ + : "Install AliCloud specific requirements" \ + && arch="$(uname -m)" \ + && [ $arch = "x86_64" ] && aliyun_arch=amd64 || aliyun_arch=arm64 \ + && [ $arch = "x86_64" ] && ossutil_arch=64 || ossutil_arch=arm64 \ + && aliyun_ver="3.0.150" \ + && ossutil_ver="1.7.6" \ + && curl -sL -o /aliyun-cli-linux-${aliyun_ver}-${aliyun_arch}.tgz https://github.com/aliyun/aliyun-cli/releases/download/v${aliyun_ver}/aliyun-cli-linux-${aliyun_ver}-${aliyun_arch}.tgz \ + && (cd /usr/local/bin ; tar xf /aliyun-cli-linux-${aliyun_ver}-${aliyun_arch}.tgz) \ + && rm /aliyun-cli-linux-${aliyun_ver}-${aliyun_arch}.tgz \ + && curl -sL -o /usr/local/bin/ossutil https://gosspublic.alicdn.com/ossutil/${ossutil_ver}/ossutil${ossutil_arch}?spm=a2c63.p38356.a3.3.44692454KkczI0 \ + && ln -s /usr/local/bin/ossutil /ossutil${ossutil_arch}?spm=a2c63.p38356.a3.3.44692454KkczI0 \ + && chmod 755 /usr/local/bin/ossutil \ + && apt-get clean && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /root/.aws /root/.ssh /config +RUN : "Install firecracker specific binaries" \ + && arch="$(uname -m)" \ + && firecracker_ver="v1.1.4" \ + && release_url="https://github.com/firecracker-microvm/firecracker/releases" \ + && echo ${release_url}/download/${firecracker_ver}/firecracker-${firecracker_ver}-${arch}.tgz \ + && wget ${release_url}/download/${firecracker_ver}/firecracker-${firecracker_ver}-${arch}.tgz \ + && tar -xzv --no-same-owner -f firecracker-${firecracker_ver}-${arch}.tgz \ + && ln -s /release-${firecracker_ver}-${arch}/firecracker-${firecracker_ver}-${arch} /bin/firecracker + +WORKDIR /gardenlinux/tests diff --git a/gardenlinux/container/integration-test/cloud.google.gpg.base64 b/gardenlinux/container/integration-test/cloud.google.gpg.base64 new file mode 100644 index 0000000..d53c272 --- /dev/null +++ b/gardenlinux/container/integration-test/cloud.google.gpg.base64 @@ -0,0 +1,22 @@ +xsBNBGKItdQBCADWmKTNZEYWgXy73FvKFY5fRro4tGNa4Be4TZW3wZpct9Cj8EjykU7S9EPoJ3Ed +KpxFltHRu7QbDi6LWSNA4XxwnudQrYGxnxx6Ru1KBHFxHhLfWsvFcGMwit/znpxtIt9UzqCm2YTE +W5NUnzQ4rXYqVQK2FLG4weYJ5bKwkY+ZsnRJpzxdHGJ0pBiqwkMT8bfQdJymUBown+SeuQ2HEqfj +VMsIRe0dweD2PHWeWo9fTXsz1Q5abiGckyOVyoN9//DgSvLUocUcZsrWvYPaN+o8lXTO3GYFGNVs +x069rxarkeCjOpiQOWrQmywXISQudcusSgmmgfsRZYW7FDBy5MQrABEBAAHNUVJhcHR1cmUgQXV0 +b21hdGljIFNpZ25pbmcgS2V5IChjbG91ZC1yYXB0dXJlLXNpZ25pbmcta2V5LTIwMjItMDMtMDct +MDhfMDFfMDEucHViKcLAYgQTAQgAFgUCYoi11AkQtT3IDRPt7wUCGwMCGQEAAMGoCAB8QBNIIN3Q +2D3aahrfkb6axd55zOwR0tnriuJRoPHoNuorOpCv9aWMMvQACNWkxsvJxEF8OUbzhSYjAR534RDi +gjTetjK2i2wKLz/kJjZbuF4ZXMynCm40eVm1XZqU63U9XR2RxmXppyNpMqQO9LrzGEnNJuh23ica +ZY6no12axymxcle/+SCmda8oDAfa0iyA2iyg/eU05buZv54MC6RB13QtS+8vOrKDGr7RYp/VYvQz +YWm+ck6DvlaVX6VB51BkLl23SQknyZIJBVPm8ttU65EyrrgG1jLLHFXDUqJ/RpNKq+PCzWiyt4uy +3AfXK89RczLu3uxiD0CQI0T31u/IzsBNBGKItdQBCADIMMJdRcg0Phv7+CrZz3xRE8Fbz8AN+YCL +igQeH0B9lijxkjAFr+thB0IrOu7ruwNY+mvdP6dAewUur+pJaIjEe+4s8JBEFb4BxJfBBPuEbGSx +bi4OPEJuwT53TMJMEs7+gIxCCmwioTggTBp6JzDsT/cdBeyWCusCQwDWpqoYCoUWJLrUQ6dOlI7s +6p+iIUNIamtyBCwb4izs27HdEpX8gvO9rEdtcb7399HyO3oD4gHgcuFiuZTpvWHdn9WYwPGM6npJ +NG7crtLnctTR0cP9KutSPNzpySeAniHx8L9ebdD9tNPCWC+OtOcGRrcBeEznkYh1C4kzdP1ORm5u +pnknABEBAAHCwF8EGAEIABMFAmKItdQJELU9yA0T7e8FAhsMAABJmAgAhRPk/dFj71bU/UTXrkEk +ZZzE9JzUgan/ttyRrV6QbFZABByf4pYjBj+yLKw3280//JWurKox2uzEq1hdXPedRHICRuh1Fjd0 +0otaQ+wGF3kY74zlWivB6Wp6tnL9STQ1oVYBUv7HhSHoJ5shELyedxxHxurUgFAD+pbFXIiK8cnA +HfXTJMcrmPpC+YWEC/DeqIyEcNPkzRhtRSuERXcq1n+KJvMUAKMD/tezwvujzBaaSWapmdnGmtRj +jL7IxUeGamVWOwLQbUr+34MwzdeJdcL8fav5LA8Uk0ulyeXdwiAK8FKQsixI+xZvz7HUs8ln4pZw +Gw/TpvO9cMkHogtgzQ== diff --git a/gardenlinux/container/needslim b/gardenlinux/container/needslim new file mode 100755 index 0000000..1550e67 --- /dev/null +++ b/gardenlinux/container/needslim @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +VERSION="$(../bin/garden-version)" +gardenlinux_build_cre=${GARDENLINUX_BUILD_CRE:-"sudo podman"} + +if [ "$(${gardenlinux_build_cre} image ls gardenlinux/slim --format \"{{.Repository}}:{{.Tag}}\")" == "" ]; then + echo "WARNING: There is no gardenlinux/slim on this box. Since this is the builder script, it will pull no binary docker images from public repos." + echo + echo "Since gardenlinux/slim is needed for almost all images we pull debian/testing-slim and rename it to gardenlinux/slim so we avoid the Chicken-and-Egg problem temporarily" + echo + echo "Please run 'make slim' afterwards" + echo + ${gardenlinux_build_cre} pull debian:testing-slim + ${gardenlinux_build_cre} tag debian:testing-slim gardenlinux/slim + ${gardenlinux_build_cre} tag debian:testing-slim gardenlinux/slim:$VERSION + ${gardenlinux_build_cre} tag debian:testing-slim gardenlinux/slim:latest +else + if [ "$(${gardenlinux_build_cre} image ls gardenlinux/slim:latest --format \"{{.ID}}\")" == \ + "$(${gardenlinux_build_cre} image ls debian:testing-slim --format \"{{.ID}}\")" ]; then + echo "WARNING: You are still using a debian:testing-slim as a temporary replacement of gardenlinux/slim:latest!" + echo + echo "Please run 'make slim' to fix" + elif [ "$(${gardenlinux_build_cre} image ls gardenlinux/slim:$VERSION --format \"{{.ID}}\")" == \ + "$(${gardenlinux_build_cre} image ls debian:testing-slim --format \"{{.ID}}\")" ]; then + echo "WARNING: You are still using a debian:testing-slim as a temporary replacement of gardenlinux/slim:$VERSION!" + echo + echo "Please run 'make slim' to fix" + fi +fi + diff --git a/gardenlinux/distroless/libc/base b/gardenlinux/distroless/libc/base new file mode 100644 index 0000000..5d3a370 --- /dev/null +++ b/gardenlinux/distroless/libc/base @@ -0,0 +1 @@ +container diff --git a/gardenlinux/distroless/libc/dpkg_include b/gardenlinux/distroless/libc/dpkg_include new file mode 100644 index 0000000..6048c15 --- /dev/null +++ b/gardenlinux/distroless/libc/dpkg_include @@ -0,0 +1 @@ +libc6 diff --git a/gardenlinux/distroless/libc/include b/gardenlinux/distroless/libc/include new file mode 100644 index 0000000..3a44535 --- /dev/null +++ b/gardenlinux/distroless/libc/include @@ -0,0 +1 @@ +usr/share/zoneinfo/.* diff --git a/gardenlinux/distroless/libc/mode b/gardenlinux/distroless/libc/mode new file mode 100644 index 0000000..1ca9c45 --- /dev/null +++ b/gardenlinux/distroless/libc/mode @@ -0,0 +1 @@ +dpkg-dependencies diff --git a/gardenlinux/distroless/libc/target b/gardenlinux/distroless/libc/target new file mode 100644 index 0000000..5d3a370 --- /dev/null +++ b/gardenlinux/distroless/libc/target @@ -0,0 +1 @@ +container diff --git a/gardenlinux/distroless/sapmachine/base b/gardenlinux/distroless/sapmachine/base new file mode 100644 index 0000000..b3559c6 --- /dev/null +++ b/gardenlinux/distroless/sapmachine/base @@ -0,0 +1 @@ +container_curl diff --git a/gardenlinux/distroless/sapmachine/mode b/gardenlinux/distroless/sapmachine/mode new file mode 100644 index 0000000..454537e --- /dev/null +++ b/gardenlinux/distroless/sapmachine/mode @@ -0,0 +1 @@ +ldd-dependencies diff --git a/gardenlinux/distroless/sapmachine/target b/gardenlinux/distroless/sapmachine/target new file mode 100644 index 0000000..aa36d5a --- /dev/null +++ b/gardenlinux/distroless/sapmachine/target @@ -0,0 +1 @@ +container-sapmachine diff --git a/gardenlinux/features/_boot/exec.late b/gardenlinux/features/_boot/exec.late new file mode 100755 index 0000000..d7f4122 --- /dev/null +++ b/gardenlinux/features/_boot/exec.late @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +set -euo pipefail + +update-kernel-cmdline + +mkdir -p /boot/efi/Default + +for kernel in /boot/vmlinuz-*; do + unshare --mount bash -c 'mount -t tmpfs none /sys && mount --bind /usr/bin/false /usr/bin/systemd-detect-virt && "$@"' \ + DRACUT_COMPRESS_XZ="$(command -v xz)" dracut \ + --no-hostonly \ + --force \ + --kver "${kernel#*-}" \ + --modules "bash dash systemd systemd-initrd kernel-modules kernel-modules-extra terminfo udev-rules dracut-systemd base fs-lib shutdown" \ + --reproducible \ + "/boot/initrd.img-${kernel#*-}" + + SYSTEMD_ESP_PATH=/boot/efi kernel-install add "${kernel#*-}" "${kernel}" +done + +sed 's/boot\/efi\///' -i /boot/efi/loader/entries/*.conf + +SYSTEMD_ESP_PATH=/boot/efi bootctl --no-variables install + +mkdir -p /boot/efi/syslinux + +# syslinux +if [ -f "/usr/bin/syslinux" ]; then + mkdir -p /boot/efi/syslinux + cp /usr/lib/syslinux/modules/bios/menu.c32 /boot/efi/syslinux/ + cp /usr/lib/syslinux/modules/bios/libutil.c32 /boot/efi/syslinux/ + + update-syslinux +fi diff --git a/gardenlinux/features/_boot/file.exclude b/gardenlinux/features/_boot/file.exclude new file mode 100644 index 0000000..2e0f9df --- /dev/null +++ b/gardenlinux/features/_boot/file.exclude @@ -0,0 +1 @@ +/boot/efi/loader/random-seed diff --git a/gardenlinux/features/_boot/file.include/usr/local/sbin/update-kernel-cmdline b/gardenlinux/features/_boot/file.include/usr/local/sbin/update-kernel-cmdline new file mode 100755 index 0000000..1c28489 --- /dev/null +++ b/gardenlinux/features/_boot/file.include/usr/local/sbin/update-kernel-cmdline @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -uoeE pipefail + +configDir="/etc/kernel/cmdline.d" + +# load extra stuff +for i in "${configDir}"/*-*.cfg; do + [ -e "$i" ] || continue + source $i +done + +echo "root=${DEVICE} ${CMDLINE_LINUX}" > /etc/kernel/cmdline.new +mv /etc/kernel/cmdline.new /etc/kernel/cmdline diff --git a/gardenlinux/features/_boot/file.include/usr/local/sbin/update-syslinux b/gardenlinux/features/_boot/file.include/usr/local/sbin/update-syslinux new file mode 100755 index 0000000..cb51948 --- /dev/null +++ b/gardenlinux/features/_boot/file.include/usr/local/sbin/update-syslinux @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +set -uoeE pipefail + +bootDir="/boot/efi" +kernelDir="${bootDir}/Default" +configDir="/etc/kernel/cmdline.d" +configFile="${bootDir}/syslinux/syslinux.cfg" + +check_version() { + local v=$1 + if [ ! -d "$kernelDir/$v" ]; then + return 1 + fi + if [ ! -f "$kernelDir/$v/linux" ]; then + return 1 + fi + if [ ! -f "$kernelDir/$v/initrd.img-$v" ]; then + return 1 + fi + return 0 +} + +if ! which syslinux &> /dev/null; then + exit 0 +fi + +#TODO: detect if anything other than bootDir/Default is used +if [ ! -d "$kernelDir" ]; then + exit 0 +fi + +# load extras +for i in "${configDir}"/*-*.cfg; do + [ -e "$i" ] || continue + source "$i" +done + +versions=() +# kernel / initrd +for kernel in /boot/vmlinuz-*; do + if check_version "${kernel#*-}"; then + versions+=("${kernel#*-}") + fi +done + +if [ "${#versions[@]}" == "0" ]; then + echo "no valid kernels found" 1>&2 + exit 1 +fi + +readarray -t vSorted < <(printf '%s\n' "${versions[@]}" | sort -rV) +{ + echo "UI menu.c32" + echo "PROMPT 0" + echo + echo "MENU TITLE Gardenlinux" + echo "TIMEOUT $TIMEOUT" + echo "DEFAULT ${vSorted[0]}" + echo + for v in "${vSorted[@]}"; do + echo "LABEL Linux $v" + echo " LINUX ../Default/$v/linux" + echo " APPEND root=${DEVICE} ${CMDLINE_LINUX}" + echo " INITRD ../Default/${v}/initrd.img-$v" + echo + done +} > "${configFile}.new" + +mv "${configFile}.new" "${configFile}" diff --git a/gardenlinux/features/_boot/info.yaml b/gardenlinux/features/_boot/info.yaml new file mode 100644 index 0000000..cf19f80 --- /dev/null +++ b/gardenlinux/features/_boot/info.yaml @@ -0,0 +1,2 @@ +description: "UEFI (systemd-boot) and legacy BIOS (syslinux) bootloader" +type: flag diff --git a/gardenlinux/features/_boot/pkg.include b/gardenlinux/features/_boot/pkg.include new file mode 100644 index 0000000..0a05cf3 --- /dev/null +++ b/gardenlinux/features/_boot/pkg.include @@ -0,0 +1,4 @@ +dracut +systemd-boot +$(if [ $arch = amd64 ]; then echo syslinux; fi) +$(if [ $arch = amd64 ]; then echo syslinux-common; fi) diff --git a/gardenlinux/features/_curl/info.yaml b/gardenlinux/features/_curl/info.yaml new file mode 100644 index 0000000..3a4d8b9 --- /dev/null +++ b/gardenlinux/features/_curl/info.yaml @@ -0,0 +1,2 @@ +description: "install curl" +type: flag diff --git a/gardenlinux/features/_curl/pkg.include b/gardenlinux/features/_curl/pkg.include new file mode 100644 index 0000000..96b64c5 --- /dev/null +++ b/gardenlinux/features/_curl/pkg.include @@ -0,0 +1,2 @@ +ca-certificates +curl diff --git a/gardenlinux/features/_dev/README.md b/gardenlinux/features/_dev/README.md new file mode 100644 index 0000000..ec21fd0 --- /dev/null +++ b/gardenlinux/features/_dev/README.md @@ -0,0 +1,21 @@ +## Feature: _dev +### Description + +This feature flag adds dev tools to the Garden Linux artifact. + + +### Features +**Warning**: Never use this feature on production! + +This feature adds debug and development tools to the Garden Linux artifact and enables tty0 autologin. + +### Unit testing +This feature does not support unit tests. + +### Meta +||| +|---|---| +|type|flag| +|artifact|None| +|included_features|None| +|excluded_features|None| diff --git a/gardenlinux/features/_dev/exec.late b/gardenlinux/features/_dev/exec.late new file mode 100755 index 0000000..7c3e564 --- /dev/null +++ b/gardenlinux/features/_dev/exec.late @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +useradd --user-group --groups wheel --create-home dev +id dev + +mkdir /home/dev/.ssh +chmod -R 700 /home/dev/.ssh +chown -R dev:dev /home/dev/.ssh +find /home/dev -printf '%M %u:%g %p\n' diff --git a/gardenlinux/features/_dev/file.include/etc/systemd/system/emergency.service.d/sulogin.conf b/gardenlinux/features/_dev/file.include/etc/systemd/system/emergency.service.d/sulogin.conf new file mode 100644 index 0000000..9bdc4f2 --- /dev/null +++ b/gardenlinux/features/_dev/file.include/etc/systemd/system/emergency.service.d/sulogin.conf @@ -0,0 +1,2 @@ +[Service] +Environment=SYSTEMD_SULOGIN_FORCE=1 diff --git a/gardenlinux/features/_dev/file.include/etc/systemd/system/getty@tty1.service.d/autologin.conf b/gardenlinux/features/_dev/file.include/etc/systemd/system/getty@tty1.service.d/autologin.conf new file mode 100644 index 0000000..a38a41e --- /dev/null +++ b/gardenlinux/features/_dev/file.include/etc/systemd/system/getty@tty1.service.d/autologin.conf @@ -0,0 +1,4 @@ +[Service] +ExecStart= +ExecStart=-/sbin/agetty --autologin root -o '-p -f -- \\u' --noclear %I $TERM + diff --git a/gardenlinux/features/_dev/file.include/etc/systemd/system/rescue.service.d/sulogin.conf b/gardenlinux/features/_dev/file.include/etc/systemd/system/rescue.service.d/sulogin.conf new file mode 100644 index 0000000..9bdc4f2 --- /dev/null +++ b/gardenlinux/features/_dev/file.include/etc/systemd/system/rescue.service.d/sulogin.conf @@ -0,0 +1,2 @@ +[Service] +Environment=SYSTEMD_SULOGIN_FORCE=1 diff --git a/gardenlinux/features/_dev/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf b/gardenlinux/features/_dev/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf new file mode 100644 index 0000000..4897dfe --- /dev/null +++ b/gardenlinux/features/_dev/file.include/etc/systemd/system/serial-getty@.service.d/autologin.conf @@ -0,0 +1,4 @@ +[Service] +ExecStart= +ExecStart=-/sbin/agetty --autologin root -o '-p -f -- \\u' --keep-baud 115200,38400,9600 %I $TERM + diff --git a/gardenlinux/features/_dev/info.yaml b/gardenlinux/features/_dev/info.yaml new file mode 100644 index 0000000..8fbbe49 --- /dev/null +++ b/gardenlinux/features/_dev/info.yaml @@ -0,0 +1,2 @@ +description: "developer features are enabled (e.g vim, autologin)" +type: flag diff --git a/gardenlinux/features/_dev/pkg.include b/gardenlinux/features/_dev/pkg.include new file mode 100644 index 0000000..f027e0d --- /dev/null +++ b/gardenlinux/features/_dev/pkg.include @@ -0,0 +1 @@ +vim diff --git a/gardenlinux/features/_dev/test/autologin.disable b/gardenlinux/features/_dev/test/autologin.disable new file mode 100644 index 0000000..e69de29 diff --git a/gardenlinux/features/_dev/test/groups.chroot b/gardenlinux/features/_dev/test/groups.chroot new file mode 120000 index 0000000..efe85b8 --- /dev/null +++ b/gardenlinux/features/_dev/test/groups.chroot @@ -0,0 +1 @@ +../../base/test/groups.chroot \ No newline at end of file diff --git a/gardenlinux/features/_dev/test/groups.d/groups.list b/gardenlinux/features/_dev/test/groups.d/groups.list new file mode 100644 index 0000000..cf84155 --- /dev/null +++ b/gardenlinux/features/_dev/test/groups.d/groups.list @@ -0,0 +1 @@ +wheel:dev diff --git a/gardenlinux/features/_dev/test/test_packages_musthave.py b/gardenlinux/features/_dev/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/_dev/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/_ignite/README.md b/gardenlinux/features/_ignite/README.md new file mode 100644 index 0000000..21cb0ce --- /dev/null +++ b/gardenlinux/features/_ignite/README.md @@ -0,0 +1,8 @@ +## Feature: _ignite + + ignition is installed on the system. + +--- + + Type: flag +# diff --git a/gardenlinux/features/_ignite/file.include/etc/dracut.conf.d/30-ignition.conf b/gardenlinux/features/_ignite/file.include/etc/dracut.conf.d/30-ignition.conf new file mode 100644 index 0000000..a8af14b --- /dev/null +++ b/gardenlinux/features/_ignite/file.include/etc/dracut.conf.d/30-ignition.conf @@ -0,0 +1 @@ +add_dracutmodules+=" ignition ignition-extra " diff --git a/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-env-generator.sh b/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-env-generator.sh new file mode 100755 index 0000000..e734557 --- /dev/null +++ b/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-env-generator.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +GENERATOR_DIR=$1 + +for i in /etc/ignition-*.env; do + if [ ! -f "$i" ]; then + exit 0 + fi + f=$(basename $i) + p=$(echo "${GENERATOR_DIR}/${f%%.env}.service.d") + mkdir -p "$p" + { + echo "[Service]" + while read -r line; do + echo Environment=\"$line\" + done < "$i" + }> "$p/env.conf" +done + +exit 0 diff --git a/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-files.env b/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-files.env new file mode 100644 index 0000000..38f2a60 --- /dev/null +++ b/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/ignition-files.env @@ -0,0 +1 @@ +IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT=false diff --git a/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/module-setup.sh b/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/module-setup.sh new file mode 100755 index 0000000..f4eaa8f --- /dev/null +++ b/gardenlinux/features/_ignite/file.include/usr/lib/dracut/modules.d/30ignition-extra/module-setup.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +depends() { + echo "ignition" +} + +install() { + # ignition environment + inst_script "$moddir/ignition-env-generator.sh" $systemdutildir/system-generators/ignition-env-generator + inst_simple "$moddir/ignition-files.env" /etc/ignition-files.env +} diff --git a/gardenlinux/features/_ignite/info.yaml b/gardenlinux/features/_ignite/info.yaml new file mode 100644 index 0000000..1d399ca --- /dev/null +++ b/gardenlinux/features/_ignite/info.yaml @@ -0,0 +1,2 @@ +description: "ignition is installed on the system" +type: flag diff --git a/gardenlinux/features/_ignite/pkg.include b/gardenlinux/features/_ignite/pkg.include new file mode 100644 index 0000000..a05068c --- /dev/null +++ b/gardenlinux/features/_ignite/pkg.include @@ -0,0 +1,3 @@ +dracut-network +ignition +curl diff --git a/gardenlinux/features/_selinux/README.md b/gardenlinux/features/_selinux/README.md new file mode 100644 index 0000000..a584397 --- /dev/null +++ b/gardenlinux/features/_selinux/README.md @@ -0,0 +1,20 @@ +## Feature: _selinux +### Description + +This feature flag adds SELinux extended attributes to the Garden Linux artifact. + + +### Features +This feature adds support for SELinux attributes files on the target systems. + +### Unit testing +This feature checks if Linux Security Module (LSM) has been correctly set to selinux on the kernel command line +and if the required selinux policy rules have been installed. + +### Meta +||| +|---|---| +|type|flag| +|artifact|None| +|included_features|None| +|excluded_features|None| diff --git a/gardenlinux/features/_selinux/exec.post b/gardenlinux/features/_selinux/exec.post new file mode 100755 index 0000000..aeae6f7 --- /dev/null +++ b/gardenlinux/features/_selinux/exec.post @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +rootfs="$1" + +# loop mounting rootfs into itself serves two purposes: +# - hides any /proc mounts etc +# - tricks setfiles into not seeing it as / => not trying to log to audit system + +[ ! -e "$rootfs/loop" ] +mkdir "$rootfs/loop" +mount --bind "$rootfs" "$rootfs/loop" + +PATH="/native_bin:$PATH" chroot "$rootfs" setfiles -r /loop /etc/selinux/default/contexts/files/file_contexts /loop + +umount "$rootfs/loop" +rmdir "$rootfs/loop" diff --git a/gardenlinux/features/_selinux/file.include/etc/kernel/cmdline.d/90-lsm.cfg b/gardenlinux/features/_selinux/file.include/etc/kernel/cmdline.d/90-lsm.cfg new file mode 100644 index 0000000..6c14fa6 --- /dev/null +++ b/gardenlinux/features/_selinux/file.include/etc/kernel/cmdline.d/90-lsm.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="$CMDLINE_LINUX security=selinux" diff --git a/gardenlinux/features/_selinux/info.yaml b/gardenlinux/features/_selinux/info.yaml new file mode 100644 index 0000000..cb8c9a4 --- /dev/null +++ b/gardenlinux/features/_selinux/info.yaml @@ -0,0 +1,2 @@ +description: "use selinux attributes file system" +type: flag diff --git a/gardenlinux/features/_selinux/pkg.include b/gardenlinux/features/_selinux/pkg.include new file mode 100644 index 0000000..40fb6de --- /dev/null +++ b/gardenlinux/features/_selinux/pkg.include @@ -0,0 +1,3 @@ +selinux-basics +selinux-policy-default +gardenlinux-selinux-module diff --git a/gardenlinux/features/_selinux/test/test_kernel_cmdline.py b/gardenlinux/features/_selinux/test/test_kernel_cmdline.py new file mode 100644 index 0000000..252b36c --- /dev/null +++ b/gardenlinux/features/_selinux/test/test_kernel_cmdline.py @@ -0,0 +1,14 @@ +import pytest +from helper.tests.file_content import file_content + + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/kernel/cmdline", "security=selinux"), + ] +) + + +def test_kernel_cmdline(client, file, args): + file_content(client, file, args, only_line_match=True) diff --git a/gardenlinux/features/_selinux/test/test_packages_musthave.py b/gardenlinux/features/_selinux/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/_selinux/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/_slim/README.md b/gardenlinux/features/_slim/README.md new file mode 100644 index 0000000..60b9a8c --- /dev/null +++ b/gardenlinux/features/_slim/README.md @@ -0,0 +1,19 @@ +## Feature: _slim +### Description + +This feature flag slimifies the whole Garden Linux artifact. + + +### Features +A slimified Garden Linux artifact is created by removing all man pages, removing unnecessary files. + +### Unit testing +This feature does not support unit tests. + +### Meta +||| +|---|---| +|type|flag| +|artifact|None| +|included_features|server| +|excluded_features|None| diff --git a/gardenlinux/features/_slim/file.exclude b/gardenlinux/features/_slim/file.exclude new file mode 100644 index 0000000..a56bb49 --- /dev/null +++ b/gardenlinux/features/_slim/file.exclude @@ -0,0 +1,6 @@ +/usr/share/doc +/usr/share/man +/usr/share/locale +/usr/share/groff +/usr/share/lintian +/usr/share/linda diff --git a/gardenlinux/features/_slim/info.yaml b/gardenlinux/features/_slim/info.yaml new file mode 100644 index 0000000..bc04ef8 --- /dev/null +++ b/gardenlinux/features/_slim/info.yaml @@ -0,0 +1,2 @@ +description: "whole setup is slimed (e.g. no man pages)" +type: flag diff --git a/gardenlinux/features/aws/README.md b/gardenlinux/features/aws/README.md new file mode 100644 index 0000000..b4da1cc --- /dev/null +++ b/gardenlinux/features/aws/README.md @@ -0,0 +1,22 @@ +## Feature: aws +### Description + +This platform feature creates an artifact for Amazon AWS. + + +### Features +This feature creates an Amazon AWS compatible image artifact as an `.raw` file. + +To be platform complaint smaller adjustments like defining the platform related clocksource config, networking config etc. are done. +The artifact includes `cloud-init` and `amazon-ec2-utils` for orchestrating the image. + +### Unit testing +This platform feature supports unit testing and is based on the `aws` fixture to validate the applied changes according its feature configuration. + +### Meta +||| +|---|---| +|type|platform| +|artifact|`.raw`| +|included_features|cloud| +|excluded_features|None| \ No newline at end of file diff --git a/gardenlinux/features/aws/exec.config b/gardenlinux/features/aws/exec.config new file mode 100755 index 0000000..03002ed --- /dev/null +++ b/gardenlinux/features/aws/exec.config @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# growpart is done in initramfs, growroot by systemd +mv /etc/cloud/cloud.cfg /etc/cloud/cloud.cfg.bak +cat /etc/cloud/cloud.cfg.bak | grep -v "^ - growpart$" | grep -v "^ - resizefs$" | grep -v "^ - ntp$" >/etc/cloud/cloud.cfg +rm /etc/cloud/cloud.cfg.bak + +systemctl enable aws-clocksource.service diff --git a/gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg b/gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg new file mode 100644 index 0000000..b1504cd --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg @@ -0,0 +1,13 @@ +apt_preserve_sources_list: true +manage_etc_hosts: true +system_info: + default_user: + name: admin + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + lock_passwd: True + gecos: Debian + groups: [adm, audio, cdrom, dialout, dip, floppy, netdev, plugdev, sudo, video] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + diff --git a/gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg b/gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg new file mode 100644 index 0000000..f144451 --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg @@ -0,0 +1 @@ +network: {config: disabled} diff --git a/gardenlinux/features/aws/file.include/etc/dracut.conf.d/90-xen-blkfront-driver.conf b/gardenlinux/features/aws/file.include/etc/dracut.conf.d/90-xen-blkfront-driver.conf new file mode 100644 index 0000000..1e1402a --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/dracut.conf.d/90-xen-blkfront-driver.conf @@ -0,0 +1 @@ +add_drivers+=" xen-blkfront " diff --git a/gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/10-console.cfg b/gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/10-console.cfg new file mode 100644 index 0000000..a40d0b0 --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/10-console.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="console=tty0 console=ttyS0 $CMDLINE_LINUX" diff --git a/gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/70-nvme.cfg b/gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/70-nvme.cfg new file mode 100644 index 0000000..edcbdb5 --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/kernel/cmdline.d/70-nvme.cfg @@ -0,0 +1,3 @@ +# Recommended to set by AWS support + +CMDLINE_LINUX="$CMDLINE_LINUX nvme_core.io_timeout=4294967295" diff --git a/gardenlinux/features/aws/file.include/etc/systemd/resolved.conf.d/00-gardenlinux-aws.conf b/gardenlinux/features/aws/file.include/etc/systemd/resolved.conf.d/00-gardenlinux-aws.conf new file mode 100644 index 0000000..582e051 --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/systemd/resolved.conf.d/00-gardenlinux-aws.conf @@ -0,0 +1,3 @@ +[Resolve] +# AWS DNS does not support DNSSEC +DNSSEC=false diff --git a/gardenlinux/features/aws/file.include/etc/systemd/system/aws-clocksource.service b/gardenlinux/features/aws/file.include/etc/systemd/system/aws-clocksource.service new file mode 100644 index 0000000..9e06b51 --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/systemd/system/aws-clocksource.service @@ -0,0 +1,9 @@ +[Unit] +Description=AWS Clocksource Setup + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/clocksource-setup.sh + +[Install] +WantedBy=basic.target diff --git a/gardenlinux/features/aws/file.include/etc/systemd/timesyncd.conf.d/00-gardenlinux-aws.conf b/gardenlinux/features/aws/file.include/etc/systemd/timesyncd.conf.d/00-gardenlinux-aws.conf new file mode 100644 index 0000000..ba4ef4d --- /dev/null +++ b/gardenlinux/features/aws/file.include/etc/systemd/timesyncd.conf.d/00-gardenlinux-aws.conf @@ -0,0 +1,3 @@ +[Time] +# AWS has an own time server +NTP=169.254.169.123 diff --git a/gardenlinux/features/aws/file.include/usr/local/sbin/clocksource-setup.sh b/gardenlinux/features/aws/file.include/usr/local/sbin/clocksource-setup.sh new file mode 100755 index 0000000..6d9df5a --- /dev/null +++ b/gardenlinux/features/aws/file.include/usr/local/sbin/clocksource-setup.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if grep -q "clocksource=" /proc/cmdline; then + exit 0 +fi + +hypervisor=$(systemd-detect-virt) +available_clock_sources=$(cat /sys/devices/system/clocksource/clocksource0/available_clocksource) +case $hypervisor in +kvm|amazon) + if [[ "$available_clock_sources" =~ "kvm-clock" ]] ; then + echo "Detected hypervisor KVM/Amazon Nitro, setting clocksource kvm-clock" | systemd-cat -p info -t clocksource-setup + echo kvm-clock > /sys/devices/system/clocksource/clocksource0/current_clocksource + fi + ;; +xen) + if [[ "$available_clock_sources" =~ "tsc" ]] ; then + echo "Detected hypervisor XEN, setting clocksource tsc" | systemd-cat -p info -t clocksource-setup + echo tsc > /sys/devices/system/clocksource/clocksource0/current_clocksource + fi + ;; +esac + diff --git a/gardenlinux/features/aws/info.yaml b/gardenlinux/features/aws/info.yaml new file mode 100644 index 0000000..a65184b --- /dev/null +++ b/gardenlinux/features/aws/info.yaml @@ -0,0 +1,5 @@ +description: "aws platform (cloudinit)" +type: platform +features: + include: + - cloud diff --git a/gardenlinux/features/aws/pkg.include b/gardenlinux/features/aws/pkg.include new file mode 100644 index 0000000..27b7c11 --- /dev/null +++ b/gardenlinux/features/aws/pkg.include @@ -0,0 +1,5 @@ +python3-cffi-backend +python3-boto +cloud-init +dmidecode +amazon-ec2-utils diff --git a/gardenlinux/features/aws/test/test_packages_musthave.py b/gardenlinux/features/aws/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/aws/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/aws/test/test_users.py b/gardenlinux/features/aws/test/test_users.py new file mode 100644 index 0000000..6e4a66e --- /dev/null +++ b/gardenlinux/features/aws/test/test_users.py @@ -0,0 +1,6 @@ +from helper.tests.users import users + +additional_user="admin" + +def test_users(client, aws): + users(client=client, additional_user=additional_user, additional_sudo_users=[additional_user]) diff --git a/gardenlinux/features/azure/README.md b/gardenlinux/features/azure/README.md new file mode 100644 index 0000000..69b0326 --- /dev/null +++ b/gardenlinux/features/azure/README.md @@ -0,0 +1,22 @@ +## Feature: azure +### Description + +This platform feature creates an artifact for Microsoft Azure. + + +### Features +This feature creates a Microsoft Azure compatible image artifact as a `.vhd` file. + +To be platform complaint smaller adjustments like defining the platform related clocksource config, networking config etc. are done. +The artifact includes `cloud-init` for orchestrating the image. + +### Unit testing +This platform feature supports unit testing and is based on the `azure` fixture to validate the applied changes according its feature configuration. + +### Meta +||| +|---|---| +|type|platform| +|artifact|`.raw`, `.vhd`| +|included_features|cloud| +|excluded_features|None| \ No newline at end of file diff --git a/gardenlinux/features/azure/convert.vhd b/gardenlinux/features/azure/convert.vhd new file mode 100755 index 0000000..a11b71e --- /dev/null +++ b/gardenlinux/features/azure/convert.vhd @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +input="$1" +output="$2" + +if [[ "$(wc -c "$input" | cut -d " " -f 1)" -gt 4294967296 ]]; then + echo "image too large" + exit 1 +fi + +tmp="$(mktemp)" +cp --sparse=always "$input" "$tmp" +truncate -s 4GiB "$tmp" + +# fix GPT and image size mismatch +echo | sfdisk "$tmp" + +qemu-img convert -f raw -O vpc -o subformat=fixed,force_size "$tmp" "$output" + +rm "$tmp" diff --git a/gardenlinux/features/azure/exec.config b/gardenlinux/features/azure/exec.config new file mode 100755 index 0000000..e14ed0c --- /dev/null +++ b/gardenlinux/features/azure/exec.config @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# disable cloud-init network configuration +echo -e "network:\n config: disabled\n" >> /etc/cloud/cloud.cfg.d/00_azure.cfg \ No newline at end of file diff --git a/gardenlinux/features/azure/file.exclude b/gardenlinux/features/azure/file.exclude new file mode 100644 index 0000000..afc6f05 --- /dev/null +++ b/gardenlinux/features/azure/file.exclude @@ -0,0 +1,2 @@ +/etc/systemd/system/systemd-timesyncd.service.d/override.conf +/etc/modprobe.d/disabled_udf.conf diff --git a/gardenlinux/features/azure/file.include/etc/chrony/chrony.conf b/gardenlinux/features/azure/file.include/etc/chrony/chrony.conf new file mode 100644 index 0000000..8241454 --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/chrony/chrony.conf @@ -0,0 +1,50 @@ +# Welcome to the chrony configuration file. See chrony.conf(5) for more +# information about usable directives. + +# Include configuration files found in /etc/chrony/conf.d. +#confdir /etc/chrony/conf.d + +# Use Debian vendor zone. +#pool 2.debian.pool.ntp.org iburst + +# Use time sources from DHCP. +#sourcedir /run/chrony-dhcp + +# Use NTP sources found in /etc/chrony/sources.d. +#sourcedir /etc/chrony/sources.d + +# This directive specify the location of the file containing ID/key pairs for +# NTP authentication. +keyfile /etc/chrony/chrony.keys + +# This directive specify the file into which chronyd will store the rate +# information. +driftfile /var/lib/chrony/chrony.drift + +# Save NTS keys and cookies. +ntsdumpdir /var/lib/chrony + +# Uncomment the following line to turn logging on. +#log tracking measurements statistics + +# Log files location. +logdir /var/log/chrony + +# Stop bad estimates upsetting machine clock. +maxupdateskew 100.0 + +# This directive enables kernel synchronisation (every 11 minutes) of the +# real-time clock. Note that it can’t be used along with the 'rtcfile' directive. +rtcsync + +# Step the system clock instead of slewing it if the adjustment is larger than +# one second, but only in the first three clock updates. +makestep 1 3 + +# Get TAI-UTC offset and leap seconds from the system tz database. +# This directive must be commented out when using time sources serving +# leap-smeared time. +#leapsectz right/UTC + +# Azure provides a PTP Clock Source +refclock PHC /dev/ptp_hyperv poll 3 dpoll -2 offset 0 diff --git a/gardenlinux/features/azure/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg b/gardenlinux/features/azure/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg new file mode 100644 index 0000000..96614b9 --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg @@ -0,0 +1,13 @@ +apt_preserve_sources_list: true +manage_etc_hosts: true +system_info: + default_user: + name: azureuser + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + lock_passwd: True + gecos: Debian + groups: [adm, audio, cdrom, dialout, dip, floppy, netdev, plugdev, sudo, video] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + diff --git a/gardenlinux/features/azure/file.include/etc/kernel/cmdline.d/10-console.cfg b/gardenlinux/features/azure/file.include/etc/kernel/cmdline.d/10-console.cfg new file mode 100644 index 0000000..a40d0b0 --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/kernel/cmdline.d/10-console.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="console=tty0 console=ttyS0 $CMDLINE_LINUX" diff --git a/gardenlinux/features/azure/file.include/etc/systemd/99-azure-unmanaged-devices.network b/gardenlinux/features/azure/file.include/etc/systemd/99-azure-unmanaged-devices.network new file mode 100644 index 0000000..0aa3f7c --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/systemd/99-azure-unmanaged-devices.network @@ -0,0 +1,6 @@ +# Ignore SR-IOV interface on Azure, since it'll be transparently bonded +# to the synthetic interface +[Match] +Driver=mlx4_en mlx5_en mlx4_core mlx5_core +[Link] +Unmanaged=yes diff --git a/gardenlinux/features/azure/file.include/etc/udev/rules.d/60-hyperv-ptp.rules b/gardenlinux/features/azure/file.include/etc/udev/rules.d/60-hyperv-ptp.rules new file mode 100644 index 0000000..d2a367c --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/udev/rules.d/60-hyperv-ptp.rules @@ -0,0 +1 @@ +SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK += "ptp_hyperv" diff --git a/gardenlinux/features/azure/file.include/etc/udev/rules.d/66-azure-storage.rules b/gardenlinux/features/azure/file.include/etc/udev/rules.d/66-azure-storage.rules new file mode 100644 index 0000000..302c822 --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/udev/rules.d/66-azure-storage.rules @@ -0,0 +1,36 @@ +# Azure specific rules for storage devices +# Taken from https://github.com/Azure/WALinuxAgent/blob/master/config/66-azure-storage.rules + +ACTION!="add|change", GOTO="azure_storage_end" +SUBSYSTEM!="block", GOTO="azure_storage_end" +ATTRS{ID_VENDOR}!="Msft", GOTO="azure_storage_end" +ATTRS{ID_MODEL}!="Virtual_Disk", GOTO="azure_storage_end" + +# Match the known ID parts for root and resource disks. +ATTRS{device_id}=="?00000000-0000-*", ENV{fabric_name}="root", GOTO="azure_storage_names" +ATTRS{device_id}=="?00000000-0001-*", ENV{fabric_name}="resource", GOTO="azure_storage_names" + +# Gen2 disk. +ATTRS{device_id}=="{f8b3781a-1e82-4818-a1c3-63d806ec15bb}", ENV{fabric_scsi_controller}="scsi0", GOTO="azure_datadisk" +# Create symlinks for data disks attached. +ATTRS{device_id}=="{f8b3781b-1e82-4818-a1c3-63d806ec15bb}", ENV{fabric_scsi_controller}="scsi1", GOTO="azure_datadisk" +ATTRS{device_id}=="{f8b3781c-1e82-4818-a1c3-63d806ec15bb}", ENV{fabric_scsi_controller}="scsi2", GOTO="azure_datadisk" +ATTRS{device_id}=="{f8b3781d-1e82-4818-a1c3-63d806ec15bb}", ENV{fabric_scsi_controller}="scsi3", GOTO="azure_datadisk" +GOTO="azure_storage_end" + +# Parse out the fabric n ame based off of scsi indicators. +LABEL="azure_datadisk" +ENV{DEVTYPE}=="partition", PROGRAM="/bin/sh -c 'readlink /sys/class/block/%k/../device|cut -d: -f4'", ENV{fabric_name}="$env{fabric_scsi_controller}/lun$result" +ENV{DEVTYPE}=="disk", PROGRAM="/bin/sh -c 'readlink /sys/class/block/%k/device|cut -d: -f4'", ENV{fabric_name}="$env{fabric_scsi_controller}/lun$result" + +ENV{fabric_name}=="scsi0/lun0", ENV{fabric_name}="root" +ENV{fabric_name}=="scsi0/lun1", ENV{fabric_name}="resource" +# Don't create a symlink for the cd-rom. +ENV{fabric_name}=="scsi0/lun2", GOTO="azure_storage_end" + +# Create the symlinks. +LABEL="azure_storage_names" +ENV{DEVTYPE}=="disk", SYMLINK+="disk/azure/$env{fabric_name}" +ENV{DEVTYPE}=="partition", SYMLINK+="disk/azure/$env{fabric_name}-part%n" + +LABEL="azure_storage_end" diff --git a/gardenlinux/features/azure/file.include/etc/udev/rules.d/99-azure-product-uuid.rules b/gardenlinux/features/azure/file.include/etc/udev/rules.d/99-azure-product-uuid.rules new file mode 100644 index 0000000..71ac538 --- /dev/null +++ b/gardenlinux/features/azure/file.include/etc/udev/rules.d/99-azure-product-uuid.rules @@ -0,0 +1,11 @@ +# Make the VM product uuid world readable on Azure virtual machines +# Taken from https://github.com/Azure/WALinuxAgent/blob/master/config/99-azure-product-uuid.rules + +SUBSYSTEM!="dmi", GOTO="product_uuid-exit" +ATTR{sys_vendor}!="Microsoft Corporation", GOTO="product_uuid-exit" +ATTR{product_name}!="Virtual Machine", GOTO="product_uuid-exit" +TEST!="/sys/devices/virtual/dmi/id/product_uuid", GOTO="product_uuid-exit" + +RUN+="/bin/chmod 0444 /sys/devices/virtual/dmi/id/product_uuid" + +LABEL="product_uuid-exit" diff --git a/gardenlinux/features/azure/info.yaml b/gardenlinux/features/azure/info.yaml new file mode 100644 index 0000000..589fc8a --- /dev/null +++ b/gardenlinux/features/azure/info.yaml @@ -0,0 +1,5 @@ +description: "azure platform (waagent)" +type: platform +features: + include: + - cloud diff --git a/gardenlinux/features/azure/pkg.exclude b/gardenlinux/features/azure/pkg.exclude new file mode 100644 index 0000000..97ca0d2 --- /dev/null +++ b/gardenlinux/features/azure/pkg.exclude @@ -0,0 +1 @@ +systemd-timesyncd diff --git a/gardenlinux/features/azure/pkg.include b/gardenlinux/features/azure/pkg.include new file mode 100644 index 0000000..fe680ae --- /dev/null +++ b/gardenlinux/features/azure/pkg.include @@ -0,0 +1,3 @@ +chrony +cloud-init +python3-cffi-backend \ No newline at end of file diff --git a/gardenlinux/features/azure/test/test_azure_times.py b/gardenlinux/features/azure/test/test_azure_times.py new file mode 100644 index 0000000..359c986 --- /dev/null +++ b/gardenlinux/features/azure/test/test_azure_times.py @@ -0,0 +1,29 @@ +import pytest +from helper.utils import get_architecture +from helper.sshclient import RemoteClient + + +def test_startup_time(client, non_chroot, non_kvm): + """ Test for startup time """ + tolerated_kernel_time = 30 + tolerated_userspace_time = 60 + (exit_code, output, error) = client.execute_command("systemd-analyze") + assert exit_code == 0, f"no {error=} expected" + lines = output.splitlines() + items = lines[0].split(" ") + time_initrd = 0 + for i, v in enumerate(items): + if v == "(kernel)": + time_kernel = items[i-1] + if v == "(initrd)": + time_initrd = items[i-1] + if v == "(userspace)": + time_userspace = items[i-1] + if len(time_kernel) >2 and time_kernel[-2:] == "ms": + time_kernel = str(float(time_kernel[:-2]) / 1000.0) + "s" + if len(time_initrd) >2 and time_initrd[-2:] == "ms": + time_initrd = str(float(time_initrd[:-2]) / 1000.0) + "s" + tf_kernel = float(time_kernel[:-1]) + float(time_initrd[:-1]) + tf_userspace = float(time_userspace[:-1]) + assert tf_kernel < tolerated_kernel_time, f"startup time in kernel space too long: {tf_kernel} seconds = but only {tolerated_kernel_time} tolerated." + assert tf_userspace < tolerated_userspace_time, f"startup time in user space too long: {tf_userspace}seconds but only {tolerated_userspace_time} tolerated." diff --git a/gardenlinux/features/azure/test/test_packages_musthave.py b/gardenlinux/features/azure/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/azure/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/azure/test/test_users.py b/gardenlinux/features/azure/test/test_users.py new file mode 100644 index 0000000..58b02a5 --- /dev/null +++ b/gardenlinux/features/azure/test/test_users.py @@ -0,0 +1,6 @@ +from helper.tests.users import users + +additional_user="azureuser" + +def test_users(client, azure): + users(client=client, additional_user=additional_user, additional_sudo_users=[additional_user]) diff --git a/gardenlinux/features/base/README.md b/gardenlinux/features/base/README.md new file mode 100644 index 0000000..289640c --- /dev/null +++ b/gardenlinux/features/base/README.md @@ -0,0 +1,21 @@ +## Feature: base +### Description + + +This feature installs the `base` layer for Garden Linux. + + +### Features +All artifacts/images are based on the `base` layer which represents a minimal setup to run the OS itself. This gets achieved by debootstrapping the `minbase` variant. +Within this feature all OS related base configurations (which still might get adjusted by other features on top of it) are performed. + +### Unit testing +Representing the base layer for Garden Linux requires many additional unit tests. Unit tests will ensure that kernel options are correctly defined, debsums matches, no duplicated uids or gids are present, password hash policies are defined and many more. Additional checks like `rkhunter` are also performed. Not all unit tests may work on all fixtures since they may require a running system (e.g. for validating the kernel options). + +### Meta +||| +|---|---| +|type|element| +|artifact|None| +|included_features|`_slim`| +|excluded_features|None| diff --git a/gardenlinux/features/base/exec.config b/gardenlinux/features/base/exec.config new file mode 100755 index 0000000..2f5c90c --- /dev/null +++ b/gardenlinux/features/base/exec.config @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +rm /etc/os-release +cat > /etc/os-release << EOF +ID=gardenlinux +NAME="Garden Linux" +PRETTY_NAME="Garden Linux $BUILDER_VERSION" +HOME_URL="https://gardenlinux.io" +SUPPORT_URL="https://github.com/gardenlinux/gardenlinux" +BUG_REPORT_URL="https://github.com/gardenlinux/gardenlinux/issues" +GARDENLINUX_CNAME=$BUILDER_CNAME +GARDENLINUX_FEATURES=$BUILDER_FEATURES +GARDENLINUX_VERSION=$BUILDER_VERSION +GARDENLINUX_COMMIT_ID=$(echo "$BUILDER_COMMIT" | head -c 8) +GARDENLINUX_COMMIT_ID_LONG=$BUILDER_COMMIT +EOF + +if [ -f /etc/update-motd.d/05-logo ]; then + sed -i "s/@VERSION@/$BUILDER_VERSION/" /etc/update-motd.d/05-logo +fi + +# set default umask to a more conservative value +sed -i 's/UMASK\t\t022/UMASK\t\t027/' /etc/login.defs + +# set Garden Linux as default for dpkg +ln -sf /etc/dpkg/origins/gardenlinux /etc/dpkg/origins/default + +chmod u-s /bin/umount /bin/mount + +# Issue #1137 +# Mark package as manual installed to pass the orphaned test +# The package is installed and required by debootstrap 1.0.127 +apt-mark manual usr-is-merged + +# Issue 1305 +# Mark some packages as manual installed so that the orphaned test +# is not complaining. These packages are installed due to a bug +# in debootstrap 1.0.127+nmu1 which installs the dependencies of +# 'usrmerge' but leaves the package itself behind. +# garden-repo-manager gets installed during the debootstarp process +apt-mark manual \ + libfile-find-rule-perl \ + libgdbm-compat4 \ + libnumber-compare-perl \ + "libperl5.*" \ + libtext-glob-perl diff --git a/gardenlinux/features/base/exec.post b/gardenlinux/features/base/exec.post new file mode 100755 index 0000000..7b09894 --- /dev/null +++ b/gardenlinux/features/base/exec.post @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +rootfs="$1" + +if [ -f "$rootfs/etc/hosts" ] && [ ! -L "$rootfs/etc/hosts" ]; then +cat > "$rootfs/etc/hosts" << EOF +127.0.0.1 localhost +127.0.1.1 garden + +# The following lines are desirable for IPv6 capable hosts +::1 localhost ip6-localhost ip6-loopback +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +EOF +fi + +if [ -f "$rootfs/etc/resolv.conf" ] && [ ! -L "$rootfs/etc/resolv.conf" ]; then +cat > "$rootfs/etc/resolv.conf" << EOF +nameserver 8.8.8.8 +nameserver 8.8.4.4 +nameserver 2001:4860:4860::8888 +nameserver 2001:4860:4860::8844 +EOF +fi + +if [ -f "$rootfs/etc/.resolv.conf.systemd-resolved.bak" ]; then +rm "$rootfs/etc/.resolv.conf.systemd-resolved.bak" +fi + +find "$rootfs/var/log/" "$rootfs/var/cache/" -type f -delete diff --git a/gardenlinux/features/base/file.exclude b/gardenlinux/features/base/file.exclude new file mode 100644 index 0000000..fe58e00 --- /dev/null +++ b/gardenlinux/features/base/file.exclude @@ -0,0 +1,11 @@ +/etc/group- +/etc/gshadow- +/etc/passwd- +/etc/shadow- +/etc/subgid- +/etc/subuid- +/var/cache/apt/pkgcache.bin +/var/cache/apt/srcpkgcache.bin +/var/cache/apt/archives/*.deb +/var/cache/apt/archives/partial/*.deb +/var/log/apt/eipp.log.xz diff --git a/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/autoclean b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/autoclean new file mode 100644 index 0000000..9206f7f --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/autoclean @@ -0,0 +1,3 @@ +DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; }; +APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; }; + diff --git a/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/gzip-indexes b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/gzip-indexes new file mode 100644 index 0000000..4607dc1 --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/gzip-indexes @@ -0,0 +1 @@ +Acquire::GzipIndexes "true"; diff --git a/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-caches b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-caches new file mode 100644 index 0000000..ed295c1 --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-caches @@ -0,0 +1,2 @@ +Dir::Cache::pkgcache ""; +Dir::Cache::srcpkgcache ""; diff --git a/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-languages b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-languages new file mode 100644 index 0000000..2318f84 --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-languages @@ -0,0 +1 @@ +Acquire::Languages "none"; diff --git a/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-recommends b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-recommends new file mode 100644 index 0000000..4e60bce --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-recommends @@ -0,0 +1,2 @@ +APT::Install-Recommends "false"; +Apt::AutoRemove::RecommendsImportant "false"; diff --git a/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-suggests b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-suggests new file mode 100644 index 0000000..a4c107a --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/apt.conf.d/no-suggests @@ -0,0 +1,2 @@ +APT::Install-Suggests "false"; +Apt::AutoRemove::SuggestsImportant "false"; diff --git a/gardenlinux/features/base/file.include/etc/apt/preferences.d/gardenlinux b/gardenlinux/features/base/file.include/etc/apt/preferences.d/gardenlinux new file mode 100644 index 0000000..ca02609 --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/apt/preferences.d/gardenlinux @@ -0,0 +1,3 @@ +Package: * +Pin: release o=GardenLinux +Pin-Priority: 900 diff --git a/gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/forceold b/gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/forceold new file mode 100644 index 0000000..36e401c --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/forceold @@ -0,0 +1,2 @@ +force-confold +force-confdef diff --git a/gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/speedup b/gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/speedup new file mode 100644 index 0000000..7db9103 --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/dpkg/dpkg.cfg.d/speedup @@ -0,0 +1 @@ +force-unsafe-io diff --git a/gardenlinux/features/base/file.include/etc/dpkg/origins/gardenlinux b/gardenlinux/features/base/file.include/etc/dpkg/origins/gardenlinux new file mode 100644 index 0000000..92c1d5a --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/dpkg/origins/gardenlinux @@ -0,0 +1,4 @@ +Vendor: GardenLinux +Vendor-URL: https://gardenlinux.io/ +Bugs: https://github.com/gardenlinux/gardenlinux/issues/new +Parent: Debian diff --git a/gardenlinux/features/base/file.include/etc/sysctl.d/10-disable-sysrq.conf b/gardenlinux/features/base/file.include/etc/sysctl.d/10-disable-sysrq.conf new file mode 100644 index 0000000..411fdc0 --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/sysctl.d/10-disable-sysrq.conf @@ -0,0 +1,2 @@ +# Disables the magic SysRq key +kernel.sysrq = 0 diff --git a/gardenlinux/features/base/file.include/etc/ucf.conf b/gardenlinux/features/base/file.include/etc/ucf.conf new file mode 100644 index 0000000..ac8a24e --- /dev/null +++ b/gardenlinux/features/base/file.include/etc/ucf.conf @@ -0,0 +1 @@ +conf_force_conffold=YES diff --git a/gardenlinux/features/base/file.include/etc/veritytab b/gardenlinux/features/base/file.include/etc/veritytab new file mode 100644 index 0000000..e69de29 diff --git a/gardenlinux/features/base/fstab b/gardenlinux/features/base/fstab new file mode 100644 index 0000000..3070317 --- /dev/null +++ b/gardenlinux/features/base/fstab @@ -0,0 +1,3 @@ +#

+LABEL=EFI /boot/efi vfat umask=0077 type=uefi +LABEL=ROOT / ext4 rw,errors=remount-ro,prjquota,discard diff --git a/gardenlinux/features/base/info.yaml b/gardenlinux/features/base/info.yaml new file mode 100644 index 0000000..7df737b --- /dev/null +++ b/gardenlinux/features/base/info.yaml @@ -0,0 +1,4 @@ +description: "base layer (debootstap --variant minbase, see docker:debian:xx-slim)" +type: element +features: + include: [ _slim ] diff --git a/gardenlinux/features/base/pkg.exclude b/gardenlinux/features/base/pkg.exclude new file mode 100644 index 0000000..8027e92 --- /dev/null +++ b/gardenlinux/features/base/pkg.exclude @@ -0,0 +1,3 @@ +gcc-9-base +gcc-10-base +gcc-11-base diff --git a/gardenlinux/features/base/pkg.include b/gardenlinux/features/base/pkg.include new file mode 100644 index 0000000..8694ccd --- /dev/null +++ b/gardenlinux/features/base/pkg.include @@ -0,0 +1,2 @@ +garden-repo-manager +rng-tools5 diff --git a/gardenlinux/features/base/test/kernel-config.d/kernel-config.txt b/gardenlinux/features/base/test/kernel-config.d/kernel-config.txt new file mode 100644 index 0000000..3f1f79f --- /dev/null +++ b/gardenlinux/features/base/test/kernel-config.d/kernel-config.txt @@ -0,0 +1,2 @@ +# define kernel parameters that should be set or not set +# uses the same syntax as the /boot/config- file \ No newline at end of file diff --git a/gardenlinux/features/base/test/rkhunter.d/rkhunter.conf b/gardenlinux/features/base/test/rkhunter.d/rkhunter.conf new file mode 100644 index 0000000..0a834ba --- /dev/null +++ b/gardenlinux/features/base/test/rkhunter.d/rkhunter.conf @@ -0,0 +1,21 @@ +UPDATE_MIRRORS=0 +MIRRORS_MODE=1 +TMPDIR=/var/lib/rkhunter/tmp +DBDIR=/var/lib/rkhunter/db +SCRIPTDIR=/usr/share/rkhunter/scripts +UPDATE_LANG="en" +LOGFILE=/var/log/rkhunter.log +USE_SYSLOG=authpriv.warning +AUTO_X_DETECT=1 +ALLOW_SSH_ROOT_USER=no +ALLOW_SSH_PROT_V1=0 +ENABLE_TESTS=ALL +DISABLE_TESTS=immutable suspscan hidden_ports hidden_procs deleted_files packet_cap_apps apps +SCRIPTWHITELIST=/usr/bin/egrep +SCRIPTWHITELIST=/usr/bin/fgrep +SCRIPTWHITELIST=/usr/bin/which +SCRIPTWHITELIST=/usr/bin/which.debianutils +SCRIPTWHITELIST=/usr/bin/ldd +SCRIPTWHITELIST=/usr/sbin/adduser +WEB_CMD="/bin/false" +INSTALLDIR=/usr diff --git a/gardenlinux/features/base/test/test_basics.py b/gardenlinux/features/base/test/test_basics.py new file mode 100644 index 0000000..57c5176 --- /dev/null +++ b/gardenlinux/features/base/test/test_basics.py @@ -0,0 +1,70 @@ +import pytest +from helper.utils import get_architecture +from helper.sshclient import RemoteClient + + +def test_no_man(client): + """ Test that no man files are present """ + (exit_code, _, error) = client.execute_command("man ls", disable_sudo=True) + assert exit_code == 127, '"man" should not be installed' + assert "man: command not found" in error + + +def test_ls(client): + """ Test for regular linux folders/mounts """ + (exit_code, output, error) = client.execute_command("ls /") + assert exit_code == 0, f"no {error=} expected" + assert output + arch = get_architecture(client) + lines = output.split("\n") + assert "bin" in lines + assert "boot" in lines + assert "dev" in lines + assert "etc" in lines + assert "home" in lines + assert "lib" in lines + if arch == "amd64": + assert "lib64" in lines + assert "mnt" in lines + assert "opt" in lines + assert "proc" in lines + assert "root" in lines + assert "run" in lines + assert "sbin" in lines + assert "srv" in lines + assert "sys" in lines + assert "tmp" in lines + assert "usr" in lines + assert "var" in lines + + +def test_startup_time(client, non_chroot, non_kvm, non_azure): + """ Test for startup time """ + tolerated_kernel_time = 15 + tolerated_userspace_time = 40 + (exit_code, output, error) = client.execute_command("systemd-analyze") + assert exit_code == 0, f"no {error=} expected" + lines = output.splitlines() + items = lines[0].split(" ") + time_initrd = 0 + for i, v in enumerate(items): + if v == "(kernel)": + time_kernel = items[i-1] + if v == "(initrd)": + time_initrd = items[i-1] + if v == "(userspace)": + time_userspace = items[i-1] + if len(time_kernel) >2 and time_kernel[-2:] == "ms": + time_kernel = str(float(time_kernel[:-2]) / 1000.0) + "s" + if len(time_initrd) >2 and time_initrd[-2:] == "ms": + time_initrd = str(float(time_initrd[:-2]) / 1000.0) + "s" + tf_kernel = float(time_kernel[:-1]) + float(time_initrd[:-1]) + tf_userspace = float(time_userspace[:-1]) + assert tf_kernel < tolerated_kernel_time, f"startup time in kernel space too long: {tf_kernel} seconds = but only {tolerated_kernel_time} tolerated." + assert tf_userspace < tolerated_userspace_time, f"startup time in user space too long: {tf_userspace}seconds but only {tolerated_userspace_time} tolerated." + + +def test_startup_script(client, gcp): + """ Test for validity of startup script on gcp """ + (exit_code, output, error) = client.execute_command("test -f /tmp/startup-script-ok") + assert exit_code == 0, f"no {error=} expected. Startup script did not run" diff --git a/gardenlinux/features/base/test/test_blacklisted_packages.py b/gardenlinux/features/base/test/test_blacklisted_packages.py new file mode 100644 index 0000000..6226c37 --- /dev/null +++ b/gardenlinux/features/base/test/test_blacklisted_packages.py @@ -0,0 +1,18 @@ +from helper.tests.blacklisted_packages import blacklisted_packages +import pytest + +@pytest.mark.parametrize( + "package", + [ + "rlogin", + "rsh", + "rcp", + "telnet", + "libdb5.3:amd64", + "libdb5.3:arm64", + "libssl1.1:amd64", + "libssl1.1:arm64" + ] +) +def test_blacklisted_packages(client, package): + blacklisted_packages(client, package) diff --git a/gardenlinux/features/base/test/test_debsums.py b/gardenlinux/features/base/test/test_debsums.py new file mode 100644 index 0000000..5893805 --- /dev/null +++ b/gardenlinux/features/base/test/test_debsums.py @@ -0,0 +1,16 @@ +from helper.tests.debsums import debsums +import pytest + +@pytest.mark.parametrize( + "exclude", + [ + [ + "/lib/systemd/system/dbus.socket", + "/usr/share/runit/meta/ssh/installed", + "/usr/share/pam-configs/cracklib" + ] + ] +) + +def test_debsums(client, exclude, chroot): + debsums(client, exclude) diff --git a/gardenlinux/features/base/test/test_disable_sysrq.py b/gardenlinux/features/base/test/test_disable_sysrq.py new file mode 100644 index 0000000..0949e2f --- /dev/null +++ b/gardenlinux/features/base/test/test_disable_sysrq.py @@ -0,0 +1,17 @@ +import pytest +from helper.tests.file_content import file_content +from helper.utils import execute_remote_command + + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/sysctl.d/10-disable-sysrq.conf", {"kernel.sysrq": "0"}), + ] +) + + +def test_dmesg(client, file, args, non_chroot): + cmd = "/usr/sbin/sysctl -a > /tmp/sysctl.txt" + execute_remote_command(client, cmd) + file_content(client, file, args) diff --git a/gardenlinux/features/base/test/test_docker.py b/gardenlinux/features/base/test/test_docker.py new file mode 100644 index 0000000..d09ddeb --- /dev/null +++ b/gardenlinux/features/base/test/test_docker.py @@ -0,0 +1,16 @@ +import pytest +from helper.sshclient import RemoteClient + + +def test_docker(client, non_chroot): + """ Test for docker capability """ + (exit_code, output, error) = client.execute_command("grep GARDENLINUX_FEATURES /etc/os-release | grep gardener", quiet=True) + if exit_code != 0: + pytest.skip("test_docker needs the gardenlinux feature gardener to be enabled") + (exit_code, output, error) = client.execute_command("sudo systemctl start docker") + if exit_code != 0: + (journal_rc, output, error) = client.execute_command("sudo journalctl --no-pager -xu docker.service") + assert exit_code == 0, f"no {error=} expected" + (exit_code, output, error) = client.execute_command("sudo docker run --rm alpine:latest ash -c 'echo from container'") + assert exit_code == 0, f"no {error=} expected" + assert output == "from container\n", f"Expected 'from container' output but got {output}" diff --git a/gardenlinux/features/base/test/test_duplicate_uids.py b/gardenlinux/features/base/test/test_duplicate_uids.py new file mode 100644 index 0000000..f0312a3 --- /dev/null +++ b/gardenlinux/features/base/test/test_duplicate_uids.py @@ -0,0 +1 @@ +from helper.tests.find_dup_uids import find_dup_uids as test_duplicate_uids \ No newline at end of file diff --git a/gardenlinux/features/base/test/test_groups.py b/gardenlinux/features/base/test/test_groups.py new file mode 100644 index 0000000..71082b5 --- /dev/null +++ b/gardenlinux/features/base/test/test_groups.py @@ -0,0 +1,15 @@ +import pytest +from helper.tests.groups import groups + + +@pytest.mark.parametrize( + "group,user", + [ + ("root", []), + ("wheel", []) + ] +) + + +def test_groups(client, group, user, non_dev, non_feature_githubActionRunner, non_container): + groups(client, group, user) diff --git a/gardenlinux/features/base/test/test_kernel_config.py b/gardenlinux/features/base/test/test_kernel_config.py new file mode 100644 index 0000000..b0e686b --- /dev/null +++ b/gardenlinux/features/base/test/test_kernel_config.py @@ -0,0 +1,34 @@ +import pytest +from helper.sshclient import RemoteClient +from helper.tests.file_content import file_content + + +@pytest.mark.parametrize( + "args", + [ + ( { + # example expected kernel config options: + #"CONFIG_INIT_ENV_ARG_LIMIT": "32", + #"CONFIG_COMPILE_TEST": "is not set", + #"CONFIG_WERROR": "n", + #"CONFIG_LOCALVERSION": "", + #"CONFIG_LOCALVERSION_AUTO": "is not set", + #"CONFIG_HAVE_KERNEL_GZIP": "y" + } + ) + ] +) + +def test_kernel_config(client, args, non_container): + """compare kernel config options from the parametrize section with the + kernel config options in the /boot/config-* file. The values 'is not set' + and 'n' are treated as the same.""" + file = "/boot/config-*" + file_content(client, file, args, ignore_comments=True) + + +def test_nvme_kernel_parameter(client, aws): + """ Test for NVME kernel params """ + (exit_code, output, error) = client.execute_command("grep -c nvme_core.io_timeout=4294967295 /proc/cmdline") + assert exit_code == 0, f"no {error=} expected" + assert output.rstrip() == "1", "Expected 'nvme_core.io_timeout=4294967295' kernel parameter" diff --git a/gardenlinux/features/base/test/test_metadata_connection.py b/gardenlinux/features/base/test/test_metadata_connection.py new file mode 100644 index 0000000..6f1a9dc --- /dev/null +++ b/gardenlinux/features/base/test/test_metadata_connection.py @@ -0,0 +1,28 @@ +import pytest +from helper.sshclient import RemoteClient + + +def test_metadata_connection(client, non_azure, non_ali, non_chroot, non_kvm): + metadata_host = "169.254.169.254" + (exit_code, output, error) = client.execute_command( + f"wget --timeout 5 http://{metadata_host}" + ) + assert exit_code == 0, f"no {error=} expected" + assert f"Connecting to {metadata_host}:80... connected." in error + assert "200 OK" in error + + +def test_metadata_connection_azure(client, azure): + metadata_url = "http://169.254.169.254/metadata/instance/compute?api-version=2021-01-01&format=json" + (exit_code, output, error) = client.execute_command( + f"curl --connect-timeout 5 '{metadata_url}' -H 'Metadata: true'" + ) + assert exit_code == 0, f"no {error=} expected" + + +def test_metadate_connection_aliyun(client, ali): + metadata_url = "http://100.100.100.200/2016-01-01" + (exit_code, output, error) = client.execute_command( + f"curl --connect-timeout 5 '{metadata_url}' -H 'Metadata: true'" + ) + assert exit_code == 0, f"no {error=} expected" diff --git a/gardenlinux/features/base/test/test_network.py b/gardenlinux/features/base/test/test_network.py new file mode 100644 index 0000000..bf4fb74 --- /dev/null +++ b/gardenlinux/features/base/test/test_network.py @@ -0,0 +1,44 @@ +import pytest +import datetime +from helper.sshclient import RemoteClient + + +def test_hostname_azure(client, azure): + """ Test for valid hostname on azure platform. + The OS is responsible to register its hostname to Azure DNS. + This test checks if hostname registration was successfull. + Only required on azure. + See: https://learn.microsoft.com/en-us/azure/virtual-machines/linux/provisioning + """ + start_time = datetime.datetime.now() + (exit_code, output, error) = client.execute_command("nslookup $(hostname)") + assert exit_code == 0, f"no {error=} expected" + end_time = datetime.datetime.now() + time_diff = (end_time - start_time) + execution_time = round(time_diff.total_seconds()) + assert execution_time <= 10, f"nslookup should not run in a timeout {error}" + + +@pytest.fixture(params=["8.8.8.8", "dns.google", "heise.de"]) +def ping4_host(request): + return request.param + +def test_ping4(client, ping4_host, non_chroot, non_kvm): + """ Test if destination by fixture in pingable (IPv4) """ + command = f"ping -c 5 -W 5 {ping4_host}" + (exit_code, output, error) = client.execute_command(command) + assert exit_code == 0, f'no {error=} expected when executing "{command}"' + assert "5 packets transmitted, 5 received, 0% packet loss" in output + + +@pytest.fixture(params=["2001:4860:4860::8888", "dns.google", "heise.de"]) +def ping6_host(request): + return request.param + +@pytest.mark.skip(reason="ipv6 not available in all vpcs") +def test_ping6(client, ping6_host): + """ Test if destination by fixture in pingable (IPv6) """ + command = f"ping6 -c 5 -W 5 {ping6_host}" + (exit_code, output, error) = client.execute_command(command) + assert exit_code == 0, f'no {error=} expected when executing "{command}"' + assert "5 packets transmitted, 5 received, 0% packet loss" in output diff --git a/gardenlinux/features/base/test/test_orphaned.py b/gardenlinux/features/base/test/test_orphaned.py new file mode 100644 index 0000000..04e094c --- /dev/null +++ b/gardenlinux/features/base/test/test_orphaned.py @@ -0,0 +1,7 @@ +from helper.tests.orphaned import orphaned + +# Execute the orphaned check only on chroot +# since it must install some packages that +# may not be possible on running machines. +def test_orphaned(client, chroot, non_container): + orphaned(client) diff --git a/gardenlinux/features/base/test/test_packages_musthave.py b/gardenlinux/features/base/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/base/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/base/test/test_partition.py b/gardenlinux/features/base/test/test_partition.py new file mode 100644 index 0000000..b3033fc --- /dev/null +++ b/gardenlinux/features/base/test/test_partition.py @@ -0,0 +1,22 @@ +import pytest +from helper.sshclient import RemoteClient + + +def test_growpart(client, non_openstack): + """ Test for an expected partition size around 6GB """ + (exit_code, output, error) = client.execute_command("df --output=size -BG /") + assert exit_code == 0, f"no {error=} expected" + lines = output.splitlines() + sgb = int(lines[1].strip()[:-1]) + assert sgb == 6, f"partition size expected to be ~6 GB but is {sgb}" + + +def test_growpart(client, openstack, openstack_flavor): + """Disk size on OpenStack is only configurable via flavors""" + disk_size = int(openstack_flavor["disk"]) + expected_disk_size = disk_size - 1 + (exit_code, output, error) = client.execute_command("df --output=size -BG /") + assert exit_code == 0, f"no {error=} expected" + lines = output.splitlines() + sgb = int(lines[1].strip()[:-1]) + assert sgb == expected_disk_size, f"partition size expected to be ~{expected_disk_size} GB but is {sgb}" diff --git a/gardenlinux/features/base/test/test_password_hashes.py b/gardenlinux/features/base/test/test_password_hashes.py new file mode 100644 index 0000000..84f14dd --- /dev/null +++ b/gardenlinux/features/base/test/test_password_hashes.py @@ -0,0 +1 @@ +from helper.tests.password_hashes import password_hashes as test_password_hashes \ No newline at end of file diff --git a/gardenlinux/features/base/test/test_password_shadow.py b/gardenlinux/features/base/test/test_password_shadow.py new file mode 100644 index 0000000..d80f66a --- /dev/null +++ b/gardenlinux/features/base/test/test_password_shadow.py @@ -0,0 +1 @@ +from helper.tests.password_shadow import password_shadow as test_password_shadow \ No newline at end of file diff --git a/gardenlinux/features/base/test/test_platform_driver.py b/gardenlinux/features/base/test/test_platform_driver.py new file mode 100644 index 0000000..2705c70 --- /dev/null +++ b/gardenlinux/features/base/test/test_platform_driver.py @@ -0,0 +1,8 @@ +import pytest +from helper.sshclient import RemoteClient + +def test_aws_ena_driver(client, aws): + """ Test for network driver on aws platform """ + (exit_code, output, error) = client.execute_command("/sbin/ethtool -i $(ip -j link show | jq -r '.[] | if .ifname != \"lo\" and .ifname != \"docker0\" then .ifname else empty end') | grep \"^driver\" | awk '{print $2}'") + assert exit_code == 0, f"no {error=} expected" + assert output.rstrip() == "ena", "Expected network interface to run with ena driver" diff --git a/gardenlinux/features/base/test/test_proc.py b/gardenlinux/features/base/test/test_proc.py new file mode 100644 index 0000000..be0e5b8 --- /dev/null +++ b/gardenlinux/features/base/test/test_proc.py @@ -0,0 +1,26 @@ +from helper.utils import execute_local_command + + +def test_proc(client, container, testconfig): + """ Test for an empty /proc within the given rootfs tarball """ + # Get image path from config for platform + image_path = testconfig.get("image") + assert image_path, f"Could not get {image_path}." + + # Execute local command (this does not need to be + # performed within a platform itself) since "/proc" + # will never be empty on a running/used system. + cmd = f"tar tf {image_path}" + rc, out = execute_local_command(cmd) + + # Only proceed if local command was sucessfull + assert rc == 0, f"Could not unpack {image_path}." + + # Validate for captures of any "proc/" content + matches = [] + for line in out.split('\n'): + if "proc/" in line: + matches.append(line) + + # Only "/proc/" as an empty directory should be found + assert len(matches) == 1, f"/proc is not empty. Found: {matches}" diff --git a/gardenlinux/features/base/test/test_random.py b/gardenlinux/features/base/test/test_random.py new file mode 100644 index 0000000..d9afe1d --- /dev/null +++ b/gardenlinux/features/base/test/test_random.py @@ -0,0 +1,70 @@ +import pytest +import re +from helper.sshclient import RemoteClient + +def test_random(client, non_metal): + (exit_code, output, error) = client.execute_command("time dd if=/dev/random of=/dev/null bs=8k count=1000 iflag=fullblock", disable_sudo=True) + """ Output should be like this: +# time dd if=/dev/random of=/dev/null bs=8k count=1000 iflag=fullblock +1000+0 records in +1000+0 records out +8192000 bytes (8.2 MB, 7.8 MiB) copied, 0.0446423 s, 184 MB/s + +real 0m0.046s +user 0m0.004s +sys 0m0.042s +""" + + assert exit_code == 0, f"no {error=} expected" + lines = error.splitlines() + bycount = lines[2].split(" ")[0] + assert bycount == "8192000", "byte cound expected to be 8192000 but is %s" % bycount + real = lines[4].split()[1] + pt=r'(\d+)m(\d+)' + m=re.search(pt, real) + duration = (int(m.group(1)) * 60) + int(m.group(2)) + assert duration == 0, "runtime of test expected to be below one second %s" % m.group(1) + + (exit_code, output, error) = client.execute_command("time rngtest --blockcount=9000 < /dev/random", disable_sudo=True) + """ Output should be like this: +# time rngtest --blockcount=9000 < /dev/random +rngtest 5 +Copyright (c) 2004 by Henrique de Moraes Holschuh +This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +rngtest: starting FIPS tests... +rngtest: bits received from input: 180000032 +rngtest: FIPS 140-2 successes: 8992 +rngtest: FIPS 140-2 failures: 8 +rngtest: FIPS 140-2(2001-10-10) Monobit: 1 +rngtest: FIPS 140-2(2001-10-10) Poker: 2 +rngtest: FIPS 140-2(2001-10-10) Runs: 2 +rngtest: FIPS 140-2(2001-10-10) Long run: 3 +rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 +rngtest: input channel speed: (min=167.311; avg=1321.184; max=1467.191)Mibits/s +rngtest: FIPS tests speed: (min=24.965; avg=138.842; max=145.599)Mibits/s +rngtest: Program run time: 1367504 microseconds + +real 0m1.370s +user 0m1.260s +sys 0m0.108s +""" + # a few test will most certainly always fail, therefore we expect an + # error + # assert exit_code == 0, f"no {error=} expected" + p_succ=r'rngtest: FIPS 140-2 successes: (\d+)' + m=re.search(p_succ, error) + successes = int(m.group(1)) + assert successes >8980, "Number of successes expected to be greater than 8980" + + p_fail=r'rngtest: FIPS 140-2 failures: (\d+)' + m=re.search(p_fail, error) + failures = int(m.group(1)) + assert failures <= 20, "Number of failures expected ot be less than or equal to 20" + + assert successes + failures == 9000, "sanity check failed" + + p_real=r'real\s+(\d+)m(\d+)' + m=re.search(p_real, error) + duration = (int(m.group(1)) * 60) + int(m.group(2)) + assert duration < 5, "Expected the test to run in less than 5 seconds" diff --git a/gardenlinux/features/base/test/test_rkhunter.py b/gardenlinux/features/base/test/test_rkhunter.py new file mode 100644 index 0000000..9b8fd27 --- /dev/null +++ b/gardenlinux/features/base/test/test_rkhunter.py @@ -0,0 +1 @@ +from helper.tests.rkhunter import rkhunter as test_rkhunter diff --git a/gardenlinux/features/base/test/test_sgid_suid_files.py b/gardenlinux/features/base/test/test_sgid_suid_files.py new file mode 100644 index 0000000..6e86bd3 --- /dev/null +++ b/gardenlinux/features/base/test/test_sgid_suid_files.py @@ -0,0 +1,40 @@ +from helper.tests.sgid_suid_files import sgid_suid_files +import pytest + +# Parametrize the test unit with further +# options to distinct 'sgid' and 'suid' tests. +@pytest.mark.parametrize( + "test_type,whitelist_files", + [ + ("sgid", [ + "/usr/bin/expiry,root,shadow", + "/usr/bin/write,root,tty", + "/usr/bin/wall,root,tty", + "/usr/bin/chage,root,shadow", + "/usr/bin/ssh-agent,root,_ssh", + "/usr/sbin/unix_chkpwd,root,shadow", + "/usr/lib/systemd-cron/crontab_setgid,root,crontab", + ] + ), + ("suid", [ + "/usr/bin/chsh,root,root", + "/usr/lib/openssh/ssh-keysign,root,root", + "/usr/bin/newgrp,root,root", + "/usr/bin/su,root,root", + "/usr/lib/dbus-1.0/dbus-daemon-launch-helper,root,messagebus", + "/usr/bin/chfn,root,root", + "/usr/bin/gpasswd,root,root", + "/usr/bin/sudo,root,root", + "/usr/bin/passwd,root,root", + "/usr/lib/polkit-1/polkit-agent-helper-1,root,root", + "/usr/bin/pkexec,root,root" + ] + ) + ] +) + + +# Run the test unit to perform the +# final tests by the given artifact. +def test_sgid_suid_files(client, test_type, whitelist_files, non_vhost): + sgid_suid_files(client, test_type, whitelist_files) diff --git a/gardenlinux/features/base/test/test_systemd.py b/gardenlinux/features/base/test/test_systemd.py new file mode 100644 index 0000000..4ebfb80 --- /dev/null +++ b/gardenlinux/features/base/test/test_systemd.py @@ -0,0 +1,42 @@ +import json +import pytest +from helper.sshclient import RemoteClient + +def test_systemctl_no_failed_units(client, non_chroot, non_kvm): + """this test always fails on kvm therefore kvm has it's own, chroot does not use systemd""" + (exit_code, output, error) = client.execute_command("systemctl list-units --output=json --state=failed") + assert exit_code == 0, f"no {error=} expected" + out = (json.loads(output)) + failed_units = [] + + for entry in out: + failed_units.append(entry['unit']) + + for unit in failed_units: + (log_exit_code, log_output, log_error) = client.execute_command(f"journalctl --no-pager -u {unit}") + print(log_output) + if log_exit_code != 0: + print(f"journalctl failed to receive logs for unit {unit}. exited with {log_exit_code}") + print(log_error) + + assert len(failed_units) == 0, f"systemd units {', '.join(failed_units)} failed" + + +def test_systemctl_no_failed_units_kvm(client, kvm): + """rngd.service does not start in kvm due of missing /dev/tpm0""" + (exit_code, output, error) = client.execute_command("systemctl list-units --output=json --state=failed") + assert exit_code == 0, f"no {error=} expected" + out = (json.loads(output)) + failed_units = [] + for entry in out: + if not entry['unit'] == "rngd.service": + failed_units.append(entry['unit']) + + for unit in failed_units: + (log_exit_code, log_output, log_error) = client.execute_command(f"journalctl --no-pager -u {unit}") + print(log_output) + if log_exit_code != 0: + print(f"journalctl failed to receive logs for unit {unit}. exited with {log_exit_code}") + print(log_error) + + assert len(failed_units) == 0, f"systemd units {', '.join(failed_units)} failed" diff --git a/gardenlinux/features/base/test/test_tiger.py b/gardenlinux/features/base/test/test_tiger.py new file mode 100644 index 0000000..4f18129 --- /dev/null +++ b/gardenlinux/features/base/test/test_tiger.py @@ -0,0 +1 @@ +from helper.tests.tiger import tiger as test_tiger \ No newline at end of file diff --git a/gardenlinux/features/base/test/test_time_config.py b/gardenlinux/features/base/test/test_time_config.py new file mode 100644 index 0000000..d2a4e18 --- /dev/null +++ b/gardenlinux/features/base/test/test_time_config.py @@ -0,0 +1,111 @@ +import pytest +import time +from pathlib import Path +from helper.sshclient import RemoteClient + + +def test_correct_ntp(client, aws): + """ Test for hyperscaler related NTP server """ + (exit_code, output, error) = client.execute_command("grep -c ^NTP=169.254.169.123 /etc/systemd/timesyncd.conf") + assert exit_code == 0, f"no {error=} expected" + assert output.rstrip() == "1", "Expected NTP server to be configured to 169.254.169.123" + + +def test_correct_ntp(client, gcp): + """ Test for hyperscaler related NTP server """ + (exit_code, output, error) = client.execute_command("timedatectl show-timesync | grep -c ^SystemNTPServers=metadata.google.internal") + assert exit_code == 0, f"no {error=} expected" + assert output.rstrip() == "1", "Expected NTP server to be configured to metadata.google.internal" + + +def test_timesync(client, azure): + """ Ensure symbolic link has been created """ + (exit_code, output, error) = client.execute_command("test -L /dev/ptp_hyperv") + assert exit_code == 0, f"Expected /dev/ptp_hyperv to be a symbolic link" + + +def test_clock(client): + """ Test clock skew """ + (exit_code, output, error) = client.execute_command("date '+%s'") + local_seconds = time.time() + assert exit_code == 0, f"no {error=} expected" + remote_seconds = int(output) + assert ( + abs(local_seconds - remote_seconds) < 5 + ), "clock skew should be less than 5 seconds. Local time is %s and remote time is %s" % (local_seconds, remote_seconds) + + +def test_ntp(client, non_azure, non_chroot): + """ Azure does not use systemd-timesyncd """ + (exit_code, output, error) = client.execute_command("timedatectl show") + assert exit_code == 0, f"no {error=} expected" + lines = output.splitlines() + ntp_ok=False + ntp_synchronised_ok=False + for l in lines: + nv = l.split("=") + if nv[0] == "NTP" and nv[1] == "yes": + ntp_ok = True + if nv[0] == "NTPSynchronized" and nv[1] == "yes": + ntp_synchronised_ok = True + assert ntp_ok, "NTP not activated" + assert ntp_synchronised_ok, "NTP not synchronized" + + +def test_files_not_in_future(client): + """ Testing for files in future """ + testscript_name="/tmp/filemodtime-test.py" + testscript='''import os +import sys +from datetime import datetime + +now = datetime.now() +dirs = ["/bin", "/etc/ssh"] +for dir in dirs: + for (dirpath, dirnames, filenames) in os.walk(dir): + for f in filenames: + file = os.path.join(dir, f) + modification = datetime.fromtimestamp(os.path.getmtime(file)) + if modification > now: + print(f"FAIL - {file}s timestamp is {modification}") + sys.exit(1) +__EOF +''' + (exit_code, output, error) = client.execute_command(f"cat << '__EOF'\n{testscript}\n > {testscript_name}") + assert exit_code == 0, f"no {error=} expected" + (exit_code, output, error) = client.execute_command(f"python3 {testscript_name}") + assert exit_code == 0, f"no {error=} expected" + assert "FAIL" not in output + + +def test_clocksource(client, aws): + """ Test for clocksource """ + # refer to https://aws.amazon.com/premiumsupport/knowledge-center/manage-ec2-linux-clock-source/ + # detect hypervisor type kvm or xen + (exit_code, output, error) = client.execute_command("systemd-detect-virt") + assert exit_code == 0, f"no {error=} expected" + hypervisor = output.rstrip() + # which architecture are we on? + (exit_code, output, error) = client.execute_command("uname -m") + assert exit_code == 0, f"no {error=} expected" + arch = output.rstrip() + # check clock_source + (exit_code, output, error) = client.execute_command("cat /sys/devices/system/clocksource/clocksource0/current_clocksource") + assert exit_code == 0, f"no {error=} expected" + if hypervisor == "xen": + assert output.rstrip() == "tsc", f"expected clocksoure for xen to be set to tsc but got {output}" + elif hypervisor == "kvm" or hypervisor == "amazon": + if arch == "aarch64": + assert output.rstrip() == "arch_sys_counter", f"expected clocksoure on ARM64 to be set to arch_sys_counter-clock but got {output}" + else: + assert output.rstrip() == "kvm-clock", f"expected clocksoure for kvm to be set to kvm-clock but got {output}" + else: + assert False, f"unknown hypervisor {hypervisor}" + + +def test_chrony(client, azure): + """ Test for specific chrony configuration on azure """ + expected_config = "refclock PHC /dev/ptp_hyperv poll 3 dpoll -2 offset 0" + (exit_code, output, error) = client.execute_command("cat /etc/chrony/chrony.conf") + assert exit_code == 0, f"no {error=} expected" + assert output.find(expected_config) != -1, f"chrony config for ptp expected but not found" diff --git a/gardenlinux/features/base/test/test_umask.py b/gardenlinux/features/base/test/test_umask.py new file mode 100644 index 0000000..23b829d --- /dev/null +++ b/gardenlinux/features/base/test/test_umask.py @@ -0,0 +1,20 @@ +from helper.tests.file_content import file_content +import pytest + +# Parametrize the test unit with further +# options. +# First: File to process +# Second: Key to search +# Third: Value of Key +@pytest.mark.parametrize( + "file,dict", + [ + ("/etc/login.defs", {"UMASK": "027"}) + ] +) + + +# Run the test unit to perform the +# final tests by the given artifact. +def test_umask(client, file, dict): + file_content(client, file, dict) diff --git a/gardenlinux/features/base/test/test_users.py b/gardenlinux/features/base/test/test_users.py new file mode 100644 index 0000000..ff2fdd6 --- /dev/null +++ b/gardenlinux/features/base/test/test_users.py @@ -0,0 +1,4 @@ +from helper.tests.users import users + +def test_users(client, non_dev, non_feature_githubActionRunner, non_vhost, non_hyperscalers, non_container): + users(client) diff --git a/gardenlinux/features/base/test/test_usr_ro.py b/gardenlinux/features/base/test/test_usr_ro.py new file mode 100644 index 0000000..558325e --- /dev/null +++ b/gardenlinux/features/base/test/test_usr_ro.py @@ -0,0 +1,5 @@ + +def test_usr_read_only(client, non_chroot): + (exit_code, output, error) = client.execute_command("/usr/bin/touch /usr/fail-test") + assert exit_code == 1, "Read-only file system error expected" + assert error.rstrip() == "/usr/bin/touch: cannot touch '/usr/fail-test': Read-only file system" diff --git a/gardenlinux/features/base/test/tiger.d/tigerrc b/gardenlinux/features/base/test/tiger.d/tigerrc new file mode 100644 index 0000000..5a7c3c6 --- /dev/null +++ b/gardenlinux/features/base/test/tiger.d/tigerrc @@ -0,0 +1,115 @@ +TigerNoBuild=Y # C files are corrupted (ouch.) +Tiger_Check_PASSWD=Y # Fast +Tiger_Check_PASSWD_FORMAT=N # Fast - not needed if on systems with pwck +Tiger_Check_PASSWD_SHADOW=Y # Time varies on # of users +Tiger_Check_PASSWD_NIS=N # Time varies on # of users +Tiger_Check_GROUP=Y # Fast +Tiger_Check_ACCOUNTS=Y # Time varies on # of users +Tiger_Check_RHOSTS=Y # Time varies on # of users +Tiger_Check_NETRC=Y # Time varies on # of users +Tiger_Check_ALIASES=Y # Fast +Tiger_Check_CRON=Y # Fast +Tiger_Check_EXPORTS=Y # Fast +Tiger_Check_SERVICES=N # Could be faster, not bad though +Tiger_Check_KNOWN=Y # Fast +Tiger_Check_PERMS=Y # Could be faster, not bad though +Tiger_Check_SIGNATURES=N # Several minutes +Tiger_Check_FILESYSTEM=Y # Time varies on disk space... can be hours +Tiger_Check_ROOTDIR=Y # Fast, only 2 checks +Tiger_Check_ROOT_ACCESS=Y # Fast +Tiger_Check_PATH=Y # Fast for just root... varies for all +Tiger_Check_EMBEDDED=N # Several minutes +Tiger_Check_BACKUPS=N # Fast +Tiger_Check_LOGFILES=N # Fast +Tiger_Check_USERUMASK=Y # Fast +Tiger_Check_ETCISSUE=N # Fast, needs to be customised +Tiger_Check_STRICTNW=Y # Fast - stringent N/W server checks +Tiger_Check_LISTENING=N # Fast +Tiger_Check_SYSTEM=Y # Depends on the specific system checks +Tiger_Check_RUNPROC=N # Fast, needs to be customized per system +Tiger_Check_DELETED=N # Depends on the number of processes on the +Tiger_Check_EXRC=N # Depends on the size of the filesystem +Tiger_Check_ROOTKIT=Y # Slow if chkrootkit is available + # system + +Tiger_Check_INETD=N # Fast for inetd, Varies on xinetd +Tiger_Check_APACHE=N # Fast +Tiger_Check_SSH=N # Fast +Tiger_Check_SENDMAIL=N # Fast +Tiger_Check_PRINTCAP=N # Fast, possibly not needed in systems that use CUPS +Tiger_Check_ANONFTP=N # Fast +Tiger_Check_FTPUSERS=N # Fast +Tiger_Check_OMNIBACK=N # Fast +Tiger_Check_NTP=N # Fast + +Tiger_Check_PATCH=N # Depends on your network connection + # (if no timeout is fixed it might stall) +Tiger_Check_SINGLE=Y # Fast +Tiger_Check_BOOT=Y # Fast +Tiger_Check_INITTAB=Y # Fast +Tiger_Check_RCUMASK=Y # Fast +Tiger_Check_NEVERLOG=Y # Fast +# disable because of check_release for debian is going to fail +Tiger_Check_OS=N # Fast +Tiger_Check_NETWORKCONFIG=N # Fast +Tiger_Deb_CheckMD5Sums=N +Tiger_Deb_NoPackFiles=N +Tiger_Check_TRUSTED=N +Tiger_Cron_SendOKReports=N +TigerCron_Log_Keep_Max=10 +Tiger_Cron_Template=N +Tiger_Cron_CheckPrev=N +Tiger_Show_INFO_Msgs=N +Tiger_Run_CRACK=N # First time, ages; subsequent fairly quick +Tiger_Output_FQDN=Y +Tiger_Run_TRIPW=N # Slow +Tiger_Run_AIDE=N # Slow +Tiger_Run_INTEGRIT=N # Slow +Tiger_INTEGRIT_CFG=/etc/integrit/integrit.conf + + +Tiger_Output_Width=150 +Tiger_CRON_Output_Width=0 +Tiger_Global_PATH="/etc/profile /etc/csh.login" +Tiger_Passwd_Constraints="PASS_MIN_DAYS PASS_MAX_DAYS PASS_WARN_AGE PASS_MIN_LEN" +Tiger_Passwd_Hashes='crypt3|md5|sha512' +Tiger_Dormant_Limit=60 +Tiger_Admin_Accounts='adm|bin|daemon|games|lp|mail|news|operator|sync|sys|uucp|man|proxy|majordom|postgres|www-data|operator|irc|gnats' +Tiger_Embed_Max_Depth=3 +Tiger_Embed_Check_Exec_Only=N +Tiger_Embed_Check_SUID=Y +Tiger_Embed_Report_Exec_Only=N +Tiger_Embedded_OK_Owners='root|bin|uucp|sys|daemon' +Tiger_Embedded_OK_Group_Write='root|bin|uucp|sys|daemon' +Tiger_Check_PATHALL=Y # Check all user PATHs in startup files. +Tiger_ROOT_PATH_OK_Owners='root|uucp|bin|news|sys|daemon' +Tiger_ROOT_PATH_OK_Group_Write='root|uucp|bin|sys|daemon' +Tiger_PATH_OK_Owners=$Tiger_ROOT_PATH_OK_Owners +eval Tiger_PATH_OK_Group_Write='$Tiger_ROOT_PATH_OK_Group_Write' +Tiger_Collect_CRACK=Y +Tiger_Crack_Local=Y +Tiger_Mail_RCPT="root" +Tiger_Files_of_Note="..[!.]*/.* */.* */.[!.]/.log/.FSP*" +Tiger_FSScan_Setuid=Y # Setuid executables +Tiger_FSScan_Setgid=Y # Setgid executables +Tiger_FSScan_Devs=Y # device files +Tiger_FSScan_SymLinks=Y # strange symbolic links +Tiger_FSScan_ofNote=Y # weird filenames +Tiger_FSScan_WDIR=Y # world writable directories +Tiger_FSScan_Unowned=Y # files with undefined owners/groups +Tiger_FSScan_WarnUnknown=Y # Warn about unknown filesystems used +Tiger_FSScan_Local='' # Filesystems considered to be local to the system, pipe-separated +Tiger_FSScan_NonLocal='' # Filesystems considered to be non-local to the system, pipe-separated +Tiger_FSScan_ReadOnly=Y +USERDOTFILES=".alias .kshrc .cshrc .profile .login .mailrc .exrc .emacs .forward .tcshrc .zshenv .zshrc .zlogin .zprofile .rcrc .bashrc .bash_profile .inputrc .xinitrc .fvwm2rc .Xsession .Xclients .less" + +Tiger_Accounts_Trust=999 +Tiger_SSH_Protocol='1|2' +Tiger_SSH_RhostsAuthentication='no' +Tiger_SSH_PasswordAuthentication='no' +Tiger_Listening_Every=N +Tiger_Listening_ValidUsers='root' +Tiger_Listening_ValidProcs='' +Tiger_Running_Procs='syslogd cron atd klogd' +Tiger_DPKG_Optimize=Y +Tiger_CHKROOTKIT_ARGS="-q" diff --git a/gardenlinux/features/cloud/README.md b/gardenlinux/features/cloud/README.md new file mode 100644 index 0000000..8e67827 --- /dev/null +++ b/gardenlinux/features/cloud/README.md @@ -0,0 +1,19 @@ +## Feature: cloud +### Description + +This feature adjusts Garden Linux for cloud functionality with cloud kernel, boot optimizations, etc. + + +### Features +All hyperscaler & cloud platforms (e.g. `ali`, `azure`, `vmware`, `openstack`, etc.) are based on this `cloud` feature. Garden Linux artifacts with cloud functionality are highly optimized for cloud/hyperscaler environments by running on ARM64 or AMD64 hardware platforms including optimized kernel and boot options. + +### Unit testing +this feature supports unit tests and ensures that all required packages are present, an autologout is configured, PAM faillock is active and the wireguard kernel module is activated. + +### Meta +||| +|---|---| +|type|element| +|artifact|None| +|included_features|server| +|excluded_features|None| diff --git a/gardenlinux/features/cloud/exec.config b/gardenlinux/features/cloud/exec.config new file mode 100755 index 0000000..4760c10 --- /dev/null +++ b/gardenlinux/features/cloud/exec.config @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +exit 0 diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/00-default.cfg b/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/00-default.cfg new file mode 100644 index 0000000..d7ed5d1 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/00-default.cfg @@ -0,0 +1,5 @@ +# DO NOT CHANGE THIS FILE! USE /etc/kernel/cmdline.d +CMDLINE_LINUX="ro earlyprintk=ttyS0,115200 consoleblank=0" +DEVICE="LABEL=ROOT" +# WARNING! 0 disables the TIMEOUT +TIMEOUT=1 diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/40-enable-swap-cgroup-accounting.cfg b/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/40-enable-swap-cgroup-accounting.cfg new file mode 100644 index 0000000..bd39abe --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/40-enable-swap-cgroup-accounting.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="$CMDLINE_LINUX cgroup_enable=memory swapaccount=1" diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/60-timeout.cfg b/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/60-timeout.cfg new file mode 100644 index 0000000..8910b3b --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/cmdline.d/60-timeout.cfg @@ -0,0 +1,2 @@ +# WARNING! 0 DISABLES THE TIMEOUT! +TIMEOUT=1 diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/entry-token b/gardenlinux/features/cloud/file.include/etc/kernel/entry-token new file mode 100644 index 0000000..697bb66 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/entry-token @@ -0,0 +1 @@ +Default diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/00-kernel-cmdline b/gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/00-kernel-cmdline new file mode 100755 index 0000000..718c595 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/00-kernel-cmdline @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euoE pipefail + +/usr/local/sbin/update-kernel-cmdline diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/zz-update-syslinux b/gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/zz-update-syslinux new file mode 100755 index 0000000..404d7e8 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/postinst.d/zz-update-syslinux @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euoE pipefail + +which syslinux &> /dev/null || exit 0 + +/usr/local/sbin/update-syslinux diff --git a/gardenlinux/features/cloud/file.include/etc/kernel/postrm.d/zz-update-syslinux b/gardenlinux/features/cloud/file.include/etc/kernel/postrm.d/zz-update-syslinux new file mode 100755 index 0000000..404d7e8 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/kernel/postrm.d/zz-update-syslinux @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euoE pipefail + +which syslinux &> /dev/null || exit 0 + +/usr/local/sbin/update-syslinux diff --git a/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_firewire.conf b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_firewire.conf new file mode 100644 index 0000000..97955f7 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_firewire.conf @@ -0,0 +1,2 @@ +blacklist firewire-core +install firewire-core /bin/true diff --git a/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_fs.conf b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_fs.conf new file mode 100644 index 0000000..4a03742 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_fs.conf @@ -0,0 +1,10 @@ +blacklist cramfs +install cramfs /bin/true +blacklist freevxfs +install freevxfs /bin/true +blacklist jffs2 +install jffs2 /bin/true +blacklist hfs +install hfs /bin/true +blacklist hfsplus +install hfsplus /bin/true diff --git a/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_net.conf b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_net.conf new file mode 100644 index 0000000..4dba0ea --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_net.conf @@ -0,0 +1,8 @@ +blacklist dccp +install dccp /bin/true +blacklist sctp +install sctp /bin/true +blacklist rds +install rds /bin/true +blacklist tipc +install tipc /bin/true diff --git a/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_udf.conf b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_udf.conf new file mode 100644 index 0000000..b716ebe --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_udf.conf @@ -0,0 +1,2 @@ +blacklist udf +install udf /bin/true diff --git a/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_usb.conf b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_usb.conf new file mode 100644 index 0000000..bccda11 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/modprobe.d/disabled_usb.conf @@ -0,0 +1,6 @@ +blacklist usbcore +install usbcore /bin/true +blacklist usb-common +install usb-common /bin/true +blacklist usb-storage +install usb-storage /bin/true diff --git a/gardenlinux/features/cloud/file.include/etc/profile.d/50-autologout.sh b/gardenlinux/features/cloud/file.include/etc/profile.d/50-autologout.sh new file mode 100755 index 0000000..b6f76e4 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/profile.d/50-autologout.sh @@ -0,0 +1,4 @@ +# shellcheck shell=sh disable=SC2148 +TMOUT=900 +readonly TMOUT +export TMOUT diff --git a/gardenlinux/features/cloud/file.include/etc/repart.d/root.conf b/gardenlinux/features/cloud/file.include/etc/repart.d/root.conf new file mode 100644 index 0000000..1aadd2d --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/repart.d/root.conf @@ -0,0 +1,2 @@ +[Partition] +Type=root diff --git a/gardenlinux/features/cloud/file.include/etc/sysctl.d/20-cloud.conf b/gardenlinux/features/cloud/file.include/etc/sysctl.d/20-cloud.conf new file mode 100644 index 0000000..4a1aa97 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/sysctl.d/20-cloud.conf @@ -0,0 +1,14 @@ +# Randomize addresses of mmap base, heap, stack and VDSO page +kernel.randomize_va_space = 2 + +# Provide protection from ToCToU races +fs.protected_hardlinks=1 + +# Provide protection from ToCToU races +fs.protected_symlinks=1 + +# Make locating kernel addresses more difficult +kernel.kptr_restrict=1 + +# Set perf only available to root +kernel.perf_event_paranoid=2 diff --git a/gardenlinux/features/cloud/file.include/etc/sysctl.d/21-ipv4-settings.conf b/gardenlinux/features/cloud/file.include/etc/sysctl.d/21-ipv4-settings.conf new file mode 100644 index 0000000..7f33455 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/sysctl.d/21-ipv4-settings.conf @@ -0,0 +1,42 @@ +# Enable syn flood protection +net.ipv4.tcp_syncookies = 1 + +# Ignore source-routed packets +net.ipv4.conf.all.accept_source_route = 0 + +# Ignore ICMP redirects +net.ipv4.conf.all.accept_redirects = 0 + +# Ignore ICMP redirects from non-GW hosts +net.ipv4.conf.all.secure_redirects = 1 + +# Ignore ICMP redirects from non-GW hosts +net.ipv4.conf.default.secure_redirects = 1 + +# Ignore ICMP broadcasts to avoid participating in Smurf attacks +net.ipv4.icmp_echo_ignore_broadcasts = 1 + +# Ignore bad ICMP errors +net.ipv4.icmp_ignore_bogus_error_responses = 1 + +# Accept source-routed packets +net.ipv4.conf.default.accept_source_route = 1 + +# Accept ICMP redirects +net.ipv4.conf.default.accept_redirects = 1 + +# Do not log spoofed, source-routed, and redirect packets +net.ipv4.conf.all.log_martians = 0 + +# Enable forwarding for all interfaces +net.ipv4.conf.all.forwarding = 1 + +# Make forwarding the default +net.ipv4.conf.default.forwarding = 1 + +# Allow redirects on all interfaces and make it the default +net.ipv4.conf.default.send_redirects = 1 +net.ipv4.conf.all.send_redirects = 1 + +# Promote a secondary IP address in case a primary IP address is removed +net.ipv4.conf.all.promote_secondaries = 1 diff --git a/gardenlinux/features/cloud/file.include/etc/sysctl.d/22-ipv6-settings.conf b/gardenlinux/features/cloud/file.include/etc/sysctl.d/22-ipv6-settings.conf new file mode 100644 index 0000000..419b18d --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/sysctl.d/22-ipv6-settings.conf @@ -0,0 +1,5 @@ +# Enable forwarding for all interfaces +net.ipv6.conf.all.forwarding = 1 + +# Make forwarding the default +net.ipv6.conf.default.forwarding = 1 diff --git a/gardenlinux/features/cloud/file.include/etc/systemd/system/rngd.service.d/architecture.conf b/gardenlinux/features/cloud/file.include/etc/systemd/system/rngd.service.d/architecture.conf new file mode 100644 index 0000000..752e3e4 --- /dev/null +++ b/gardenlinux/features/cloud/file.include/etc/systemd/system/rngd.service.d/architecture.conf @@ -0,0 +1,2 @@ +[Unit] +ConditionArchitecture=x86-64 \ No newline at end of file diff --git a/gardenlinux/features/cloud/info.yaml b/gardenlinux/features/cloud/info.yaml new file mode 100644 index 0000000..1ac0330 --- /dev/null +++ b/gardenlinux/features/cloud/info.yaml @@ -0,0 +1,6 @@ +description: "cloud functionality (cloud kernel, cloud boot)" +type: element +features: + include: + - server + - _boot diff --git a/gardenlinux/features/cloud/pkg.include b/gardenlinux/features/cloud/pkg.include new file mode 100644 index 0000000..02faa24 --- /dev/null +++ b/gardenlinux/features/cloud/pkg.include @@ -0,0 +1,2 @@ +fdisk +linux-image-6.1-cloud-$arch diff --git a/gardenlinux/features/cloud/test/debsums.disable b/gardenlinux/features/cloud/test/debsums.disable new file mode 100644 index 0000000..b193d52 --- /dev/null +++ b/gardenlinux/features/cloud/test/debsums.disable @@ -0,0 +1,2 @@ +# disabling as it will fail because of the signed systemd-bootx64.efi +# TODO : re-enable when we have our own systemd packages diff --git a/gardenlinux/features/cloud/test/test_autologout.py b/gardenlinux/features/cloud/test/test_autologout.py new file mode 100644 index 0000000..9e4c5bf --- /dev/null +++ b/gardenlinux/features/cloud/test/test_autologout.py @@ -0,0 +1,24 @@ +from helper.tests.file_content import file_content +import pytest + +# Parametrize the test unit with further +# options. +# First: File to process +# Second: Key to search +# Third: Value of Key +@pytest.mark.parametrize( + "file,dict", + [ + ("/etc/profile.d/50-autologout.sh", { + "TMOUT": "900", + "readonly": "TMOUT", + "export": "TMOUT" + }) + ] +) + + +# Run the test unit to perform the +# final tests by the given artifact. +def test_autologout(client, file, dict): + file_content(client, file, dict) diff --git a/gardenlinux/features/cloud/test/test_packages_musthave.py b/gardenlinux/features/cloud/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/cloud/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/cloud/test/test_wireguard.py b/gardenlinux/features/cloud/test/test_wireguard.py new file mode 100644 index 0000000..a7fad43 --- /dev/null +++ b/gardenlinux/features/cloud/test/test_wireguard.py @@ -0,0 +1,12 @@ +import pytest +from helper.tests.kernel_modules import kernel_modules + +@pytest.mark.parametrize( + "kernel_module", + [ + "net/wireguard/wireguard.ko" + ] +) + +def test_kernel_modules(client, kernel_module, non_chroot): + kernel_modules(client, kernel_module) diff --git a/gardenlinux/features/firewall/README.md b/gardenlinux/features/firewall/README.md new file mode 100644 index 0000000..90c7cd8 --- /dev/null +++ b/gardenlinux/features/firewall/README.md @@ -0,0 +1,36 @@ +## Feature: firewall +### Description + + +This features adds a basic `nftables` based firewall to Garden Linux. + + +### Features +This features adds a basic `nftables` based firewall to Garden Linux and is controlled by systemd. +The firewall rules can be adjusted by editing the corresponding rule file: +``` +IPv4 and IPv6: /etc/nft.d/default.conf +``` + +#### Input +By default, only the following incoming rules are allowed: + + * tcp/22 [SSH] + * state/related-established + +#### Output + * Open (not filtered) + +#### Forward + * Open (not filtered) + +### Unit testing +This feature support unit tests and validates that the default incoming policy is set to drop. Additionally, all needed packages will be validated. + +### Meta +||| +|---|---| +|type|element| +|artifact|None| +|included_features|None| +|excluded_features|None| diff --git a/gardenlinux/features/firewall/exec.config b/gardenlinux/features/firewall/exec.config new file mode 100755 index 0000000..dc4406d --- /dev/null +++ b/gardenlinux/features/firewall/exec.config @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + set -Eeuo pipefail + +# Add and enable Garden Linux CIS firewall +systemctl enable nftables + +# add drop in dir for nft +echo -e "\ninclude \"/etc/nft.d/*.conf\"" >> /etc/nftables.conf diff --git a/gardenlinux/features/firewall/file.include/etc/nft.d/default.conf b/gardenlinux/features/firewall/file.include/etc/nft.d/default.conf new file mode 100644 index 0000000..9e3752e --- /dev/null +++ b/gardenlinux/features/firewall/file.include/etc/nft.d/default.conf @@ -0,0 +1,21 @@ +table inet filter { + chain input { + counter + policy drop + iifname "lo" counter accept + ip daddr 127.0.0.1 counter accept + icmp type echo-request limit rate 5/second accept + ip6 saddr ::1 ip6 daddr ::1 counter accept + icmpv6 type { echo-request, nd-router-advert, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept + ct state related,established counter accept + tcp dport ssh ct state new counter accept + rt type 0 counter drop + meta l4proto ipv6-icmp counter accept + } + chain forward { + type filter hook forward priority 0; policy accept; + } + chain output { + type filter hook output priority 0; policy accept; + } +} diff --git a/gardenlinux/features/firewall/info.yaml b/gardenlinux/features/firewall/info.yaml new file mode 100644 index 0000000..b6f3ca4 --- /dev/null +++ b/gardenlinux/features/firewall/info.yaml @@ -0,0 +1,4 @@ +# This subfeature can only be used with +# its base feature: [cis, fedramp] +description: "Garden Linux Firewall" +type: element diff --git a/gardenlinux/features/firewall/pkg.include b/gardenlinux/features/firewall/pkg.include new file mode 100644 index 0000000..bf1c9d6 --- /dev/null +++ b/gardenlinux/features/firewall/pkg.include @@ -0,0 +1 @@ +nftables \ No newline at end of file diff --git a/gardenlinux/features/firewall/test/test_nft_config.py b/gardenlinux/features/firewall/test/test_nft_config.py new file mode 100644 index 0000000..f5dc5f5 --- /dev/null +++ b/gardenlinux/features/firewall/test/test_nft_config.py @@ -0,0 +1,8 @@ +from helper.utils import execute_remote_command + + +def test_nft_config(client, non_chroot): + # This will already fail if table "inet" is missing + out = execute_remote_command(client, "nft list table inet filter") + # Validate that input has policy drop as default + assert "type filter hook input priority filter; policy drop;" in out diff --git a/gardenlinux/features/firewall/test/test_packages_musthave.py b/gardenlinux/features/firewall/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/firewall/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/firewall/test/test_systemd_units.py b/gardenlinux/features/firewall/test/test_systemd_units.py new file mode 100644 index 0000000..4885df3 --- /dev/null +++ b/gardenlinux/features/firewall/test/test_systemd_units.py @@ -0,0 +1,16 @@ +import pytest +from helper.utils import execute_remote_command +from helper.utils import validate_systemd_unit + + +@pytest.mark.parametrize( + "systemd_unit", + [ + "nftables" + ] +) + + +def test_systemd_unit(client, systemd_unit, non_chroot): + execute_remote_command(client, f"systemctl start {systemd_unit}") + validate_systemd_unit(client, f"{systemd_unit}") diff --git a/gardenlinux/features/gcp/README.md b/gardenlinux/features/gcp/README.md new file mode 100644 index 0000000..c1aa272 --- /dev/null +++ b/gardenlinux/features/gcp/README.md @@ -0,0 +1,36 @@ +## Feature: gcp +### Description + +This platform feature creates an artifact for the Google Cloud Platform. + + +### Features +This feature creates a GCP compatible image artifact as an `.raw` file. + +To be platform complaint smaller adjustments like defining the platform related clocksource config, networking config etc. are done. +The artifact includes `cloud-init` and `amazon-ec2-utils` for orchestrating the image. + +### Uploading Image to GCP + +- create compressed tar image (disk must be called disk.raw) + +```tar --format=oldgnu -Sczf compressed-image.tar.gz disk.raw``` + +- upload tar: + +```gsutil cp compressed-image.tar.gz gs://garden-linux-test``` + +- create image: + +```gcloud compute images create garden-linux-gcp-01 --source-uri gs://garden-linux-test/compressed-image.tar.gz``` + +### Unit testing +This platform feature supports unit testing and is based on the `gcp` fixture to validate the applied changes according its feature configuration. + +### Meta +||| +|---|---| +|type|platform| +|artifact|`.raw`| +|included_features|cloud| +|excluded_features|None| diff --git a/gardenlinux/features/gcp/convert.gcpimage.tar.gz b/gardenlinux/features/gcp/convert.gcpimage.tar.gz new file mode 100755 index 0000000..d691f6a --- /dev/null +++ b/gardenlinux/features/gcp/convert.gcpimage.tar.gz @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +tar --format=oldgnu -cSz --transform "s|$1|disk.raw|" "$1" > "$2" diff --git a/gardenlinux/features/gcp/exec.config b/gardenlinux/features/gcp/exec.config new file mode 100755 index 0000000..6f4eb38 --- /dev/null +++ b/gardenlinux/features/gcp/exec.config @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# set default timezone +ln -sf /usr/share/zoneinfo/UTC /etc/localtime + +# cannot execute startup script in /tmp, usr /var/tmp instead +sed -i 's/^run_dir =\s*/run_dir = \/var\/tmp/g' /etc/default/instance_configs.cfg + +cat >>/etc/ssh/sshd_conf <&2 +} + +####################################### +# Retrieves the device name for an NVMe namespace using nvme-cli. +# Globals: +# Uses nvme_cli_bin +# Arguments: +# The path to the nvme namespace (/dev/nvme0n?) +# Outputs: +# The device name parsed from the JSON in the vendor ext of the ns-id command. +# Returns: +# 0 if the device name for the namespace could be retrieved, 1 otherwise +####################################### +function get_namespace_device_name() { + local nvme_json + nvme_json="$("$nvme_cli_bin" id-ns -b "$1" | xxd -p -seek 384 | xxd -p -r)" + if [[ $? -ne 0 ]]; then + return 1 + fi + + if [[ -z "$nvme_json" ]]; then + err "NVMe Vendor Extension disk information not present" + return 1 + fi + + local device_name + device_name="$(echo "$nvme_json" | grep device_name | sed -e 's/.*"device_name":[ \t]*"\([a-zA-Z0-9_-]\+\)".*/\1/')" + + # Error if our device name is empty + if [[ -z "$device_name" ]]; then + err "Empty name" + return 1 + fi + + echo "$device_name" + return 0 +} + +####################################### +# Retrieves the nsid for an NVMe namespace +# Globals: +# None +# Arguments: +# The path to the nvme namespace (/dev/nvme0n*) +# Outputs: +# The namespace number/id +# Returns: +# 0 if the namespace id could be retrieved, 1 otherwise +####################################### +function get_namespace_number() { + local dev_path="$1" + local namespace_number + if [[ "$dev_path" =~ $NAMESPACE_NUMBER_REGEX ]]; then + namespace_number="${BASH_REMATCH[1]}" + else + return 1 + fi + + echo "$namespace_number" + return 0 +} + +####################################### +# Retrieves the partition number for a device path if it exists +# Globals: +# None +# Arguments: +# The path to the device partition (/dev/nvme0n*p*) +# Outputs: +# The value after 'p' in the device path, or an empty string if the path has +# no partition. +####################################### +function get_partition_number() { + local dev_path="$1" + local partition_number + if [[ "$dev_path" =~ $PARTITION_NUMBER_REGEX ]]; then + partition_number="${BASH_REMATCH[1]}" + echo "$partition_number" + else + echo '' + fi + return 0 +} + +####################################### +# Generates a symlink for a PD-NVMe device using the metadata's disk name. +# Primarily used for testing but can be used if the script is directly invoked. +# Globals: +# Uses ID_SERIAL_SHORT (can be populated by identify_pd_disk) +# Arguments: +# The device path for the disk +####################################### +function gen_symlink() { + local dev_path="$1" + local partition_number="$(get_partition_number "$dev_path")" + + if [[ -n "$partition_number" ]]; then + ln -s "$dev_path" /dev/disk/by-id/google-"$ID_SERIAL_SHORT"-part"$partition_number" > /dev/null 2>&1 + else + ln -s "$dev_path" /dev/disk/by-id/google-"$ID_SERIAL_SHORT" > /dev/null 2>&1 + fi + + return 0 +} + +####################################### +# Populates the ID_* global variables with a disk's device name and namespace +# Globals: +# Populates ID_SERIAL_SHORT, and ID_SERIAL +# Arguments: +# The device path for the disk +# Returns: +# 0 on success and 1 if an error occurrs +####################################### +function identify_pd_disk() { + local dev_path="$1" + local dev_name + dev_name="$(get_namespace_device_name "$dev_path")" + if [[ $? -ne 0 ]]; then + return 1 + fi + + ID_SERIAL_SHORT="$dev_name" + ID_SERIAL="Google_PersistentDisk_${ID_SERIAL_SHORT}" + return 0 +} + +function print_help_message() { + echo "Usage: google_nvme_id [-s] [-h] -d device_path" + echo " -d (Required): Specifies the path to generate a name" + echo " for. This needs to be a path to an nvme device or namespace" + echo " -s: Create symbolic link for the disk under /dev/disk/by-id." + echo " Otherwise, the disk name will be printed to STDOUT" + echo " -h: Print this help message" +} + +function main() { + local opt_gen_symlink='false' + local device_path='' + + while getopts :d:sh flag; do + case "$flag" in + d) device_path="$OPTARG";; + s) opt_gen_symlink='true';; + h) print_help_message + return 0 + ;; + :) echo "Invalid option: ${OPTARG} requires an argument" 1>&2 + return 1 + ;; + *) return 1 + esac + done + + if [[ -z "$device_path" ]]; then + echo "Device path (-d) argument required. Use -h for full usage." 1>&2 + exit 1 + fi + + # Ensure the nvme-cli command is installed + command -v "$nvme_cli_bin" > /dev/null 2>&1 + if [[ $? -ne 0 ]]; then + err "The nvme utility (/usr/sbin/nvme) was not found. You may need to run \ +with sudo or install nvme-cli." + return 1 + fi + + # Ensure the passed device is actually an NVMe device + "$nvme_cli_bin" id-ctrl "$device_path" &>/dev/null + if [[ $? -ne 0 ]]; then + err "Passed device was not an NVMe device. (You may need to run this \ +script as root/with sudo)." + return 1 + fi + + # Detect the type of attached nvme device + local controller_id + controller_id=$("$nvme_cli_bin" id-ctrl "$device_path") + if [[ ! "$controller_id" =~ nvme_card-pd ]] ; then + err "Device is not a PD-NVMe device" + return 1 + fi + + # Fill the global variables for the id command for the given disk type + # Error messages will be printed closer to error, no need to reprint here + identify_pd_disk "$device_path" + if [[ $? -ne 0 ]]; then + return $? + fi + + # Gen symlinks or print out the globals set by the identify command + if [[ "$opt_gen_symlink" == 'true' ]]; then + gen_symlink "$device_path" + else + # These will be consumed by udev + echo "ID_SERIAL_SHORT=${ID_SERIAL_SHORT}" + echo "ID_SERIAL=${ID_SERIAL}" + fi + + return $? + +} +main "$@" diff --git a/gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/64-gce-disk-removal.rules b/gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/64-gce-disk-removal.rules new file mode 100644 index 0000000..dee719a --- /dev/null +++ b/gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/64-gce-disk-removal.rules @@ -0,0 +1,17 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# When a disk is removed, unmount any remaining attached volumes. + +ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd*|vd*|nvme*", RUN+="/bin/sh -c '/bin/umount -fl /dev/$name && /usr/bin/logger -p daemon.warn -s WARNING: hot-removed /dev/$name that was still mounted, data may have been corrupted'" diff --git a/gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/65-gce-disk-naming.rules b/gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/65-gce-disk-naming.rules new file mode 100644 index 0000000..6af9b3d --- /dev/null +++ b/gardenlinux/features/gcp/file.include/usr/lib/udev/rules.d/65-gce-disk-naming.rules @@ -0,0 +1,34 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Name the attached disks as the specified by deviceName. + +ACTION!="add|change", GOTO="gce_disk_naming_end" +SUBSYSTEM!="block", GOTO="gce_disk_naming_end" + +# SCSI naming +KERNEL=="sd*|vd*", IMPORT{program}="scsi_id --export --whitelisted -d $tempnode" + +# NVME Local SSD naming +KERNEL=="nvme*n*", ATTRS{model}=="nvme_card", PROGRAM="/bin/sh -c 'nsid=$$(echo %k|sed -re s/nvme[0-9]+n\([0-9]+\).\*/\\1/); echo $$((nsid-1))'", ENV{ID_SERIAL_SHORT}="local-nvme-ssd-%c" +KERNEL=="nvme*", ATTRS{model}=="nvme_card", ENV{ID_SERIAL}="Google_EphemeralDisk_$env{ID_SERIAL_SHORT}" + +# NVME Persistent Disk Naming +KERNEL=="nvme*n*", ATTRS{model}=="nvme_card-pd", IMPORT{program}="google_nvme_id -d $tempnode" + +# Symlinks +KERNEL=="sd*|vd*|nvme*", ENV{DEVTYPE}=="disk", SYMLINK+="disk/by-id/google-$env{ID_SERIAL_SHORT}" +KERNEL=="sd*|vd*|nvme*", ENV{DEVTYPE}=="partition", SYMLINK+="disk/by-id/google-$env{ID_SERIAL_SHORT}-part%n" + +LABEL="gce_disk_naming_end" diff --git a/gardenlinux/features/gcp/info.yaml b/gardenlinux/features/gcp/info.yaml new file mode 100644 index 0000000..06da9f5 --- /dev/null +++ b/gardenlinux/features/gcp/info.yaml @@ -0,0 +1,5 @@ +description: "Google Compute Engine platform (gce guest environment)" +type: platform +features: + include: + - cloud diff --git a/gardenlinux/features/gcp/pkg.exclude b/gardenlinux/features/gcp/pkg.exclude new file mode 100644 index 0000000..77617c0 --- /dev/null +++ b/gardenlinux/features/gcp/pkg.exclude @@ -0,0 +1 @@ +irqbalance diff --git a/gardenlinux/features/gcp/pkg.include b/gardenlinux/features/gcp/pkg.include new file mode 100644 index 0000000..130cf6a --- /dev/null +++ b/gardenlinux/features/gcp/pkg.include @@ -0,0 +1,3 @@ +google-compute-engine-oslogin +google-guest-agent +nvme-cli diff --git a/gardenlinux/features/gcp/test/test_blacklisted_packages.py b/gardenlinux/features/gcp/test/test_blacklisted_packages.py new file mode 100644 index 0000000..ac5cbdd --- /dev/null +++ b/gardenlinux/features/gcp/test/test_blacklisted_packages.py @@ -0,0 +1,11 @@ +from helper.tests.blacklisted_packages import blacklisted_packages +import pytest + +@pytest.mark.parametrize( + "package", + [ + "irqbalance" + ] +) +def test_blacklisted_packages(client, package): + blacklisted_packages(client, package) diff --git a/gardenlinux/features/gcp/test/test_packages_musthave.py b/gardenlinux/features/gcp/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/gcp/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/gcp/test/test_users.py b/gardenlinux/features/gcp/test/test_users.py new file mode 100644 index 0000000..719fefc --- /dev/null +++ b/gardenlinux/features/gcp/test/test_users.py @@ -0,0 +1,6 @@ +from helper.tests.users import users + +additional_user="gardenlinux" + +def test_users(client, gcp): + users(client=client, additional_user=additional_user, additional_sudo_users=[additional_user]) diff --git a/gardenlinux/features/kvm/README.md b/gardenlinux/features/kvm/README.md new file mode 100644 index 0000000..1f1b822 --- /dev/null +++ b/gardenlinux/features/kvm/README.md @@ -0,0 +1,20 @@ +## Feature: kvm +### Description + +This platform feature creates an artifact for KVM/QEMU. + + +### Features +This feature creates a KVM/QEMU compatible image artifact as a `.qcow2` file. + +### Unit testing +This platform feature supports unit testing and is based on the `kvm` fixture to validate the applied changes according its feature configuration. +Next to this, `kvm` fixture is the base platform to perform local unit tests that may require a running OS. + +### Meta +||| +|---|---| +|type|platform| +|artifact|`.raw`,`.qcow2`| +|included_features|`server`| +|excluded_features|None| diff --git a/gardenlinux/features/kvm/exec.config b/gardenlinux/features/kvm/exec.config new file mode 100755 index 0000000..cb66245 --- /dev/null +++ b/gardenlinux/features/kvm/exec.config @@ -0,0 +1 @@ +systemctl enable ignition-disable.service diff --git a/gardenlinux/features/kvm/file.exclude b/gardenlinux/features/kvm/file.exclude new file mode 100644 index 0000000..1061173 --- /dev/null +++ b/gardenlinux/features/kvm/file.exclude @@ -0,0 +1,4 @@ +/initrd.img.old +/initrd.img +/vmlinuz.old +/vmlinuz diff --git a/gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/10-console.cfg b/gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/10-console.cfg new file mode 100644 index 0000000..a40d0b0 --- /dev/null +++ b/gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/10-console.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="console=tty0 console=ttyS0 $CMDLINE_LINUX" diff --git a/gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/50-ignition.cfg b/gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/50-ignition.cfg new file mode 100644 index 0000000..6b25fd4 --- /dev/null +++ b/gardenlinux/features/kvm/file.include/etc/kernel/cmdline.d/50-ignition.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="$CMDLINE_LINUX ignition.firstboot=1 ignition.platform.id=qemu" diff --git a/gardenlinux/features/kvm/file.include/etc/systemd/system/ignition-disable.service b/gardenlinux/features/kvm/file.include/etc/systemd/system/ignition-disable.service new file mode 100644 index 0000000..1ad9530 --- /dev/null +++ b/gardenlinux/features/kvm/file.include/etc/systemd/system/ignition-disable.service @@ -0,0 +1,24 @@ +[Unit] +# Based on https://github.com/coreos/ignition/blob/main/systemd/ignition-delete-config.service +Description=Ignition disable after first boot + +ConditionFirstBoot=true +ConditionPathExists=/run/ignition.env +RequiresMountsFor=/boot/efi + +DefaultDependencies=no +Before=sysinit.target +After=systemd-tmpfiles-setup.service + +# TODO : do we really need to fail to boot? +OnFailure=emergency.target +OnFailureJobMode=isolate + +[Service] +Type=oneshot +EnvironmentFile=/run/ignition.env +ExecStart=/bin/bash -c "rm -f /etc/kernel/cmdline.d/50-ignition.cfg && /usr/local/sbin/update-bootloaders" +RemainAfterExit=yes + +[Install] +WantedBy=sysinit.target diff --git a/gardenlinux/features/kvm/file.include/etc/udev/rules.d/60-onmetal.rules b/gardenlinux/features/kvm/file.include/etc/udev/rules.d/60-onmetal.rules new file mode 100644 index 0000000..d6e82ce --- /dev/null +++ b/gardenlinux/features/kvm/file.include/etc/udev/rules.d/60-onmetal.rules @@ -0,0 +1,13 @@ +# e.g. on guest vdc with serial oda-abcdefabcdefabcd +# /dev/vdc +# symlink /dev/onmetal/oda -> /dev/vdc +# symlink /dev/disk/by-id/wwn-abcdefabcdefabcd -> /dev/vdc +# +# /dev/vdc3 +# symlink /dev/onmetal/oda3 -> /dev/vdc3 +# symlink /dev/disk/by-id/wwn-abcdefabcdefabcd-part3 -> /dev/vdc3 + +KERNEL=="*d*[!0-9]", ATTRS{serial}=="[a-z]d[a-z]-[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]", PROGRAM="/bin/sh -ec 'SERIAL=$attr{serial}; echo $${SERIAL%%-*} $${SERIAL#*-}'", SYMLINK+="onmetal/$result{1}", SYMLINK+="disk/by-id/wwn-$result{2}" + +# partition +KERNEL=="*d*[0-9]", ATTRS{serial}=="[a-z]d[a-z]-[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]", PROGRAM="/bin/sh -ec 'SERIAL=$attr{serial}; echo $${SERIAL%%-*} $${SERIAL#*-}'", SYMLINK+="onmetal/$result{1}%n", SYMLINK+="disk/by-id/wwn-$result{2}-part%n" diff --git a/gardenlinux/features/kvm/file.include/usr/local/sbin/update-bootloaders b/gardenlinux/features/kvm/file.include/usr/local/sbin/update-bootloaders new file mode 100755 index 0000000..7b931fa --- /dev/null +++ b/gardenlinux/features/kvm/file.include/usr/local/sbin/update-bootloaders @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +update-kernel-cmdline +update-syslinux +for kernel in /boot/vmlinuz-*; do + kernel-install add "${kernel#*-}" "${kernel}" +done diff --git a/gardenlinux/features/kvm/info.yaml b/gardenlinux/features/kvm/info.yaml new file mode 100644 index 0000000..1e7f08a --- /dev/null +++ b/gardenlinux/features/kvm/info.yaml @@ -0,0 +1,6 @@ +description: "kvm and qemu platform" +type: platform +features: + include: + - cloud + - _ignite diff --git a/gardenlinux/features/kvm/pkg.include b/gardenlinux/features/kvm/pkg.include new file mode 100644 index 0000000..83cd440 --- /dev/null +++ b/gardenlinux/features/kvm/pkg.include @@ -0,0 +1,2 @@ +dosfstools +qemu-guest-agent diff --git a/gardenlinux/features/kvm/test/capabilities.d/capabilities.list b/gardenlinux/features/kvm/test/capabilities.d/capabilities.list new file mode 100644 index 0000000..249cef4 --- /dev/null +++ b/gardenlinux/features/kvm/test/capabilities.d/capabilities.list @@ -0,0 +1 @@ +/usr/bin/arping cap_net_raw=ep diff --git a/gardenlinux/features/kvm/test/test_packages_musthave.py b/gardenlinux/features/kvm/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/kvm/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/log/exec.config b/gardenlinux/features/log/exec.config new file mode 100755 index 0000000..0616fa4 --- /dev/null +++ b/gardenlinux/features/log/exec.config @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +systemctl enable systemd-journald-audit.socket +systemctl disable rsyslog.service \ No newline at end of file diff --git a/gardenlinux/features/log/file.include.stat b/gardenlinux/features/log/file.include.stat new file mode 100644 index 0000000..e89aac4 --- /dev/null +++ b/gardenlinux/features/log/file.include.stat @@ -0,0 +1 @@ +root root 0640 /etc/audit/rules.d/*-*.rules diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/10-base-config.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/10-base-config.rules new file mode 100644 index 0000000..b86d66f --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/10-base-config.rules @@ -0,0 +1,13 @@ +## First rule - delete all +-D + +## Increase the buffers to survive stress events. +## Make this bigger for busy systems +-b 8192 + +## This determine how long to wait in burst of events +--backlog_wait_time 60000 + +## Set failure mode to syslog +-f 1 + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/10-no-audit.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/10-no-audit.rules new file mode 100644 index 0000000..99b82f7 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/10-no-audit.rules @@ -0,0 +1,9 @@ +## This set of rules is to suppress the performance effects of the +## audit system. The result is that you only get hardwired events. +-D + +## This suppresses syscall auditing for all tasks started +## with this rule in effect. Remove it if you need syscall +## auditing. +-a task,never + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/11-loginuid.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/11-loginuid.rules new file mode 100644 index 0000000..9b0a3e9 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/11-loginuid.rules @@ -0,0 +1,3 @@ +## Make the loginuid immutable. This prevents tampering with the auid. +--loginuid-immutable + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/12-cont-fail.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/12-cont-fail.rules new file mode 100644 index 0000000..d5ef0ad --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/12-cont-fail.rules @@ -0,0 +1,7 @@ +## This rule will cause auditctl to continue loading rules when it runs +## across an unsupported field or a rule with a syntax error however it will +## report an error at exit. The normal action is to report the line and +## issue with the rule and exit immediately with an error to get the admin's +## attention to fix the rules. + +-c diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/12-ignore-error.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/12-ignore-error.rules new file mode 100644 index 0000000..ad84db9 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/12-ignore-error.rules @@ -0,0 +1,7 @@ +## This rule will cause auditctl to continue loading rules when it runs +## across an unsupported field or a rule with a syntax error but exit with +## success reason code. The normal action is to report the line and issue with +## the rule and exit immediately with an error to get the admin's attention to +## fix the rules. + +-i diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/20-dont-audit.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/20-dont-audit.rules new file mode 100644 index 0000000..6d43d07 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/20-dont-audit.rules @@ -0,0 +1,13 @@ +## This is for don't audit rules. We put these early because audit +### is a first match wins system. Uncomment the rules you want. + +## Cron jobs fill the logs with stuff we normally don't want +#-a never,user -F subj_type=crond_t + +## This prevents chrony from overwhelming the logs +#-a never,exit -F arch=x86_64 -S adjtimex -F auid=unset -F uid=chrony -F subj_type=chronyd_t + +### This is not very interesting and wastes a lot of space if +### the server is public facing +#-a always,exclude -F msgtype=CRYPTO_KEY_USER + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/21-no32bit.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/21-no32bit.rules new file mode 100644 index 0000000..9f5eca5 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/21-no32bit.rules @@ -0,0 +1,5 @@ +## If you are on a 64 bit platform, everything _should_ be running +## in 64 bit mode. This rule will detect any use of the 32 bit syscalls +## because this might be a sign of someone exploiting a hole in the 32 +## bit API. +-a always,exit -F arch=b32 -S all -F key=32bit-abi diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/22-ignore-chrony.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/22-ignore-chrony.rules new file mode 100644 index 0000000..2318909 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/22-ignore-chrony.rules @@ -0,0 +1,3 @@ +## This rule suppresses the time-change event when chrony does time updates +-a never,exit -F arch=b64 -S adjtimex -F auid=unset -F uid=chrony -F subj_type=chronyd_t +-a never,exit -F arch=b32 -S adjtimex -F auid=unset -F uid=chrony -F subj_type=chronyd_t diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/23-ignore-filesystems.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/23-ignore-filesystems.rules new file mode 100644 index 0000000..aae5989 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/23-ignore-filesystems.rules @@ -0,0 +1,8 @@ +# This rule suppresses events that originate on the below file systems. +# Typically you would use this in conjunction with rules to monitor +# kernel modules. The filesystem listed are known to cause hundreds of +# path records during kernel module load. As an aside, if you do see the +# tracefs or debugfs module load and this is a production system, you really +# should look into why its getting loaded and prevent it if possible. +-a never,filesystem -F fstype=tracefs +-a never,filesystem -F fstype=debugfs diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-nispom.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-nispom.rules new file mode 100644 index 0000000..e3873ef --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-nispom.rules @@ -0,0 +1,94 @@ +## This file contains a sample audit configuration intended to +## meet the NISPOM Chapter 8 rules. This rule depends on having +## 10-base-config.rules & 99-finalize.rules installed. + +## Audit 1, 1(a) Enough information to determine the date and time of +## action (e.g., common network time), the system locale of the action, +## the system entity that initiated or completed the action, the resources +## involved, and the action involved. + +## Things that could affect time +-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=time-change +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=time-change +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=time-change +-w /etc/localtime -p wa -k time-change + +## Things that could affect system locale +-a always,exit -F arch=b32 -S sethostname,setdomainname -F key=system-locale +-a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale +-w /etc/issue -p wa -k system-locale +-w /etc/issue.net -p wa -k system-locale +-w /etc/hosts -p wa -k system-locale +-w /etc/hostname -p wa -k system-locale +-w /etc/sysconfig/network -p wa -k system-locale +-a always,exit -F dir=/etc/NetworkManager/ -F perm=wa -F key=system-locale + +## Audit 1, 1(b) Successful and unsuccessful logons and logoffs. +## This is covered by patches to login, gdm, and openssh +## Might also want to watch these files if needing extra information +#-w /var/log/tallylog -p wa -k logins +#-w /var/run/faillock/ -p wa -k logins +#-w /var/log/lastlog -p wa -k logins +#-w /var/log/btmp -p wa -k logins +#-w /var/run/utmp -p wa -k logins + +## Audit 1, 1(c) Successful and unsuccessful accesses to +## security-relevant objects and directories, including +## creation, open, close, modification, and deletion. + +## unsuccessful creation +-a always,exit -F arch=b32 -S creat,link,mknod,mkdir,symlink,mknodat,linkat,symlinkat -F exit=-EACCES -F key=creation +-a always,exit -F arch=b64 -S mkdir,creat,link,symlink,mknod,mknodat,linkat,symlinkat -F exit=-EACCES -F key=creation +-a always,exit -F arch=b32 -S link,mkdir,symlink,mkdirat -F exit=-EPERM -F key=creation +-a always,exit -F arch=b64 -S mkdir,link,symlink,mkdirat -F exit=-EPERM -F key=creation + +## unsuccessful open +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F key=open +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F key=open +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F key=open +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F key=open + +## unsuccessful close +-a always,exit -F arch=b32 -S close -F exit=-EIO -F key=close +-a always,exit -F arch=b64 -S close -F exit=-EIO -F key=close + +## unsuccessful modifications +-a always,exit -F arch=b32 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EACCES -F key=mods +-a always,exit -F arch=b64 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EACCES -F key=mods +-a always,exit -F arch=b32 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EPERM -F key=mods +-a always,exit -F arch=b64 -S rename -S renameat -S truncate -S chmod -S setxattr -S lsetxattr -S removexattr -S lremovexattr -F exit=-EPERM -F key=mods + +## unsuccessful deletion +-a always,exit -F arch=b32 -S unlink,rmdir,unlinkat -F exit=-EACCES -F key=delete +-a always,exit -F arch=b64 -S rmdir,unlink,unlinkat -F exit=-EACCES -F key=delete +-a always,exit -F arch=b32 -S unlink,rmdir,unlinkat -F exit=-EPERM -F key=delete +-a always,exit -F arch=b64 -S rmdir,unlink,unlinkat -F exit=-EPERM -F key=delete + +## Audit 1, 1(d) Changes in user authenticators. +## Covered by patches to libpam, passwd, and shadow-utils +## Might also want to watch these files for changes +-w /etc/group -p wa -k auth +-w /etc/passwd -p wa -k auth +-w /etc/gshadow -p wa -k auth +-w /etc/shadow -p wa -k auth +-w /etc/security/opasswd -p wa -k auth + +## Audit 1, 1(e) The blocking or blacklisting of a user ID, +## terminal, or access port and the reason for the action. +## Covered by patches to pam_tally2 or pam_faillock and pam_limits + +## Audit 1, 1(f) Denial of access resulting from an excessive +## number of unsuccessful logon attempts. +## Covered by patches to pam_tally2 or pam_faillock + +## Audit 1, 2 Audit Trail Protection. The contents of audit trails +## shall be protected against unauthorized access, modification, +## or deletion. +## This should be covered by file permissions, but we can watch it +## to see any activity +-w /var/log/audit/ -k audit-logs + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-failed.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-failed.rules new file mode 100644 index 0000000..6aca1b9 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-failed.rules @@ -0,0 +1,13 @@ +## Unsuccessful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b32 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create +-a always,exit -F arch=b64 -S creat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-create diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-success.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-success.rules new file mode 100644 index 0000000..4141e3c --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-1-create-success.rules @@ -0,0 +1,7 @@ +## Successful file creation (open with O_CREAT) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S open -F a1&0100 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b32 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create +-a always,exit -F arch=b64 -S creat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-create diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules new file mode 100644 index 0000000..ffe5bfd --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-failed.rules @@ -0,0 +1,13 @@ +## Unsuccessful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-modification diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-success.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-success.rules new file mode 100644 index 0000000..5617e01 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-2-modify-success.rules @@ -0,0 +1,7 @@ +## Successful file modifications (open for write or truncate) +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S open -F a1&01003 -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b32 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification +-a always,exit -F arch=b64 -S truncate,ftruncate -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-modification diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-failed.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-failed.rules new file mode 100644 index 0000000..39ac7a8 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-failed.rules @@ -0,0 +1,5 @@ +## Unsuccessful file access (any other opens) This has to go last. +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-success.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-success.rules new file mode 100644 index 0000000..79004ce --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-3-access-success.rules @@ -0,0 +1,4 @@ +## Successful file access (any other opens) This has to go last. +## These next two are likely to result in a whole lot of events +-a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access +-a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules new file mode 100644 index 0000000..946c9cc --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-failed.rules @@ -0,0 +1,5 @@ +## Unsuccessful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-delete diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-success.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-success.rules new file mode 100644 index 0000000..7955cdf --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-4-delete-success.rules @@ -0,0 +1,3 @@ +## Successful file delete +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-delete diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules new file mode 100644 index 0000000..49b9299 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-failed.rules @@ -0,0 +1,5 @@ +## Unsuccessful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-perm-change diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules new file mode 100644 index 0000000..52cbac8 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-5-perm-change-success.rules @@ -0,0 +1,3 @@ +## Successful permission change +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat,setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-perm-change diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules new file mode 100644 index 0000000..44e7148 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-failed.rules @@ -0,0 +1,5 @@ +## Unsuccessful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-owner-change diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules new file mode 100644 index 0000000..056b706 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42-6-owner-change-success.rules @@ -0,0 +1,3 @@ +## Successful ownership change +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change +-a always,exit -F arch=b64 -S lchown,fchown,chown,fchownat -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-owner-change diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42.rules new file mode 100644 index 0000000..1af73b3 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-ospp-v42.rules @@ -0,0 +1,90 @@ +## The purpose of these rules is to meet the requirements for Operating +## System Protection Profile (OSPP)v4.2. These rules depends on having +## the following rule files copied to /etc/audit/rules.d: +## +## 10-base-config.rules, 11-loginuid.rules, +## 30-ospp-v42-1-create-failed.rules, 30-ospp-v42-1-create-success.rules, +## 30-ospp-v42-2-modify-failed.rules, 30-ospp-v42-2-modify-success.rules, +## 30-ospp-v42-3-access-failed.rules, 30-ospp-v42-3-access-success.rules, +## 30-ospp-v42-4-delete-failed.rules, 30-ospp-v42-4-delete-success.rules, +## 30-ospp-v42-5-perm-change-failed.rules, +## 30-ospp-v42-5-perm-change-success.rules, +## 30-ospp-v42-6-owner-change-failed.rules, +## 30-ospp-v42-6-owner-change-success.rules +## +## original copies may be found in /usr/share/audit/sample-rules/ + + +## User add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch passwd and +## shadow for writes +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/passwd -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S openat,open_by_handle_at -F a2&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b32 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F arch=b64 -S open -F a1&03 -F path=/etc/shadow -F auid>=1000 -F auid!=unset -F key=user-modify + +## User enable and disable. This is entirely handled by pam. + +## Group add delete modify. This is covered by pam. However, someone could +## open a file and directly create or modify a user, so we'll watch group and +## gshadow for writes +-a always,exit -F path=/etc/passwd -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/shadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=user-modify +-a always,exit -F path=/etc/group -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify +-a always,exit -F path=/etc/gshadow -F perm=wa -F auid>=1000 -F auid!=unset -F key=group-modify + + +## Use of special rights for config changes. This would be use of setuid +## programs that relate to user accts. This is not all setuid apps because +## requirements are only for ones that affect system configuration. +-a always,exit -F path=/usr/sbin/unix_chkpwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/usernetctl -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/userhelper -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/seunshare -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/mount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgrp -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newuidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/gpasswd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/newgidmap -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/umount -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/passwd -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/crontab -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/bin/at -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes +-a always,exit -F path=/usr/sbin/grub2-set-bootflag -F perm=x -F auid>=1000 -F auid!=unset -F key=special-config-changes + +## Privilege escalation via su or sudo. This is entirely handled by pam. +## Special case for systemd-run. It is not audit aware, specifically watch it +-a always,exit -F path=/usr/bin/systemd-run -F perm=x -F auid!=unset -F key=maybe-escalation +## Special case for pkexec. It is not audit aware, specifically watch it +-a always,exit -F path=/usr/bin/pkexec -F perm=x -F key=maybe-escalation + + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=special-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=special-config-changes + +## Audit log access +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=access-audit-trail +## Attempts to Alter Process and Session Initiation Information +-a always,exit -F path=/var/run/utmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/btmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session +-a always,exit -F path=/var/log/wtmp -F perm=wa -F auid>=1000 -F auid!=unset -F key=session + +## Attempts to modify MAC controls +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F auid>=1000 -F auid!=unset -F key=MAC-policy + +## Software updates. This is entirely handled by rpm. + +## System start and shutdown. This is entirely handled by systemd + +## Kernel Module loading. This is handled in 43-module-load.rules + +## Application invocation. The requirements list an optional requirement +## FPT_SRP_EXT.1 Software Restriction Policies. This event is intended to +## state results from that policy. This would be handled entirely by +## that daemon. + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-pci-dss-v31.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-pci-dss-v31.rules new file mode 100644 index 0000000..8101cd6 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-pci-dss-v31.rules @@ -0,0 +1,120 @@ +## The purpose of these rules is to meet the pci-dss v3.1 auditing requirements +## These rules depends on having 10-base-config.rules & 99-finalize.rules +## installed. + +## NOTE: +## 1) if this is being used on a 32 bit machine, comment out the b64 lines +## 2) These rules assume that login under the root account is not allowed. +## 3) It is also assumed that 1000 represents the first usable user account. To +## be sure, look at UID_MIN in /etc/login.defs. +## 4) If these rules generate too much spurious data for your tastes, limit the +## syscall file rules with a directory, like -F dir=/etc +## 5) You can search for the results on the key fields in the rules +## + + +## 10.1 Implement audit trails to link all access to individual user. +## This requirement is implicitly met + +## 10.2.1 Implement audit trails to detect user accesses to cardholder data +## This would require a watch on the database that excludes the daemon's +## access. This rule is commented out due to needing a path name +#-a always,exit -F path=path-to-db -F auid>=1000 -F auid!=unset -F uid!=daemon-acct -F perm=r -F key=10.2.1-cardholder-access + +## 10.2.2 Log administrative action. To meet this, you need to enable tty +## logging. The pam config below should be placed into su and sudo pam stacks. +## session required pam_tty_audit.so disable=* enable=root + +## Special case for systemd-run. It is not audit aware, specifically watch it +-a always,exit -F path=/usr/bin/systemd-run -F perm=x -F auid!=unset -F key=maybe-escalation +## Special case for pkexec. It is not audit aware, specifically watch it +-a always,exit -F path=/usr/bin/pkexec -F perm=x -F key=maybe-escalation + +## Watch for configuration changes to privilege escalation. +-a always,exit -F path=/etc/sudoers -F perm=wa -F key=10.2.2-priv-config-changes +-a always,exit -F dir=/etc/sudoers.d/ -F perm=wa -F key=10.2.2-priv-config-changes + +## 10.2.3 Access to all audit trails. +-a always,exit -F dir=/var/log/audit/ -F perm=r -F auid>=1000 -F auid!=unset -F key=10.2.3-access-audit-trail +-a always,exit -F path=/usr/sbin/ausearch -F perm=x -F key=10.2.3-access-audit-trail +-a always,exit -F path=/usr/sbin/aureport -F perm=x -F key=10.2.3-access-audit-trail +-a always,exit -F path=/usr/sbin/aulast -F perm=x -F key=10.2.3-access-audit-trail +-a always,exit -F path=/usr/sbin/aulastlogin -F perm=x -F key=10.2.3-access-audit-trail +-a always,exit -F path=/usr/sbin/auvirt -F perm=x -F key=10.2.3-access-audit-trail + +## 10.2.4 Invalid logical access attempts. This is naturally met by pam. You +## can find these events with: ausearch --start today -m user_login -sv no -i + +## 10.2.5.a Use of I&A mechanisms is logged. Pam naturally handles this. +## you can find the events with: +## ausearch --start today -m user_auth,user_chauthtok -i + +## 10.2.5.b All elevation of privileges is logged +-a always,exit -F arch=b64 -S setuid -F a0=0 -F exe=/usr/bin/su -F key=10.2.5.b-elevated-privs-session +-a always,exit -F arch=b32 -S setuid -F a0=0 -F exe=/usr/bin/su -F key=10.2.5.b-elevated-privs-session +-a always,exit -F arch=b64 -S setresuid -F a0=0 -F exe=/usr/bin/sudo -F key=10.2.5.b-elevated-privs-session +-a always,exit -F arch=b32 -S setresuid -F a0=0 -F exe=/usr/bin/sudo -F key=10.2.5.b-elevated-privs-session +-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -F key=10.2.5.b-elevated-privs-setuid +-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -F key=10.2.5.b-elevated-privs-setuid + +## 10.2.5.c All changes, additions, or deletions to any account are logged +## This is implicitly covered by shadow-utils. We will place some rules +## in case someone tries to hand edit the trusted databases +-a always,exit -F path=/etc/group -F perm=wa -F key=10.2.5.c-accounts +-a always,exit -F path=/etc/passwd -F perm=wa -F key=10.2.5.c-accounts +-a always,exit -F path=/etc/gshadow -F perm=wa -F key=10.2.5.c-accounts +-a always,exit -F path=/etc/shadow -F perm=wa -F key=10.2.5.c-accounts +-a always,exit -F path=/etc/security/opasswd -F perm=wa -F key=10.2.5.c-accounts + + +## 10.2.6 Verify the following are logged: +## Initialization of audit logs +## Stopping or pausing of audit logs. +## These are handled implicitly by auditd + +## 10.2.7 Creation and deletion of system-level objects +## This requirement seems to be database table related and not audit + +## 10.3 Record at least the following audit trail entries +## 10.3.1 through 10.3.6 are implicitly met by the audit system. + +## 10.4.2b Time data is protected. +## We will place rules to check time synchronization +-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=10.4.2b-time-change +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=10.4.2b-time-change +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=10.4.2b-time-change +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=10.4.2b-time-change +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=10.4.2b-time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=10.4.2b-time-change +-w /etc/localtime -p wa -k 10.4.2b-time-change + +## 10.5 Secure audit trails so they cannot be altered +## The audit system protects audit logs by virtue of being the root user. +## That means that no normal user can tamper with the audit trail. If for +## some reason you suspect that admins may be malicious or that their acct +## could be compromised, then enable the remote logging plugin and get the +## logs off the system to assure that there is an unaltered copy. + +## 10.5.1 Limit viewing of audit trails to those with a job-related need. +## The audit daemon by default limits viewing of the audit trail to root. +## If someone that is not an admin has a job related need to see logs, then +## create a unique group for people with this need and set the log_group +## configuration item in auditd.conf + +## 10.5.2 Protect audit trail files from unauthorized modifications. +## See discussion in 10.5 above + +## 10.5.3 Promptly back up audit trail files to a centralized log server +## See discussion in 10.5 above + +## 10.5.4 Write logs for external-facing technologies onto a secure, +## centralized, internal log serve +## See discussion in 10.5 above + +## 10.5.5 Use file-integrity monitoring or change-detection software on logs +-a always,exit -F dir=/var/log/audit/ -F perm=wa -F key=10.5.5-modification-audit + +## Feel free to add watches on other critical logs +# -a always,exit -F path=path-to-log -F perm=wa -F key=10.5.5-modification-log + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/30-stig.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-stig.rules new file mode 100644 index 0000000..32119cb --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/30-stig.rules @@ -0,0 +1,142 @@ +## The purpose of these rules is to meet the stig auditing requirements +## These rules depends on having 10-base-config.rules & 99-finalize.rules +## installed. + +## NOTE: +## 1) if this is being used on a 32 bit machine, comment out the b64 lines +## 2) These rules assume that login under the root account is not allowed. +## 3) It is also assumed that 1000 represents the first usable user account. To +## be sure, look at UID_MIN in /etc/login.defs. +## 4) If these rules generate too much spurious data for your tastes, limit the +## syscall file rules with a directory, like -F dir=/etc +## 5) You can search for the results on the key fields in the rules +## +## +## (GEN002880: CAT II) The IAO will ensure the auditing software can +## record the following for each audit event: +##- Date and time of the event +##- Userid that initiated the event +##- Type of event +##- Success or failure of the event +##- For I&A events, the origin of the request (e.g., terminal ID) +##- For events that introduce an object into a user’s address space, and +## for object deletion events, the name of the object, and in MLS +## systems, the object’s security level. +## +## Things that could affect time +-a always,exit -F arch=b32 -S adjtimex,settimeofday,stime -F key=time-change +-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=time-change +-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change +-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change +# Introduced in 2.6.39, commented out because it can make false positives +#-a always,exit -F arch=b32 -S clock_adjtime -F key=time-change +#-a always,exit -F arch=b64 -S clock_adjtime -F key=time-change +-w /etc/localtime -p wa -k time-change + +## Things that affect identity +-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 + +## Things that could affect system locale +-a always,exit -F arch=b32 -S sethostname,setdomainname -F key=system-locale +-a always,exit -F arch=b64 -S sethostname,setdomainname -F key=system-locale +-w /etc/issue -p wa -k system-locale +-w /etc/issue.net -p wa -k system-locale +-w /etc/hosts -p wa -k system-locale +-w /etc/hostname -p wa -k system-locale +-a always,exit -F dir=/etc/NetworkManager/ -F perm=wa -F key=system-locale + +## Things that could affect MAC policy +-a always,exit -F dir=/etc/selinux/ -F perm=wa -F key=MAC-policy + + +## (GEN002900: CAT III) The IAO will ensure audit files are retained at +## least one year; systems containing SAMI will be retained for five years. +## +## Site action - no action in config files + +## (GEN002920: CAT III) The IAO will ensure audit files are backed up +## no less than weekly onto a different system than the system being +## audited or backup media. +## +## Can be done with cron script + +## (GEN002700: CAT I) (Previously – G095) The SA will ensure audit data +## files have permissions of 640, or more restrictive. +## +## Done automatically by auditd + +## (GEN002720-GEN002840: CAT II) (Previously – G100-G106) The SA will +## configure the auditing system to audit the following events for all +## users and root: +## +## - Logon (unsuccessful and successful) and logout (successful) +## +## Handled by pam, sshd, login, and gdm +## Might also want to watch these files if needing extra information +#-w /var/log/tallylog -p wa -k logins +#-w /var/run/faillock/ -p wa -k logins +#-w /var/log/lastlog -p wa -k logins + + +##- Process and session initiation (unsuccessful and successful) +## +## The session initiation is audited by pam without any rules needed. +## Might also want to watch this file if needing extra information +#-w /var/run/utmp -p wa -k session +#-w /var/log/btmp -p wa -k session +#-w /var/log/wtmp -p wa -k session + +##- Discretionary access control permission modification (unsuccessful +## and successful use of chown/chmod) +-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S lchown,fchown,chown,fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod +-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod + +##- Unauthorized access attempts to files (unsuccessful) +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open,truncate,ftruncate,creat,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access +-a always,exit -F arch=b64 -S open,truncate,ftruncate,creat,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access + +##- Use of print command (unsuccessful and successful) + +##- Export to media (successful) +## You have to mount media before using it. You must disable all automounting +## so that its done manually in order to get the correct user requesting the +## export +-a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -F key=export +-a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -F key=export + +##- System startup and shutdown (unsuccessful and successful) + +##- Files and programs deleted by the user (successful and unsuccessful) +-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete +-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete + +##- All system administration actions +##- All security personnel actions +## +## Look for pam_tty_audit and add it to your login entry point's pam configs. +## If that is not found, use sudo which should be patched to record its +## commands to the audit system. Do not allow unrestricted root shells or +## sudo cannot record the action. +-w /etc/sudoers -p wa -k actions +-w /etc/sudoers.d/ -p wa -k actions + +## Special case for systemd-run. It is not audit aware, specifically watch it +-a always,exit -F path=/usr/bin/systemd-run -F perm=x -F auid!=unset -F key=maybe-escalation +## Special case for pkexec. It is not audit aware, specifically watch it +-a always,exit -F path=/usr/bin/pkexec -F perm=x -F key=maybe-escalation + +## (GEN002860: CAT II) (Previously – G674) The SA and/or IAO will +##ensure old audit logs are closed and new audit logs are started daily. +## +## Site action. Can be assisted by a cron job + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/31-privileged.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/31-privileged.rules new file mode 100644 index 0000000..4383866 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/31-privileged.rules @@ -0,0 +1,11 @@ +##- Use of privileged commands (unsuccessful and successful) +## You can run the following commands to generate the rules: +#find /bin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' > priv.rules +#find /sbin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' >> priv.rules +#find /usr/bin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' >> priv.rules +#find /usr/sbin -type f -perm -04000 2>/dev/null | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $1 }' >> priv.rules +#filecap /bin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> priv.rules +#filecap /sbin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> priv.rules +#filecap /usr/bin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> priv.rules +#filecap /usr/sbin 2>/dev/null | sed '1d' | awk '{ printf "-a always,exit -F path=%s -F perm=x -F auid>=1000 -F auid!=unset -F key=privileged\n", $2 }' >> priv.rules + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/32-power-abuse.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/32-power-abuse.rules new file mode 100644 index 0000000..9d11cd1 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/32-power-abuse.rules @@ -0,0 +1,4 @@ +## The purpose of this rule is to detect when an admin may be abusing power +## by looking in user's home dir. +-a always,exit -F dir=/home -F uid=0 -F auid>=1000 -F auid!=unset -C auid!=obj_uid -F key=power-abuse + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/40-local.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/40-local.rules new file mode 100644 index 0000000..7b90cde --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/40-local.rules @@ -0,0 +1,4 @@ +## Put your own watches after this point +# -a exit,always -F path=file -F perm=rwxa -F key=text +# -a exit,always -F dir=directory -F perm=rwxa -F key=text + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/41-containers.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/41-containers.rules new file mode 100644 index 0000000..73e1287 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/41-containers.rules @@ -0,0 +1,9 @@ +## Use these rules if you want to log container events +## watch for container creation +-a always,exit -F arch=b32 -S clone -F a0&0x7C020000 -F key=container-create +-a always,exit -F arch=b64 -S clone -F a0&0x7C020000 -F key=container-create + +## watch for containers that may change their configuration +-a always,exit -F arch=b32 -S unshare,setns -F key=container-config +-a always,exit -F arch=b64 -S unshare,setns -F key=container-config + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/42-injection.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/42-injection.rules new file mode 100644 index 0000000..fe90514 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/42-injection.rules @@ -0,0 +1,13 @@ +## These rules watch for code injection by the ptrace facility. +## This could indicate someone trying to do something bad or +## just debugging + +#-a always,exit -F arch=b32 -S ptrace -F key=tracing +-a always,exit -F arch=b64 -S ptrace -F key=tracing +-a always,exit -F arch=b32 -S ptrace -F a0=0x4 -F key=code-injection +-a always,exit -F arch=b64 -S ptrace -F a0=0x4 -F key=code-injection +-a always,exit -F arch=b32 -S ptrace -F a0=0x5 -F key=data-injection +-a always,exit -F arch=b64 -S ptrace -F a0=0x5 -F key=data-injection +-a always,exit -F arch=b32 -S ptrace -F a0=0x6 -F key=register-injection +-a always,exit -F arch=b64 -S ptrace -F a0=0x6 -F key=register-injection + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/43-module-load.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/43-module-load.rules new file mode 100644 index 0000000..8907507 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/43-module-load.rules @@ -0,0 +1,6 @@ +## These rules watch for kernel module insertion. By monitoring +## the syscall, we do not need any watches on programs. +-a always,exit -F arch=b32 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b64 -S init_module,finit_module -F key=module-load +-a always,exit -F arch=b32 -S delete_module -F key=module-unload +-a always,exit -F arch=b64 -S delete_module -F key=module-unload diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/44-installers.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/44-installers.rules new file mode 100644 index 0000000..171a383 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/44-installers.rules @@ -0,0 +1,9 @@ +# These rules watch for invocation of things known to install software + +-a always,exit -F perm=x -F path=/usr/bin/dnf-3 -F key=software-installer +-a always,exit -F perm=x -F path=/usr/bin/yum -F key=software-installer +-a always,exit -F perm=x -F path=/usr/bin/pip -F key=software-installer +-a always,exit -F perm=x -F path=/usr/bin/npm -F key=software-installer +-a always,exit -F perm=x -F path=/usr/bin/cpan -F key=software-installer +-a always,exit -F perm=x -F path=/usr/bin/gem -F key=software-installer +-a always,exit -F perm=x -F path=/usr/bin/luarocks -F key=software-installer diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/70-einval.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/70-einval.rules new file mode 100644 index 0000000..a93ff65 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/70-einval.rules @@ -0,0 +1,6 @@ +## These are rules are to locate poorly written programs. +## Its never planned to waste time on a syscall with incorrect parameters +## This is more of a debugging step than something people should run with +## in production. +-a never,exit -F arch=b64 -S rt_sigreturn +-a always,exit -S all -F exit=-EINVAL -F key=einval-retcode diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/71-networking.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/71-networking.rules new file mode 100644 index 0000000..82755e2 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/71-networking.rules @@ -0,0 +1,3 @@ +## This is to check if the system is making or receiving connections +## externally +-a always,exit -F arch=b64 -S accept,connect -F key=external-access diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/99-finalize.rules b/gardenlinux/features/log/file.include/etc/audit/rules.d/99-finalize.rules new file mode 100644 index 0000000..e370185 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/99-finalize.rules @@ -0,0 +1,3 @@ +## Make the configuration immutable - reboot is required to change audit rules +#-e 2 + diff --git a/gardenlinux/features/log/file.include/etc/audit/rules.d/README b/gardenlinux/features/log/file.include/etc/audit/rules.d/README new file mode 100644 index 0000000..b6b95cc --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/audit/rules.d/README @@ -0,0 +1,31 @@ +This group of rules are meant to be used with the augenrules program. +The augenrules program expects rules to be located in /etc/audit/rules.d/ +The rules will get processed in a specific order based on their natural +sort order. To make things easier to use, the files in this directory are +organized into groups with the following meanings: + +10 - Kernel and auditctl configuration +20 - Rules that could match general rules but we want a different match +30 - Main rules +40 - Optional rules +50 - Server Specific rules +70 - System local rules +90 - Finalize (immutable) + +There is one set of rules, 31-privileged.rules, that should be regenerated. +There is a script in the comments of that file. You can uncomment the commands +and run the script and then rename the resulting file. + +The rules are not meant to be used all at once. They are pieces of a policy +that should be thought out and individual files copied to /etc/audit/rules.d/ +For example, if you wanted to set a system up in the STIG configuration, copy +rules 10-base-config, 30-stig, 31-privileged, and 99-finalize. You can add +more if you like. Also, not all arches have the same syscalls. It is expected +that the rules be fine tuned for the arch they are deployed on. For example, +aarch64 does not have the open syscall. It should just be deleted from the +rules. + +Once you have the rules in the rules.d directory, you can load them by running +augenrules --load + +Source: https://github.com/linux-audit/audit-userspace.git diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.conf b/gardenlinux/features/log/file.include/etc/rsyslog.conf new file mode 100644 index 0000000..10851bd --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.conf @@ -0,0 +1,23 @@ +########################### +#### GLOBAL DIRECTIVES #### +########################### + +# +# Set the default permissions for all log files. +# +$FileOwner root +$FileGroup adm +$FileCreateMode 0640 +$DirCreateMode 0755 +$Umask 0022 + +# +# Where to place spool and state files +# +$WorkDirectory /var/spool/rsyslog + +# +# Include all config files in /etc/rsyslog.d/ +# +$IncludeConfig /etc/rsyslog.d/*.conf + diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.d/10-local.conf.disabled b/gardenlinux/features/log/file.include/etc/rsyslog.d/10-local.conf.disabled new file mode 100644 index 0000000..86c96ee --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.d/10-local.conf.disabled @@ -0,0 +1,18 @@ +# +# Log anything besides private authentication messages to a single log file +# +*.*;auth,authpriv.none -/var/log/syslog + +# +# Log commonly used facilities to their own log file +# +auth,authpriv.* /var/log/auth.log +cron.* -/var/log/cron.log +kern.* -/var/log/kern.log +mail.* -/var/log/mail.log +user.* -/var/log/user.log + +# +# Emergencies are sent to everybody logged in. +# +*.emerg :omusrmsg:* diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.d/20-input.conf b/gardenlinux/features/log/file.include/etc/rsyslog.d/20-input.conf new file mode 100644 index 0000000..e14a1f8 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.d/20-input.conf @@ -0,0 +1 @@ +module(load="imuxsock") # provides support for local system logging diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.d/21-input-klog.conf.disabled b/gardenlinux/features/log/file.include/etc/rsyslog.d/21-input-klog.conf.disabled new file mode 100644 index 0000000..57f54d5 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.d/21-input-klog.conf.disabled @@ -0,0 +1,2 @@ +module(load="imklog") # provides kernel logging support + diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.d/29-input-mark.conf.disabled b/gardenlinux/features/log/file.include/etc/rsyslog.d/29-input-mark.conf.disabled new file mode 100644 index 0000000..f309c81 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.d/29-input-mark.conf.disabled @@ -0,0 +1,2 @@ +module(load="immark") # provides --MARK-- message capability + diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.d/30-server.conf.disabled b/gardenlinux/features/log/file.include/etc/rsyslog.d/30-server.conf.disabled new file mode 100644 index 0000000..4266042 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.d/30-server.conf.disabled @@ -0,0 +1,8 @@ +# provides UDP syslog reception +module(load="imudp") +input(type="imudp" port="514") + +# provides TCP syslog reception +module(load="imtcp") +input(type="imtcp" port="514") + diff --git a/gardenlinux/features/log/file.include/etc/rsyslog.d/60-audit-log-service.conf.disabled b/gardenlinux/features/log/file.include/etc/rsyslog.d/60-audit-log-service.conf.disabled new file mode 100644 index 0000000..b26041e --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/rsyslog.d/60-audit-log-service.conf.disabled @@ -0,0 +1,9 @@ +$ModLoad omrelp +*.* action(type="omrelp" target="" port="10515" timeout="90" Template="SyslogForwarderTemplate" +rebindInterval="1000" +tls="on" +tls.caCert="" +tls.myCert="" +tls.myPrivKey="" +tls.authmode="name" +tls.permittedpeer=["auditlog-rsyslog-server"]) diff --git a/gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/10-minimum.conf b/gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/10-minimum.conf new file mode 100644 index 0000000..4f91726 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/10-minimum.conf @@ -0,0 +1,4 @@ +[Journal] +Seal=yes +ReadKMsg=yes +Audit=yes diff --git a/gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/20-rsyslog.conf b/gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/20-rsyslog.conf new file mode 100644 index 0000000..a2c2ea9 --- /dev/null +++ b/gardenlinux/features/log/file.include/etc/systemd/journald.conf.d/20-rsyslog.conf @@ -0,0 +1,3 @@ +[Journal] +ForwardToSyslog=yes +MaxLevelSyslog=info diff --git a/gardenlinux/features/log/info.yaml b/gardenlinux/features/log/info.yaml new file mode 100644 index 0000000..842fd05 --- /dev/null +++ b/gardenlinux/features/log/info.yaml @@ -0,0 +1,2 @@ +description: 'logging infrastructure including audit log and remotelogging' +type: element diff --git a/gardenlinux/features/log/pkg.include b/gardenlinux/features/log/pkg.include new file mode 100644 index 0000000..1bfb3a9 --- /dev/null +++ b/gardenlinux/features/log/pkg.include @@ -0,0 +1,3 @@ +auditd +systemd-journal-remote +rsyslog-relp diff --git a/gardenlinux/features/metal/README.md b/gardenlinux/features/metal/README.md new file mode 100644 index 0000000..97b3a67 --- /dev/null +++ b/gardenlinux/features/metal/README.md @@ -0,0 +1,20 @@ +## Feature: metal +### Description + +This platform feature creates an artifact for (bare) metal systems. + + +### Features +This feature creates a (bare) metal compatible image artifact as an `.iso` file and includes further metal related stuff like standard kernel, grub, etc. that are required for physical components. + + +### Unit testing +This platform feature supports unit testing and is based on the `metal` fixture to validate the applied changes according its feature configuration. + +### Meta +||| +|---|---| +|type|platform| +|artifact|`.raw`,`.qcow2`| +|included_features|`cloud`| +|excluded_features|None| diff --git a/gardenlinux/features/metal/exec.config b/gardenlinux/features/metal/exec.config new file mode 100755 index 0000000..eab19ac --- /dev/null +++ b/gardenlinux/features/metal/exec.config @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +systemctl enable ipmievd diff --git a/gardenlinux/features/metal/file.exclude b/gardenlinux/features/metal/file.exclude new file mode 100644 index 0000000..771654a --- /dev/null +++ b/gardenlinux/features/metal/file.exclude @@ -0,0 +1,8 @@ +/etc/network +# prefer systemd unit files +/etc/init.d/irqbalance +/etc/init.d/ipmievd +/etc/runit +/etc/sv +/usr/share/runit +/etc/mdadm/mdadm.conf diff --git a/gardenlinux/features/metal/file.include/etc/cron.monthly/update-pciids b/gardenlinux/features/metal/file.include/etc/cron.monthly/update-pciids new file mode 120000 index 0000000..d4d53dd --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/cron.monthly/update-pciids @@ -0,0 +1 @@ +/usr/sbin/update-pciids \ No newline at end of file diff --git a/gardenlinux/features/metal/file.include/etc/cron.monthly/update-usbids b/gardenlinux/features/metal/file.include/etc/cron.monthly/update-usbids new file mode 120000 index 0000000..33858cc --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/cron.monthly/update-usbids @@ -0,0 +1 @@ +/usr/sbin/update-usbids \ No newline at end of file diff --git a/gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/00-default.cfg b/gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/00-default.cfg new file mode 100644 index 0000000..d7ed5d1 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/00-default.cfg @@ -0,0 +1,5 @@ +# DO NOT CHANGE THIS FILE! USE /etc/kernel/cmdline.d +CMDLINE_LINUX="ro earlyprintk=ttyS0,115200 consoleblank=0" +DEVICE="LABEL=ROOT" +# WARNING! 0 disables the TIMEOUT +TIMEOUT=1 diff --git a/gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/10-console.cfg b/gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/10-console.cfg new file mode 100644 index 0000000..bb43ade --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/cmdline.d/10-console.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="console=tty0 console=ttyS0,115200 $CMDLINE_LINUX" diff --git a/gardenlinux/features/metal/file.include/etc/kernel/entry-token b/gardenlinux/features/metal/file.include/etc/kernel/entry-token new file mode 100644 index 0000000..697bb66 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/entry-token @@ -0,0 +1 @@ +Default diff --git a/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-kernel-cmdline b/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-kernel-cmdline new file mode 100755 index 0000000..718c595 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-kernel-cmdline @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euoE pipefail + +/usr/local/sbin/update-kernel-cmdline diff --git a/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-ucode b/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-ucode new file mode 100755 index 0000000..754e857 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/00-ucode @@ -0,0 +1,9 @@ +#!/bin/sh + +bootdir=/boot + +[ -x /usr/sbin/iucode_tool ] || exit 0 +[ -x /lib/firmware/intel-ucode ] || exit 0 + +rm -f ${bootdir}/intel-ucode.img +/usr/sbin/iucode_tool --scan-system --write-earlyfw=${bootdir}/intel-ucode.img /lib/firmware/intel-ucode diff --git a/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/zz-update-syslinux b/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/zz-update-syslinux new file mode 100755 index 0000000..404d7e8 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/postinst.d/zz-update-syslinux @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euoE pipefail + +which syslinux &> /dev/null || exit 0 + +/usr/local/sbin/update-syslinux diff --git a/gardenlinux/features/metal/file.include/etc/kernel/postrm.d/zz-update-syslinux b/gardenlinux/features/metal/file.include/etc/kernel/postrm.d/zz-update-syslinux new file mode 100755 index 0000000..404d7e8 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/kernel/postrm.d/zz-update-syslinux @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euoE pipefail + +which syslinux &> /dev/null || exit 0 + +/usr/local/sbin/update-syslinux diff --git a/gardenlinux/features/metal/file.include/etc/udev/rules.d/69-nostbyrot.rules b/gardenlinux/features/metal/file.include/etc/udev/rules.d/69-nostbyrot.rules new file mode 100644 index 0000000..2125e0a --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/udev/rules.d/69-nostbyrot.rules @@ -0,0 +1 @@ +ACTION=="add|change", SUBSYSTEM=="block", ATTRS{queue/rotational}=="1", RUN+="/usr/bin/hdparm -B 254 -S 0 /dev/%k" diff --git a/gardenlinux/features/metal/file.include/etc/udev/rules.d/71-intellldp.rules b/gardenlinux/features/metal/file.include/etc/udev/rules.d/71-intellldp.rules new file mode 100644 index 0000000..7e932e0 --- /dev/null +++ b/gardenlinux/features/metal/file.include/etc/udev/rules.d/71-intellldp.rules @@ -0,0 +1 @@ +SUBSYSTEM=="net", ACTION=="add|change", DRIVERS=="i40e", PROGRAM="/sbin/ethtool --set-priv-flags %k disable-fw-lldp on" diff --git a/gardenlinux/features/metal/file.include/usr/sbin/update-usbids b/gardenlinux/features/metal/file.include/usr/sbin/update-usbids new file mode 100755 index 0000000..66d2baf --- /dev/null +++ b/gardenlinux/features/metal/file.include/usr/sbin/update-usbids @@ -0,0 +1,46 @@ +#!/bin/sh + +# see also update-pciids.sh (fancier) + +[ "$1" = "-q" ] && quiet="true" || quiet="false" + +set -e +SRC="http://www.linux-usb.org/usb.ids" +DEST=/var/lib/usbutils/usb.ids + +# if usb.ids is read-only (because the filesystem is read-only), +# then just skip this whole process. +if ! touch ${DEST} >&2 >/dev/null ; then + ${quiet} || echo "${DEST} is read-only, exiting." + exit 0 +fi + +if which wget >/dev/null 2>&1 ; then + DL="wget -O $DEST.new $SRC" + ${quiet} && DL="$DL -q" +elif which lynx >/dev/null 2>&1 ; then + DL="eval lynx -source $SRC >$DEST.new" +else + echo >&2 "update-usbids: cannot find wget nor lynx" + exit 1 +fi + +if ! $DL ; then + echo >&2 "update-usbids: download failed" + rm -f $DEST.new + exit 1 +fi + +if ! grep >/dev/null "^C " $DEST.new ; then + echo >&2 "update-usbids: missing class info, probably truncated file" + exit 1 +fi + +if [ -f $DEST ] ; then + mv $DEST $DEST.old + # --reference is supported only by chmod from GNU file, so let's ignore any errors + chmod -f --reference=$DEST.old $DEST.new 2>/dev/null || true +fi +mv $DEST.new $DEST + +${quiet} || echo "Done." diff --git a/gardenlinux/features/metal/info.yaml b/gardenlinux/features/metal/info.yaml new file mode 100644 index 0000000..efb1882 --- /dev/null +++ b/gardenlinux/features/metal/info.yaml @@ -0,0 +1,6 @@ +description: "metal features (standard kernel, starndard grub, physical components)" +type: platform +features: + include: + - server + - _boot diff --git a/gardenlinux/features/metal/pkg.include b/gardenlinux/features/metal/pkg.include new file mode 100644 index 0000000..9f08062 --- /dev/null +++ b/gardenlinux/features/metal/pkg.include @@ -0,0 +1,16 @@ +ethtool +fdisk +lvm2 +dosfstools +efibootmgr +efitools +ipmitool +pciutils +usbutils +numactl +mdadm +hdparm +smartmontools +frr +linux-image-6.1-$arch +tpm2-tools diff --git a/gardenlinux/features/metal/test/debsums.disable b/gardenlinux/features/metal/test/debsums.disable new file mode 100644 index 0000000..b193d52 --- /dev/null +++ b/gardenlinux/features/metal/test/debsums.disable @@ -0,0 +1,2 @@ +# disabling as it will fail because of the signed systemd-bootx64.efi +# TODO : re-enable when we have our own systemd packages diff --git a/gardenlinux/features/metal/test/test_packages_musthave.py b/gardenlinux/features/metal/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/metal/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/openstack/README.md b/gardenlinux/features/openstack/README.md new file mode 100644 index 0000000..cb6aea8 --- /dev/null +++ b/gardenlinux/features/openstack/README.md @@ -0,0 +1,21 @@ +## Feature: openstack +### Description + +This platform feature creates an artifact for OpenStack (CCEE) systems. + + +### Features +This feature creates an OpenStack (CCEE) ompatible image artifact as a `.raw`, `.qcow2` and `.vmdk` file. +This particular feature set for Garden Linux on OpenStack is for an OpenStack landscape that uses VMware ESXi as hypervisor (which is why you will find `open-vm-tools` in `pkg.include`). If you want to run Garden Linux on OpenStack, you will most likely have to create your own feature set that matches your environment and hypervisor. +A word of **WARNING**: Since OpenStack is an open environment, we can only provide a reference implementation at this point. + +### Unit testing +This platform feature supports unit testing and is based on the `openstackccee` fixture to validate the applied changes according its feature configuration. + +### Meta +||| +|---|---| +|type|platform| +|artifact|`.raw`,`.qcow2`, `.vmdk`| +|included_features|cloud| +|excluded_features|None| diff --git a/gardenlinux/features/openstack/convert.qcow2 b/gardenlinux/features/openstack/convert.qcow2 new file mode 100755 index 0000000..54f2134 --- /dev/null +++ b/gardenlinux/features/openstack/convert.qcow2 @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +qemu-img convert -f raw -O qcow2 "$1" "$2" diff --git a/gardenlinux/features/openstack/convert.vmdk b/gardenlinux/features/openstack/convert.vmdk new file mode 100755 index 0000000..14e7f1f --- /dev/null +++ b/gardenlinux/features/openstack/convert.vmdk @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +qemu-img convert -o subformat=streamOptimized -f raw -O vmdk "$1" "$2" diff --git a/gardenlinux/features/openstack/exec.config b/gardenlinux/features/openstack/exec.config new file mode 100755 index 0000000..ef099ce --- /dev/null +++ b/gardenlinux/features/openstack/exec.config @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# growpart is done in initramfs, growroot by systemd +mv /etc/cloud/cloud.cfg /etc/cloud/cloud.cfg.bak +cat /etc/cloud/cloud.cfg.bak | grep -v "^ - growpart$" | grep -v "^ - resizefs$" | grep -v "^ - ntp$" >/etc/cloud/cloud.cfg +rm /etc/cloud/cloud.cfg.bak diff --git a/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg b/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg new file mode 100644 index 0000000..b1504cd --- /dev/null +++ b/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg @@ -0,0 +1,13 @@ +apt_preserve_sources_list: true +manage_etc_hosts: true +system_info: + default_user: + name: admin + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + lock_passwd: True + gecos: Debian + groups: [adm, audio, cdrom, dialout, dip, floppy, netdev, plugdev, sudo, video] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + diff --git a/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/50-datasource.cfg b/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/50-datasource.cfg new file mode 100644 index 0000000..f8e0f59 --- /dev/null +++ b/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/50-datasource.cfg @@ -0,0 +1 @@ +datasource_list: [ ConfigDrive, OpenStack, Ec2 ] diff --git a/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg b/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg new file mode 100644 index 0000000..f144451 --- /dev/null +++ b/gardenlinux/features/openstack/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg @@ -0,0 +1 @@ +network: {config: disabled} diff --git a/gardenlinux/features/openstack/file.include/etc/cloud/ds-identify.cfg b/gardenlinux/features/openstack/file.include/etc/cloud/ds-identify.cfg new file mode 100644 index 0000000..637d75d --- /dev/null +++ b/gardenlinux/features/openstack/file.include/etc/cloud/ds-identify.cfg @@ -0,0 +1,2 @@ +datasource: OpenStack +policy: enabled diff --git a/gardenlinux/features/openstack/file.include/etc/default/grub.d/90_openstack_cc_ee.cfg b/gardenlinux/features/openstack/file.include/etc/default/grub.d/90_openstack_cc_ee.cfg new file mode 100644 index 0000000..4ff27a4 --- /dev/null +++ b/gardenlinux/features/openstack/file.include/etc/default/grub.d/90_openstack_cc_ee.cfg @@ -0,0 +1 @@ +GRUB_CMDLINE_LINUX="$GRUB_CMDLINE_LINUX product_name='OpenStack Nova'" \ No newline at end of file diff --git a/gardenlinux/features/openstack/file.include/etc/kernel/cmdline.d/10-console.cfg b/gardenlinux/features/openstack/file.include/etc/kernel/cmdline.d/10-console.cfg new file mode 100644 index 0000000..a40d0b0 --- /dev/null +++ b/gardenlinux/features/openstack/file.include/etc/kernel/cmdline.d/10-console.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="console=tty0 console=ttyS0 $CMDLINE_LINUX" diff --git a/gardenlinux/features/openstack/info.yaml b/gardenlinux/features/openstack/info.yaml new file mode 100644 index 0000000..bea4036 --- /dev/null +++ b/gardenlinux/features/openstack/info.yaml @@ -0,0 +1,5 @@ +description: "OpenStack platform (cloud-init, open-vm-tools)" +type: platform +features: + include: + - cloud diff --git a/gardenlinux/features/openstack/pkg.include b/gardenlinux/features/openstack/pkg.include new file mode 100644 index 0000000..2665805 --- /dev/null +++ b/gardenlinux/features/openstack/pkg.include @@ -0,0 +1,5 @@ +python3-cffi-backend +python3-boto +cloud-init +dmidecode +open-vm-tools \ No newline at end of file diff --git a/gardenlinux/features/openstack/test/test_check_files.py b/gardenlinux/features/openstack/test/test_check_files.py new file mode 100644 index 0000000..9724bbd --- /dev/null +++ b/gardenlinux/features/openstack/test/test_check_files.py @@ -0,0 +1,18 @@ +from helper.utils import check_file +import pytest + + +@pytest.mark.parametrize( + "file_name", + [ + "/etc/cloud/ds-identify.cfg", + "/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg", + "/etc/cloud/cloud.cfg.d/50-datasource.cfg", + "/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg" + ] +) + + +def test_check_file(client, file_name): + exists = check_file(client, file_name) + assert exists, f"File {file_name} could not be found." diff --git a/gardenlinux/features/openstack/test/test_content_in_file.py b/gardenlinux/features/openstack/test/test_content_in_file.py new file mode 100644 index 0000000..bd87655 --- /dev/null +++ b/gardenlinux/features/openstack/test/test_content_in_file.py @@ -0,0 +1,17 @@ +import pytest +from helper.tests.file_content import file_content +from helper.utils import execute_remote_command + + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/cloud/cloud.cfg", "ntp"), + ("/etc/cloud/cloud.cfg", "resizefs"), + ("/etc/cloud/cloud.cfg", "growpart") + ] +) + + +def test_file_content(client, file, args): + file_content(client, file, args, only_line_match=True, invert=True) diff --git a/gardenlinux/features/openstack/test/test_packages_musthave.py b/gardenlinux/features/openstack/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/openstack/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/ostreeImage/file.exclude b/gardenlinux/features/ostreeImage/file.exclude new file mode 100644 index 0000000..2e0f9df --- /dev/null +++ b/gardenlinux/features/ostreeImage/file.exclude @@ -0,0 +1 @@ +/boot/efi/loader/random-seed diff --git a/gardenlinux/features/ostreeImage/fstab b/gardenlinux/features/ostreeImage/fstab new file mode 100644 index 0000000..a2acbd2 --- /dev/null +++ b/gardenlinux/features/ostreeImage/fstab @@ -0,0 +1,3 @@ +# +LABEL=EFI /boot/efi vfat umask=0077 type=uefi,size=500M +LABEL=ROOT / ext4 rw,errors=remount-ro,prjquota,discard diff --git a/gardenlinux/features/ostreeImage/image.ostree.raw b/gardenlinux/features/ostreeImage/image.ostree.raw new file mode 100755 index 0000000..a7cc722 --- /dev/null +++ b/gardenlinux/features/ostreeImage/image.ostree.raw @@ -0,0 +1,88 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +export PATH="/builder/image.d:$PATH" + +rootfs_work="$(mktemp -d)" +mount -t tmpfs tmpfs "$rootfs_work" + +MYROOT="$(mktemp -d)" +mount -t tmpfs tmpfs "$MYROOT" +mkdir -p "$MYROOT"/sysroot +mkdir -p "$MYROOT"/sysroot/ostree/deploy +mkdir -p "$MYROOT"/sysroot/ostree/deploy/gardenlinux/var +OSTREE_SYSROOT="$MYROOT/sysroot" +OSTREE_REPO=$OSTREE_SYSROOT/ostree/repo +OSTREE_REF="gardenlinux/today/$BUILDER_ARCH" + +rootfs="$1" +output="$2" + +tar xf "$rootfs" -C "$rootfs_work" + + +mkdir -p $OSTREE_REPO +mkdir -p $OSTREE_SYSROOT +download="$(mktemp -d)" +pushd $download +curl --remote-name http://ostree.gardenlinux.io/gardenlinux-today-$BUILDER_ARCH.tar.gz +tar xf gardenlinux-today-$BUILDER_ARCH.tar.gz --directory $OSTREE_REPO +ls -l $OSTREE_REPO +popd +rm -rf $download +ostree admin deploy --karg=root=LABEL=ROOT --karg-append=rw --karg-append=efi=runtime --karg-append=systemd.journald.forward_to_console=1 --sysroot=$OSTREE_SYSROOT --os=gardenlinux $OSTREE_REF + +boot_hash=`ls "$OSTREE_SYSROOT"/ostree/boot.1.1/gardenlinux/` +mkdir -p "$OSTREE_SYSROOT"/ostree/boot.1.1/gardenlinux/$boot_hash/0/sysroot + +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/gardenlinux/var/home +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/gardenlinux/var/roothome +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/gardenlinux/var/opt +mkdir -p "$OSTREE_SYSROOT"/ostree/deploy/gardenlinux/var/srv + +# Build disk image, this is hacky as of now, needs rework +# Setup bootloader +boot_dir=$(mktemp -d) +cp -r $OSTREE_SYSROOT/boot/* $boot_dir +LOADER_TEMP=$(mktemp -d) +echo BOOT_DIR: $boot_dir +ls -la $boot_dir +rm -rf $boot_dir/loader +# move to temp dir to avoid errors with systemd-boot install +mv $boot_dir/loader.1 $LOADER_TEMP +mkdir -p $rootfs_work/boot/efi +mount --bind $boot_dir $rootfs_work/boot/efi +mount --rbind /proc $rootfs_work/proc +mount --rbind /sys $rootfs_work/sys +ls -l $rootfs_work/usr/bin +SYSTEMD_ESP_PATH=/boot/efi chroot $rootfs_work /usr/bin/bootctl --no-variables install +umount -l $rootfs_work/proc +umount -l $rootfs_work/sys +umount $rootfs_work/boot/efi +# recover from temp dir +cp -r $LOADER_TEMP/* $boot_dir/loader.1 +cp -r $LOADER_TEMP/* $boot_dir/loader +cp $boot_dir/loader/loader.1/entries/* $boot_dir/loader/entries +cat $boot_dir/loader/entries/* +echo 'timeout 7' > $boot_dir/loader/loader.conf + +efi_partition=$(mktemp) +root_partition=$(mktemp) +partitions=$(mktemp) + +# fixme: make disk size dynamic +truncate -s 900M "$efi_partition" +# make_reproducible_vfat $OSTREE_SYSROOT/boot "$efi_partition" +make_reproducible_vfat -t 11111111 $boot_dir "$efi_partition" +size_uefi=$(du -b "$efi_partition" | awk '{ padded_size = $1 + (MB - ($1 % MB) % MB); print (padded_size / MB) }' "MB=1048576") +part_uuid_uefi=b0e0359c-007b-4361-a0d1-a7ca2d73fe3c +echo -e "$part_uuid_uefi\tuefi\t$size_uefi\t0\t$efi_partition\tEFI" >> "$partitions" + +truncate -s 3G "$root_partition" +make_reproducible_ext4 -t 11111111 -l ROOT "$MYROOT"/sysroot "$root_partition" +size_rootfs=$(du -b "$root_partition" | awk '{ padded_size = $1 + (MB - ($1 % MB) % MB); print (padded_size / MB) }' "MB=1048576") +part_uuid_rootfs=a9bef950-8218-4888-9f1c-1ad8bb481807 +echo -e "$part_uuid_rootfs\tlinux\t$size_rootfs\t0\t$root_partition\tROOT" >> "$partitions" + +makedisk $rootfs_work "$output" < "$partitions" diff --git a/gardenlinux/features/ostreeImage/info.yaml b/gardenlinux/features/ostreeImage/info.yaml new file mode 100644 index 0000000..6bc5f79 --- /dev/null +++ b/gardenlinux/features/ostreeImage/info.yaml @@ -0,0 +1,2 @@ +description: 'image-based system using OSTree' +type: platform diff --git a/gardenlinux/features/ostreeImage/pkg.include b/gardenlinux/features/ostreeImage/pkg.include new file mode 100644 index 0000000..2bf617d --- /dev/null +++ b/gardenlinux/features/ostreeImage/pkg.include @@ -0,0 +1 @@ +systemd-boot diff --git a/gardenlinux/features/ostreeRepo/exec.late b/gardenlinux/features/ostreeRepo/exec.late new file mode 100755 index 0000000..6a49230 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/exec.late @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo OSTree builder +ostree --version + +OSTREE_SYSROOT=/sysroot + +kernel="$(find /boot -name 'vmlinuz-*' | sort -V | tail -n 1)" +version="${kernel#*-}" + +# Adapt to ostree root-fs requirements, see https://ostreedev.github.io/ostree/adapting-existing/ + +declare -A TOPLEVEL_LINKS=( + ["home"]="var/home" + ["media"]="run/media" + ["mnt"]="var/mnt" + ["opt"]="var/opt" + # ["ostree"]="$OSTREE_SYSROOT/ostree" + # ["root"]="var/roothome" + ["srv"]="var/srv" +) + +for link in "${!TOPLEVEL_LINKS[@]}"; do + target=${TOPLEVEL_LINKS[$link]} + echo mv $link $(dirname $target) + mv $link $(dirname $target) + echo ln -sf $target $link + ln -sf $target $link +done + +mv root var/roothome +ln -sf var/roothome root + +ln -s sysroot/ostree ostree + +echo $(date --utc +%Y-%m-%dT%H:%M%Z) > /timestamp + + +# Using a heredoc to create the update script in the root-fs +# This script is a workaround for multiple issues +# - the *.origin files lack the remote in the refspec +# - OSTree won't update the bootloader entries on the ESP +# Syntax taken from https://tldp.org/LDP/abs/html/here-docs.html +( +cat <<'EOF' +#!/usr/bin/bash + +echo Performing OSTree upgrade + +ostree log gardenlinux/today/arm64 + +# Workaround for missing remote in origin file +find /ostree/deploy/*/deploy/*.origin | while read origin_file +do + sudo sed -i 's#refspec=gardenlinux#refspec=origin:gardenlinux#g' $origin_file +done + +sudo ostree admin upgrade + +# Workaround for wrong location of boot loader entries +sudo mkdir -p /var/ESPmnt +sudo mount /dev/vda1 /var/ESPmnt +sudo cp /boot/loader/entries/* /var/ESPmnt/loader/entries +sudo umount /dev/vda1 + +ostree log gardenlinux/today/arm64 + +ostree admin status + +echo Done upgrading. Run 'sudo systemctl reboot' to boot into the latest commit. +EOF +) > /usr/bin/gl-ostree-upgrade + +chmod +x /usr/bin/gl-ostree-upgrade + +update-kernel-cmdline + +mkdir -p /boot/efi/Default + +for kernel in /boot/vmlinuz-*; do + unshare --mount bash -c 'mount -t tmpfs none /sys && mount --bind /usr/bin/false /usr/bin/systemd-detect-virt && "$@"' \ + DRACUT_COMPRESS_XZ="$(command -v xz)" dracut \ + --no-hostonly \ + --force \ + --kver "${version}" \ + --add "ostree" \ + --modules "bash dash systemd systemd-initrd kernel-modules kernel-modules-extra terminfo udev-rules dracut-systemd base fs-lib shutdown" \ + --reproducible \ + "/boot/initrd.img-${version}" + + SYSTEMD_ESP_PATH=/boot/efi kernel-install add "${kernel#*-}" "${kernel}" +done + +sed 's/boot\/efi\///' -i /boot/efi/loader/entries/*.conf + +SYSTEMD_ESP_PATH=/boot/efi bootctl --no-variables install + +# systemctl disable ignition-disable.service +# systemctl disable rsyslog.service +# systemctl disable systemd-repart.service +# systemctl disable syslog.socket + +rm /etc/systemd/system/ignition-disable.service || true +rm /usr/lib/systemd/system/initrd-root-fs.target.wants/systemd-repart.service +rm /usr/lib/systemd/system/sysinit.target.wants/systemd-repart.service +rm /usr/lib/systemd/system/systemd-repart.service + +# ostree kernel location: https://ostreedev.github.io/ostree/deployment/#contents-of-a-deployment +mkdir -p /usr/lib/modules/$version/ +cp $kernel /usr/lib/modules/$version/vmlinuz +cp "/boot/initrd.img-$version" /usr/lib/modules/$version/initramfs.img + +# Build fails if we do this here instead of in image.ostree.raw +# mv /etc /usr/etc + +# Delete apt because this is an image-based system +rm -f /etc/cron.daily/apt-compat +rm -f /etc/logrotate.d/apt +rm -f /etc/systemd/system/timers.target.wants/apt-daily-upgrade.timer +rm -f /etc/systemd/system/timers.target.wants/apt-daily.timer +rm -f /usr/bin/apt* +rm -f /usr/bin/debconf-apt-progress +rm -rf /etc/apt +rm -rf /etc/dpkg +rm -rf /usr/lib/apt/ +rm -rf /usr/lib/dpkg/methods/apt/ +rm -rf /usr/lib/systemd/system/apt* +rm -rf /usr/share/bash-completion/completions/apt +rm -rf /usr/share/bug/apt/ +rm -rf /var/cache/apt/ +rm -rf /var/lib/apt/ +rm -rf /var/lib/dpkg +rm -rf /var/log/apt/ + +find "/var/log/" -type f -delete diff --git a/gardenlinux/features/ostreeRepo/file.exclude b/gardenlinux/features/ostreeRepo/file.exclude new file mode 100644 index 0000000..2e0f9df --- /dev/null +++ b/gardenlinux/features/ostreeRepo/file.exclude @@ -0,0 +1 @@ +/boot/efi/loader/random-seed diff --git a/gardenlinux/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf b/gardenlinux/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf new file mode 100644 index 0000000..fd6877b --- /dev/null +++ b/gardenlinux/features/ostreeRepo/file.include/usr/lib/tmpfiles.d/ostree.conf @@ -0,0 +1,2 @@ +d /var/log/journal 0755 root root - +d /var/log/audit 0755 root root - diff --git a/gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-kernel-cmdline b/gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-kernel-cmdline new file mode 100755 index 0000000..1c28489 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-kernel-cmdline @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -uoeE pipefail + +configDir="/etc/kernel/cmdline.d" + +# load extra stuff +for i in "${configDir}"/*-*.cfg; do + [ -e "$i" ] || continue + source $i +done + +echo "root=${DEVICE} ${CMDLINE_LINUX}" > /etc/kernel/cmdline.new +mv /etc/kernel/cmdline.new /etc/kernel/cmdline diff --git a/gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-syslinux b/gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-syslinux new file mode 100755 index 0000000..cb51948 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/file.include/usr/local/sbin/update-syslinux @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +set -uoeE pipefail + +bootDir="/boot/efi" +kernelDir="${bootDir}/Default" +configDir="/etc/kernel/cmdline.d" +configFile="${bootDir}/syslinux/syslinux.cfg" + +check_version() { + local v=$1 + if [ ! -d "$kernelDir/$v" ]; then + return 1 + fi + if [ ! -f "$kernelDir/$v/linux" ]; then + return 1 + fi + if [ ! -f "$kernelDir/$v/initrd.img-$v" ]; then + return 1 + fi + return 0 +} + +if ! which syslinux &> /dev/null; then + exit 0 +fi + +#TODO: detect if anything other than bootDir/Default is used +if [ ! -d "$kernelDir" ]; then + exit 0 +fi + +# load extras +for i in "${configDir}"/*-*.cfg; do + [ -e "$i" ] || continue + source "$i" +done + +versions=() +# kernel / initrd +for kernel in /boot/vmlinuz-*; do + if check_version "${kernel#*-}"; then + versions+=("${kernel#*-}") + fi +done + +if [ "${#versions[@]}" == "0" ]; then + echo "no valid kernels found" 1>&2 + exit 1 +fi + +readarray -t vSorted < <(printf '%s\n' "${versions[@]}" | sort -rV) +{ + echo "UI menu.c32" + echo "PROMPT 0" + echo + echo "MENU TITLE Gardenlinux" + echo "TIMEOUT $TIMEOUT" + echo "DEFAULT ${vSorted[0]}" + echo + for v in "${vSorted[@]}"; do + echo "LABEL Linux $v" + echo " LINUX ../Default/$v/linux" + echo " APPEND root=${DEVICE} ${CMDLINE_LINUX}" + echo " INITRD ../Default/${v}/initrd.img-$v" + echo + done +} > "${configFile}.new" + +mv "${configFile}.new" "${configFile}" diff --git a/gardenlinux/features/ostreeRepo/fstab b/gardenlinux/features/ostreeRepo/fstab new file mode 100644 index 0000000..a2acbd2 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/fstab @@ -0,0 +1,3 @@ +# +LABEL=EFI /boot/efi vfat umask=0077 type=uefi,size=500M +LABEL=ROOT / ext4 rw,errors=remount-ro,prjquota,discard diff --git a/gardenlinux/features/ostreeRepo/image.ostreeRepo.tar.gz b/gardenlinux/features/ostreeRepo/image.ostreeRepo.tar.gz new file mode 100755 index 0000000..a18f208 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/image.ostreeRepo.tar.gz @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +set -euxo pipefail + +export PATH="/builder/image.d:$PATH" + +rootfs_work="$(mktemp -d)" +mount -t tmpfs tmpfs "$rootfs_work" + +MYROOT="$(mktemp -d)" +mount -t tmpfs tmpfs "$MYROOT" +mkdir -p "$MYROOT"/sysroot +OSTREE_SYSROOT="$MYROOT/sysroot" +OSTREE_REPO=$OSTREE_SYSROOT/ostree/repo +OSTREE_REF="gardenlinux/today/$BUILDER_ARCH" + +rootfs="$1" +output="$2" + +tar xf "$rootfs" -C "$rootfs_work" + +mv "$rootfs_work"/etc "$rootfs_work"/usr/etc + +mkdir -p $OSTREE_REPO + +# fixme: re-enable later +# if curl --head --silent --fail http://ostree.gardenlinux.io/gardenlinux-today-$BUILDER_ARCH.tar.gz 2> /dev/null; +# then +# echo "Using http://ostree.gardenlinux.io/gardenlinux-today-$BUILDER_ARCH.tar.gz" +# mkdir -p $OSTREE_REPO +# download="$(mktemp -d)" +# pushd $download +# curl --remote-name http://ostree.gardenlinux.io/gardenlinux-today-$BUILDER_ARCH.tar.gz +# tar xf gardenlinux-today-$BUILDER_ARCH.tar.gz --directory $OSTREE_REPO +# popd +# rm -rf $download +# else +# echo "Coud not download http://ostree.gardenlinux.io/gardenlinux-today-$BUILDER_ARCH.tar.gz, building new repo" + ostree init --mode=archive --repo=$OSTREE_REPO + ostree admin init-fs --modern $OSTREE_SYSROOT + ostree admin os-init --sysroot=$OSTREE_SYSROOT gardenlinux + ostree config --repo=$OSTREE_REPO set sysroot.bootloader none + ostree remote --repo=$OSTREE_REPO add --no-gpg-verify --no-sign-verify origin http://ostree.gardenlinux.io/gardenlinux-today-$BUILDER_ARCH $OSTREE_REF +# fi + +ostree commit --repo=$OSTREE_REPO --branch $OSTREE_REF --skip-if-unchanged -s "Gardenlinux build $(date --utc +%Y-%m-%dT%H:%M%Z)" "$rootfs_work" + +tar --directory $OSTREE_REPO --create --mtime="@$BUILDER_TIMESTAMP" --sort name --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime . | gzip > "$output" diff --git a/gardenlinux/features/ostreeRepo/info.yaml b/gardenlinux/features/ostreeRepo/info.yaml new file mode 100644 index 0000000..7127904 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/info.yaml @@ -0,0 +1,5 @@ +description: 'image-based system using OSTree' +type: flag +features: + exclude: + - _boot diff --git a/gardenlinux/features/ostreeRepo/pkg.include b/gardenlinux/features/ostreeRepo/pkg.include new file mode 100644 index 0000000..7044241 --- /dev/null +++ b/gardenlinux/features/ostreeRepo/pkg.include @@ -0,0 +1,10 @@ +bubblewrap +dracut +ostree +ostree-boot +podman +podman-toolbox +systemd-boot +vim +dosfstools +sudo diff --git a/gardenlinux/features/server/README.md b/gardenlinux/features/server/README.md new file mode 100644 index 0000000..0536961 --- /dev/null +++ b/gardenlinux/features/server/README.md @@ -0,0 +1,20 @@ +## Feature: server +### Description + +This server layer adds further services like `auditd`, `SELinux` and `systemd` for service management. + + +### Features +This server layer adds further services like `auditd`, `SELinux` and `systemd` for service management. +Additional tools like `dnsutils`, `sudo`, `sysstat` etc. are installed. + +### Unit testing +This features support unit testing and validates extended capabilities on `ping`, the installed packages, sshd configuration, systemd units etc. + +### Meta +||| +|---|---| +|type|element| +|artifact|None| +|included_features|`base`, `ssh`,`_selinux`| +|excluded_features|None| diff --git a/gardenlinux/features/server/exec.config b/gardenlinux/features/server/exec.config new file mode 100755 index 0000000..b0c5d41 --- /dev/null +++ b/gardenlinux/features/server/exec.config @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +pushd /etc || exit 1 +chmod 644 locale.conf +rm -f /etc/default/locale +ln -s locale.conf default/locale +popd || exit 1 + +systemctl enable systemd-networkd +systemctl enable systemd-resolved +systemctl enable tmp.mount + +for i in $(ls /boot | grep vmlinuz | sed "s/vmlinuz-//"); do + systemctl enable kexec-load@$i +done + +update-ca-certificates +addgroup --system wheel + +# fix file system permissions for higher security +chmod g-w / /etc/hosts + +# allow su only for members of wheel +sed -r '/^auth\s+sufficient\s+pam_rootok\.so/a auth required pam_wheel.so' -i /etc/pam.d/su + +# set permissions to 0640 to /etc/skel/* +chmod 0640 /etc/skel/.bash* +chmod 0640 /etc/skel/.profile + +# remove nis +sed -r '/^netgroup:\s+nis/d' -i /etc/nsswitch.conf + +DEBIAN_FRONTEND=noninteractive pam-auth-update --remove passwdqc diff --git a/gardenlinux/features/server/exec.early b/gardenlinux/features/server/exec.early new file mode 100755 index 0000000..1f92436 --- /dev/null +++ b/gardenlinux/features/server/exec.early @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +mkdir -p /run/systemd/resolve/ +cp /etc/resolv.conf /run/systemd/resolve/stub-resolv.conf diff --git a/gardenlinux/features/server/exec.post b/gardenlinux/features/server/exec.post new file mode 100755 index 0000000..2b92f67 --- /dev/null +++ b/gardenlinux/features/server/exec.post @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +rootfs="$1" + +rm "$rootfs/etc/hostname" +echo -n > "$rootfs/etc/hostname" + +rm "$rootfs/etc/resolv.conf" +ln -s /run/systemd/resolve/resolv.conf "$rootfs/etc/resolv.conf" diff --git a/gardenlinux/features/server/file.exclude b/gardenlinux/features/server/file.exclude new file mode 100644 index 0000000..c74076b --- /dev/null +++ b/gardenlinux/features/server/file.exclude @@ -0,0 +1,25 @@ +/etc/init.d/sysstat +/etc/ufw +/etc/init.d/auditd +/etc/init.d/dbus +/etc/monit +/etc/init.d/sudo +/etc/init.d/sysstat +/etc/init.d/udev +/etc/init.d/kexec +/etc/init.d/kexec-load +/etc/sv +/etc/runit +/var/log/runit +/var/log/private +/etc/update-motd.d/10-uname +/etc/cron.d/e2scrub_all +/var/lib/dbus/machine-id +/usr/games +/initrd.img +/initrd.img.old +/vmlinuz +/vmlinuz.old +/usr/lib/**/__pycache__ +/etc/nvme/hostid +/etc/nvme/hostnqn diff --git a/gardenlinux/features/server/file.include/etc/dracut.conf.d/25-uefi-stub.conf b/gardenlinux/features/server/file.include/etc/dracut.conf.d/25-uefi-stub.conf new file mode 100644 index 0000000..3d907f0 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/dracut.conf.d/25-uefi-stub.conf @@ -0,0 +1,2 @@ +# We set --uefi explicitly when calling dracut +uefi_stub=/usr/lib/systemd/boot/efi/linuxx64.efi.stub diff --git a/gardenlinux/features/server/file.include/etc/dracut.conf.d/general.conf b/gardenlinux/features/server/file.include/etc/dracut.conf.d/general.conf new file mode 100644 index 0000000..4dd0787 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/dracut.conf.d/general.conf @@ -0,0 +1,3 @@ +do_strip="no" +compress="xz" +reproducible="yes" diff --git a/gardenlinux/features/server/file.include/etc/kernel-img.conf b/gardenlinux/features/server/file.include/etc/kernel-img.conf new file mode 100644 index 0000000..0fa75c4 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/kernel-img.conf @@ -0,0 +1 @@ +do_symlinks = no diff --git a/gardenlinux/features/server/file.include/etc/locale.conf b/gardenlinux/features/server/file.include/etc/locale.conf new file mode 100644 index 0000000..4d43ca5 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/locale.conf @@ -0,0 +1,2 @@ +LANG=C.UTF-8 +LANGUAGE="en_US:en" diff --git a/gardenlinux/features/server/file.include/etc/machine-id b/gardenlinux/features/server/file.include/etc/machine-id new file mode 100644 index 0000000..e7dd222 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/machine-id @@ -0,0 +1 @@ +uninitialized diff --git a/gardenlinux/features/server/file.include/etc/profile.d/50-nohistory.sh b/gardenlinux/features/server/file.include/etc/profile.d/50-nohistory.sh new file mode 100755 index 0000000..9223663 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/profile.d/50-nohistory.sh @@ -0,0 +1,4 @@ +# shellcheck shell=sh disable=SC2148 +HISTFILE=/dev/null +readonly HISTFILE +export HISTFILE diff --git a/gardenlinux/features/server/file.include/etc/sudoers.d/keepssh b/gardenlinux/features/server/file.include/etc/sudoers.d/keepssh new file mode 100644 index 0000000..6106499 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/sudoers.d/keepssh @@ -0,0 +1 @@ +Defaults env_keep+=SSH_AUTH_SOCK diff --git a/gardenlinux/features/server/file.include/etc/sudoers.d/wheel b/gardenlinux/features/server/file.include/etc/sudoers.d/wheel new file mode 100644 index 0000000..6e12a8d --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/sudoers.d/wheel @@ -0,0 +1 @@ +%wheel ALL=(ALL:ALL) NOPASSWD: ALL diff --git a/gardenlinux/features/server/file.include/etc/sysctl.d/enable-unprivileged-user-namespaces.conf b/gardenlinux/features/server/file.include/etc/sysctl.d/enable-unprivileged-user-namespaces.conf new file mode 100644 index 0000000..c4ee63d --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/sysctl.d/enable-unprivileged-user-namespaces.conf @@ -0,0 +1 @@ +kernel.unprivileged_userns_clone = 1 diff --git a/gardenlinux/features/server/file.include/etc/sysctl.d/restric-dmesg.conf b/gardenlinux/features/server/file.include/etc/sysctl.d/restric-dmesg.conf new file mode 100644 index 0000000..fd707af --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/sysctl.d/restric-dmesg.conf @@ -0,0 +1 @@ +kernel.dmesg_restrict = 1 diff --git a/gardenlinux/features/server/file.include/etc/systemd/network/99-default.network b/gardenlinux/features/server/file.include/etc/systemd/network/99-default.network new file mode 100644 index 0000000..b4135bd --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/network/99-default.network @@ -0,0 +1,11 @@ +[Match] +Name=en* eth* + +[Network] +DHCP=yes +LLDP=true +EmitLLDP=true + +[DHCPv4] +UseDomains=true +UseMTU=true diff --git a/gardenlinux/features/server/file.include/etc/systemd/networkd.conf.d/00-gardenlinux-server.conf b/gardenlinux/features/server/file.include/etc/systemd/networkd.conf.d/00-gardenlinux-server.conf new file mode 100644 index 0000000..1274aee --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/networkd.conf.d/00-gardenlinux-server.conf @@ -0,0 +1,3 @@ +[Network] +ManageForeignRoutingPolicyRules=no +ManageForeignRoutes=no diff --git a/gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/00-disable-llmnr.conf b/gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/00-disable-llmnr.conf new file mode 100644 index 0000000..ad4ea0b --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/00-disable-llmnr.conf @@ -0,0 +1,2 @@ +[Resolve] +LLMNR=no diff --git a/gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/01-disable-mdns.conf b/gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/01-disable-mdns.conf new file mode 100644 index 0000000..523ed7c --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/resolved.conf.d/01-disable-mdns.conf @@ -0,0 +1,2 @@ +[Resolve] +MulticastDNS=no diff --git a/gardenlinux/features/server/file.include/etc/systemd/system.conf.d/00-gardenlinux-server.conf b/gardenlinux/features/server/file.include/etc/systemd/system.conf.d/00-gardenlinux-server.conf new file mode 100644 index 0000000..832dc93 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system.conf.d/00-gardenlinux-server.conf @@ -0,0 +1,2 @@ +[Manager] +RuntimeWatchdogSec=20s diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/kexec-load@.service b/gardenlinux/features/server/file.include/etc/systemd/system/kexec-load@.service new file mode 100644 index 0000000..cd3837f --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/kexec-load@.service @@ -0,0 +1,12 @@ +[Unit] +Description=load %i kernel into the current kernel +Documentation=man:kexec(8) +DefaultDependencies=no +Before=shutdown.target umount.target final.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/kexec -l /boot/vmlinuz-%i --initrd=/boot/initramfs-%i.img --reuse-cmdline + +[Install] +WantedBy=kexec.target diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/systemd-coredump@.service.d/override.conf b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-coredump@.service.d/override.conf new file mode 100644 index 0000000..893d6a6 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-coredump@.service.d/override.conf @@ -0,0 +1,2 @@ +[Service] +ExecStopPost=/bin/sh -c "rmdir /run/systemd/propagate/systemd-coredump@%i.service" diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/systemd-growfs@.service.d/override.conf b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-growfs@.service.d/override.conf new file mode 100644 index 0000000..55a07ca --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-growfs@.service.d/override.conf @@ -0,0 +1,2 @@ +[Unit] +After=systemd-repart.service diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf new file mode 100644 index 0000000..1ef5a03 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-networkd-wait-online.service.d/any.conf @@ -0,0 +1,3 @@ +[Service] +ExecStart= +ExecStart=/lib/systemd/systemd-networkd-wait-online --any diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/systemd-resolved.service.d/wait-for-networkd.conf b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-resolved.service.d/wait-for-networkd.conf new file mode 100644 index 0000000..74d9aa3 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-resolved.service.d/wait-for-networkd.conf @@ -0,0 +1,2 @@ +[Unit] +After=systemd-networkd.service diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/systemd-timesyncd.service.d/override.conf b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-timesyncd.service.d/override.conf new file mode 100644 index 0000000..6e3d4d6 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/systemd-timesyncd.service.d/override.conf @@ -0,0 +1,3 @@ +[Unit] +After=systemd-resolved.service +Wants=systemd-resolved.service diff --git a/gardenlinux/features/server/file.include/etc/systemd/system/tmp.mount b/gardenlinux/features/server/file.include/etc/systemd/system/tmp.mount new file mode 100644 index 0000000..2088099 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/systemd/system/tmp.mount @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Temporary Directory (/tmp) +Documentation=https://systemd.io/TEMPORARY_DIRECTORIES +Documentation=man:file-hierarchy(7) +Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems +ConditionPathIsSymbolicLink=!/tmp +DefaultDependencies=no +Conflicts=umount.target +Before=local-fs.target umount.target +After=swap.target + +[Mount] +What=tmpfs +Where=/tmp +Type=tmpfs +Options=mode=1777,strictatime,nosuid,nodev,noexec + +[Install] +WantedBy=local-fs.target diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/05-logo b/gardenlinux/features/server/file.include/etc/update-motd.d/05-logo new file mode 100755 index 0000000..24aa438 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/05-logo @@ -0,0 +1,9 @@ +#!/bin/sh + +echo ' ____ _ _ _ ' +echo ' / ___| __ _ _ __ __| | ___ _ __ | | _ _ __ _ ___ __' +echo '| | _ / _` | '\''__/ _` |/ _ \ '\''_ \ | | | | '\''_ \| | | \ \/ /' +echo '| |_| | (_| | | | (_| | __/ | | | | |___| | | | | |_| |> < ' +echo ' \____|\__,_|_| \__,_|\___|_| |_| |_____|_|_| |_|\__,_/_/\_\' +echo 'Garden Linux @VERSION@ (based on Debian GNU/Linux bookworm) ' +echo diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/10-hostname b/gardenlinux/features/server/file.include/etc/update-motd.d/10-hostname new file mode 100755 index 0000000..d014e89 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/10-hostname @@ -0,0 +1,2 @@ +#!/bin/sh +echo "Welcome to \e[0;32m$(hostname -f)\e[0m $(date)" diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/20-uname b/gardenlinux/features/server/file.include/etc/update-motd.d/20-uname new file mode 100755 index 0000000..d84fa09 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/20-uname @@ -0,0 +1,2 @@ +#!/bin/sh +echo "$(uname -svm) $(uptime -p)" diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/30-load b/gardenlinux/features/server/file.include/etc/update-motd.d/30-load new file mode 100755 index 0000000..a144e80 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/30-load @@ -0,0 +1,10 @@ +#!/bin/sh + +cpu="$(cat /proc/cpuinfo | grep processor -c)" +loadavg="$(cat /proc/loadavg)" +iostat="$(iostat -c -o JSON)" + +echo "Load: \ +$(echo "${loadavg}" | awk '{printf("%s%s\033[0m, %s%s\033[0m, %s%s\033[0m", ($1>'${cpu}')?"\033[31m":($1>('${cpu}'/10))?"\033[33m":"\033[32m",$1, ($2>'${cpu}')?"\033[31m":($2>('${cpu}'/10))?"\033[33m":"\033[32m" ,$2, ($3>'${cpu}')?"\033[31m":($3>('${cpu}'/10))?"\033[33m":"\033[32m" ,$3)}') of ${cpu}CPUs \ +$(echo "${iostat}" | grep -o -e "\"iowait\": [[:digit:]]*\.[[:digit:]]*" | awk '{printf("%s%s iowait\033[0m", ($2<1)?"\033[32m":($2<10)?"\033[33m":"\033[31m", ($2<1)?"no":$2 "%")}') \ +$(echo "${iostat}" | grep -o -e "\"steal\": [[:digit:]]*\.[[:digit:]]*" | awk '{printf("%s%s steal time\033[0m", ($2<1)?"\033[32m":($2<10)?"\033[33m":"\033[31m", ($2<1)?"no":$2 "%")}')" diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/40-free b/gardenlinux/features/server/file.include/etc/update-motd.d/40-free new file mode 100755 index 0000000..59bddcd --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/40-free @@ -0,0 +1,16 @@ +#!/bin/sh + +ramfree="$(free)" +diskfree="$(df)" + +HEADERS="RAM" +CONTENT="$(echo "${ramfree}" | grep Mem | awk '{printf("%s%.2f%%\033[0m", (($4/$2)<0.1)?"\033[31m":(($4/$2)<0.3)?"\033[33m":"\033[32m", $4/$2 * 100.0)}')" +if [ $(echo "${ramfree}" | grep Swap | awk '{printf $2; }') -gt 0 ]; then + HEADERS="${HEADERS},Swap" + CONTENT="${CONTENT},$(echo "${ramfree}" | grep Swap | awk '{printf("%s%.2f%%\033[0m", (($4/$2)<0.1)?"\033[31m":(($4/$2)<0.9)?"\033[33m":"\033[32m", $4/$2 * 100.0)}')" +fi +HEADERS="${HEADERS}$(echo "${diskfree}" | grep '^/dev'| awk '{printf(",%s", $6)}')" +CONTENT="${CONTENT}$(echo "${diskfree}" | grep '^/dev'| awk '{printf(",%s%.2f%%\033[0m", (($4/$2)<0.1)?"\033[31m":(($4/$2)<0.3)?"\033[33m":"\033[32m", $4/$2 * 100.0)}')" + +echo " ,${HEADERS}\nFree:,${CONTENT}" | column -t -s, + diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/45-line b/gardenlinux/features/server/file.include/etc/update-motd.d/45-line new file mode 100755 index 0000000..82a76d3 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/45-line @@ -0,0 +1,2 @@ +#!/bin/sh +echo diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/50-network b/gardenlinux/features/server/file.include/etc/update-motd.d/50-network new file mode 100755 index 0000000..a03e523 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/50-network @@ -0,0 +1,46 @@ +#!/bin/sh + +mask2cdr () +{ + # Assumes there's no "255." after a non-255 byte in the mask + local x=${1##*255.} + set -- 0^^^128^192^224^240^248^252^254^ $(( (${#1} - ${#x})*2 )) ${x%%.*} + x=${1%%$3*} + echo $(( $2 + (${#x}/4) )) +} + +mac2ipv6ll () +{ + IFS=':'; set $1; unset IFS + printf "fe80::%x%x:%x:%x:%x/64\n" $(( 0x${1} ^ 2 )) 0x${2} 0x${3}ff 0xfe${4} 0x${5}${6} +} + +for i in $(networkctl | grep routable | awk '{ print $2; }') ipmi; do + if [ "$i" = "ipmi" ]; then + ip="$(ipmitool lan print 2>/dev/null)" + if [ -n "$ip" ]; then + ipv4="$(printf "%15s/%s" $(echo "$ip" | grep "IP Address " | cut -d: -f2) $(mask2cdr $(echo "$ip" | grep "Subnet Mask " | cut -d: -f2)))" + ipv6="$(ipmitool lan6 print 1 "static_addr" 2>/dev/null)\n$(ipmitool lan6 print 1 "dynamic_addr" 2>/dev/null)" + ipv6="$(echo "$ipv6" | grep " Address:" | sort -u | cut -d: -f2- | tr -d ' ' | grep -v ^::/0)" + ipv6ll="$(mac2ipv6ll $(echo "$ip" | grep "MAC Address " | cut -d: -f2- ))" + fi + else + ip="$(ip addr show dev $i)" + if [ -n "$ip" ]; then + ipv4="$(echo "$ip" | grep "inet " | awk '{ split($2,a,"/"); printf "%15s/%s\n",a[1],a[2]; }')" + ipv6="$(echo "$ip" | grep "inet6 " | awk '{ split($2,a,"/"); printf "%s/%s\n",a[1],a[2]; }')" + ipv6ll="$(echo "$ipv6" | grep -e "^fe80" -e "^::1")" + ipv6="$(echo "$ipv6" | grep -v "^fe80" | grep -v "^::1")" + fi + fi + + while [ ! -z "$ipv4" -o ! -z "$ipv6" -o ! -z "$ipv6ll" ]; do + out="$out\n$i,$(echo "$ipv4" | head -1),$(echo "$ipv6ll" | head -1),$(echo "$ipv6" | head -1)" + i="" + ipv4=$(echo "$ipv4" | tail +2) + ipv6=$(echo "$ipv6" | tail +2) + ipv6ll=$(echo "$ipv6ll" | tail +2) + done +done + +echo "$out" | column -t -s, diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/55-line b/gardenlinux/features/server/file.include/etc/update-motd.d/55-line new file mode 100755 index 0000000..82a76d3 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/55-line @@ -0,0 +1,2 @@ +#!/bin/sh +echo diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/92-unattended-upgrades b/gardenlinux/features/server/file.include/etc/update-motd.d/92-unattended-upgrades new file mode 100755 index 0000000..57799a4 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/92-unattended-upgrades @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ -x /usr/share/unattended-upgrades/update-motd-unattended-upgrades ]; then + exec /usr/share/unattended-upgrades/update-motd-unattended-upgrades +fi diff --git a/gardenlinux/features/server/file.include/etc/update-motd.d/95-needrestart b/gardenlinux/features/server/file.include/etc/update-motd.d/95-needrestart new file mode 100755 index 0000000..178ee83 --- /dev/null +++ b/gardenlinux/features/server/file.include/etc/update-motd.d/95-needrestart @@ -0,0 +1,10 @@ +#!/bin/sh + +if [ -x /usr/sbin/needrestart ]; then +svc="$(/usr/sbin/needrestart -b | /usr/bin/grep NEEDRESTART-SVC | /usr/bin/cut -d: -f2)" + +if [ "$svc" != "" ]; then + echo "Services running outdated binaries:\e[0;31m"$svc"\e[0m" + echo +fi +fi diff --git a/gardenlinux/features/server/file.include/usr/lib/systemd/system/dbus.socket b/gardenlinux/features/server/file.include/usr/lib/systemd/system/dbus.socket new file mode 100644 index 0000000..5c373cf --- /dev/null +++ b/gardenlinux/features/server/file.include/usr/lib/systemd/system/dbus.socket @@ -0,0 +1,5 @@ +[Unit] +Description=D-Bus System Message Bus Socket + +[Socket] +ListenStream=/run/dbus/system_bus_socket diff --git a/gardenlinux/features/server/file.include/usr/share/pam-configs/garden b/gardenlinux/features/server/file.include/usr/share/pam-configs/garden new file mode 100644 index 0000000..1d4fd55 --- /dev/null +++ b/gardenlinux/features/server/file.include/usr/share/pam-configs/garden @@ -0,0 +1,26 @@ +Name: Garden Linux pam policies +Default: yes +Priority: 1024 +Conflicts: passwdqc +Password-Type: Primary +Password: + required pam_passwdqc.so min=disabled,disabled,12,8,8 passphrase=4 similar=deny retry=5 + required pam_pwhistory.so use_authtok remember=5 retry=5 +Password-Initial: + required pam_passwdqc.so min=disabled,disabled,12,8,8 passphrase=4 similar=deny retry=5 + required pam_pwhistory.so use_authtok remember=5 retry=5 +Auth-Type: Primary +Auth: + required pam_faillock.so preauth silent audit deny=5 unlock_time=900 +Auth-Initial: + required pam_faillock.so preauth silent audit deny=5 unlock_time=900 +Account-Type: Primary +Account: + required pam_faillock.so +Account-Initial: + required pam_faillock.so +Session-Type: Additional +Session: + optional pam_umask.so +Session-Initial: + optional pam_umask.so diff --git a/gardenlinux/features/server/file.include/usr/share/pam-configs/garden-extra b/gardenlinux/features/server/file.include/usr/share/pam-configs/garden-extra new file mode 100644 index 0000000..66a973f --- /dev/null +++ b/gardenlinux/features/server/file.include/usr/share/pam-configs/garden-extra @@ -0,0 +1,8 @@ +Name: Garden Linux pam policies faillock +Default: yes +Priority: 128 +Auth-Type: Primary +Auth: + [default=die] pam_faillock.so authfail silent audit deny=5 unlock_time=900 +Auth-Initial: + [default=die] pam_faillock.so authfail silent audit deny=5 unlock_time=900 diff --git a/gardenlinux/features/server/info.yaml b/gardenlinux/features/server/info.yaml new file mode 100644 index 0000000..b67f906 --- /dev/null +++ b/gardenlinux/features/server/info.yaml @@ -0,0 +1,8 @@ +description: 'server layer with complete init system (systemd)' +type: element +features: + include: + - base + - ssh + - log + - _selinux diff --git a/gardenlinux/features/server/pkg.include b/gardenlinux/features/server/pkg.include new file mode 100644 index 0000000..068664b --- /dev/null +++ b/gardenlinux/features/server/pkg.include @@ -0,0 +1,32 @@ +bsdextrautils +ca-certificates +dnsutils +iproute2 +iptables +iputils-ping +kexec-tools +less +libnss-resolve +libnss-systemd +libpam-passwdqc +libpam-systemd +lsb-release +net-tools +netbase +nvme-cli +policykit-1 +procps +quota +sosreport +sudo +sysstat +systemd +systemd-boot +systemd-boot-efi +systemd-coredump +systemd-cron +systemd-timesyncd +tcpdump +traceroute +vim-tiny +wget diff --git a/gardenlinux/features/server/test/capabilities.d/capabilities.list b/gardenlinux/features/server/test/capabilities.d/capabilities.list new file mode 100644 index 0000000..fb502af --- /dev/null +++ b/gardenlinux/features/server/test/capabilities.d/capabilities.list @@ -0,0 +1 @@ +/usr/bin/ping cap_net_raw=ep \ No newline at end of file diff --git a/gardenlinux/features/server/test/pkg.ignore b/gardenlinux/features/server/test/pkg.ignore new file mode 100644 index 0000000..fb2e309 --- /dev/null +++ b/gardenlinux/features/server/test/pkg.ignore @@ -0,0 +1 @@ +systemd-coredump diff --git a/gardenlinux/features/server/test/systemctl.d/systemctl_disabled.list b/gardenlinux/features/server/test/systemctl.d/systemctl_disabled.list new file mode 100644 index 0000000..e69de29 diff --git a/gardenlinux/features/server/test/systemctl.d/systemctl_enabled.list b/gardenlinux/features/server/test/systemctl.d/systemctl_enabled.list new file mode 100644 index 0000000..47b0117 --- /dev/null +++ b/gardenlinux/features/server/test/systemctl.d/systemctl_enabled.list @@ -0,0 +1,2 @@ +systemd-networkd.service +auditd.service diff --git a/gardenlinux/features/server/test/test_autologin.py b/gardenlinux/features/server/test/test_autologin.py new file mode 100644 index 0000000..addfa94 --- /dev/null +++ b/gardenlinux/features/server/test/test_autologin.py @@ -0,0 +1,26 @@ +from helper.tests.file_content import file_content +import pytest + +# Parametrize the test unit with further +# options. +# First: File to process +# Second: Key to search +# Third: Value of Key +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/systemd/system/serial-getty@.service.d/autologin.conf", {"ExecStart": "autologin"}), + ("/etc/systemd/system/getty@tty1.service.d/autologin.conf", {"ExecStart": "autologin"}) + ] +) + + +# Run the test unit to perform the +# final tests by the given artifact. +# Testing: +# - The key/value search lookup is inverted. As a +# resulty any found pattern is an error. +# - Files that can not be found are ignored +# and will not fail the test. +def test_autologin(client, file, args, non_dev): + file_content(client, file, args, invert=True, ignore_missing=True) diff --git a/gardenlinux/features/server/test/test_capabilities.py b/gardenlinux/features/server/test/test_capabilities.py new file mode 100644 index 0000000..ec8b0f0 --- /dev/null +++ b/gardenlinux/features/server/test/test_capabilities.py @@ -0,0 +1 @@ +from helper.tests.capabilities import capabilities as test_capabilities \ No newline at end of file diff --git a/gardenlinux/features/server/test/test_dmesg.py b/gardenlinux/features/server/test/test_dmesg.py new file mode 100644 index 0000000..ee95d1a --- /dev/null +++ b/gardenlinux/features/server/test/test_dmesg.py @@ -0,0 +1,18 @@ +import pytest +from helper.tests.file_content import file_content +from helper.utils import execute_remote_command + + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/sysctl.d/restric-dmesg.conf", {"kernel.dmesg_restrict": "1"}), + ("/tmp/sysctl.txt", {"kernel.dmesg_restrict": "1"}) + ] +) + + +def test_dmesg(client, file, args, non_chroot, non_feature_gardener): + cmd = "sysctl -a > /tmp/sysctl.txt" + execute_remote_command(client, cmd) + file_content(client, file, args) diff --git a/gardenlinux/features/server/test/test_history.py b/gardenlinux/features/server/test/test_history.py new file mode 100644 index 0000000..ef67daa --- /dev/null +++ b/gardenlinux/features/server/test/test_history.py @@ -0,0 +1,36 @@ +from helper.tests.file_content import file_content +from helper.utils import unset_env_var +import pytest + +# Parametrize the test unit with further +# options. +# First: File to process +# Second: Key to search +# Third: Value of Key +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/profile.d/50-nohistory.sh", { + "HISTFILE": "/dev/null", + "readonly": "HISTFILE", + "export": "HISTFILE" + }) + ] +) + + +# Run the test unit to perform the +# final tests by the given artifact. +# Testing: +# - The key/value search lookup is inverted. As a +# resulty any found pattern is an error. +# - Files that can not be found are ignored +# and will not fail the test. +def test_history(client, file, args): + # Test is config is present in file + file_content(client, file, args) + + # Test write protection of an ENV var + env_var = "HISTFILE" + unset_var = unset_env_var(client, env_var) + assert unset_var == 1, f"ENV VAR {env_var} is not write protected or was not present." diff --git a/gardenlinux/features/server/test/test_kernel_parameter.py b/gardenlinux/features/server/test/test_kernel_parameter.py new file mode 100644 index 0000000..360dd05 --- /dev/null +++ b/gardenlinux/features/server/test/test_kernel_parameter.py @@ -0,0 +1,13 @@ +from helper.tests.kernel_parameter import kernel_parameter +import pytest + +@pytest.mark.parametrize( + "parameter,value", + [ + ("fs.protected_symlinks", 1), + ("fs.protected_hardlinks", 1), + ("kernel.randomize_va_space", 2) + ] +) +def test_kernel_parameter(client, parameter, value, non_chroot): + kernel_parameter(client, parameter, value) diff --git a/gardenlinux/features/server/test/test_machine_id.py b/gardenlinux/features/server/test/test_machine_id.py new file mode 100644 index 0000000..ef37a10 --- /dev/null +++ b/gardenlinux/features/server/test/test_machine_id.py @@ -0,0 +1,8 @@ +from helper.tests.machine_id import machine_id +from helper.tests.machine_id import machine_id_powered_on + +def test_machine_id(client, chroot): + machine_id(client) + +def test_machine_id_powered_on(client, non_chroot): + machine_id_powered_on(client) diff --git a/gardenlinux/features/server/test/test_packages_musthave.py b/gardenlinux/features/server/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/server/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/server/test/test_pam.py b/gardenlinux/features/server/test/test_pam.py new file mode 100644 index 0000000..7a10b69 --- /dev/null +++ b/gardenlinux/features/server/test/test_pam.py @@ -0,0 +1,14 @@ +from helper.tests.file_content import file_content +import pytest + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/pam.d/common-password", "password required pam_passwdqc.so min=disabled,disabled,12,8,8 passphrase=4 similar=deny retry=5"), + ("/etc/pam.d/common-password", "password required pam_pwhistory.so use_authtok remember=5 retry=5") + ] +) + + +def test_pam_faillock(client, file, args): + file_content(client, file, args, only_line_match=True) diff --git a/gardenlinux/features/server/test/test_pam_faillock.py b/gardenlinux/features/server/test/test_pam_faillock.py new file mode 100644 index 0000000..81c7ed1 --- /dev/null +++ b/gardenlinux/features/server/test/test_pam_faillock.py @@ -0,0 +1,15 @@ +from helper.tests.file_content import file_content +import pytest + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/pam.d/common-auth", "auth required pam_faillock.so preauth silent audit deny=5 unlock_time=900"), + ("/etc/pam.d/common-auth", "auth [default=die] pam_faillock.so authfail silent audit deny=5 unlock_time=900"), + ("/etc/pam.d/common-account", "account required pam_faillock.so") + ] +) + + +def test_pam_faillock(client, file, args): + file_content(client, file, args, only_line_match=True) diff --git a/gardenlinux/features/server/test/test_reset_env_vars.py b/gardenlinux/features/server/test/test_reset_env_vars.py new file mode 100644 index 0000000..3aae187 --- /dev/null +++ b/gardenlinux/features/server/test/test_reset_env_vars.py @@ -0,0 +1,12 @@ +from helper.tests.file_content import file_content +import pytest + +@pytest.mark.parametrize( + "file,args,sudo", + [ + ("/etc/sudoers", {"Defaults": "env_reset"}, True) + ] +) + +def test_reset_env_vars(client, file, args, sudo): + file_content(client, file, args, invert=False, ignore_missing=False, sudo=sudo) diff --git a/gardenlinux/features/server/test/test_systemctl.py b/gardenlinux/features/server/test/test_systemctl.py new file mode 100644 index 0000000..61072f9 --- /dev/null +++ b/gardenlinux/features/server/test/test_systemctl.py @@ -0,0 +1,18 @@ +from helper.tests.systemctl import systemctl +import pytest + +@pytest.mark.parametrize( + "state, services", + [ + ("enabled", [ + "systemd-networkd.service", + "auditd.service" + ] + ), + ("disabled", [] + ) + ] +) + +def test_systemctl(client, state, services): + systemctl(client, state, services) \ No newline at end of file diff --git a/gardenlinux/features/server/test/test_tiger.py b/gardenlinux/features/server/test/test_tiger.py new file mode 100644 index 0000000..4f18129 --- /dev/null +++ b/gardenlinux/features/server/test/test_tiger.py @@ -0,0 +1 @@ +from helper.tests.tiger import tiger as test_tiger \ No newline at end of file diff --git a/gardenlinux/features/server/test/test_users.py b/gardenlinux/features/server/test/test_users.py new file mode 100644 index 0000000..51db4fe --- /dev/null +++ b/gardenlinux/features/server/test/test_users.py @@ -0,0 +1,4 @@ +from helper.tests.users import users + +def test_users(client, non_dev, non_feature_githubActionRunner, non_vhost, non_hyperscalers): + users(client) diff --git a/gardenlinux/features/server/test/tiger.d/tigerrc b/gardenlinux/features/server/test/tiger.d/tigerrc new file mode 100644 index 0000000..e2ce39f --- /dev/null +++ b/gardenlinux/features/server/test/tiger.d/tigerrc @@ -0,0 +1,2 @@ +Tiger_Check_SERVICES=Y +Tiger_Check_SSH=Y diff --git a/gardenlinux/features/server/todo b/gardenlinux/features/server/todo new file mode 100644 index 0000000..a82c63a --- /dev/null +++ b/gardenlinux/features/server/todo @@ -0,0 +1 @@ +wireguard diff --git a/gardenlinux/features/ssh/README.md b/gardenlinux/features/ssh/README.md new file mode 100644 index 0000000..12be49e --- /dev/null +++ b/gardenlinux/features/ssh/README.md @@ -0,0 +1,26 @@ +## Feature: ssh +### Description + +This ssh layer adds the OpenSSH server with Fail2ban. + + +### Features +This ssh layer adds the OpenSSH server with Fail2ban. Fail2ban uses `nftables` which is installed and managed by the [firewall](../firewall/) feature. + +#### Configs +Services may be modified to the users need. + +OpenSSH server: `file.include/etc/ssh/sshd_config` + +Fail2ban: `file.include/etc/fail2ban/jail.conf` + +### Unit testing +This feature supports unit tests that will validate the correct configuration of the openssh server. These checks can be performed on any target platform. + +### Meta +||| +|---|---| +|type|element| +|artifact|None| +|included_features|`firewall`| +|excluded_features|None| diff --git a/gardenlinux/features/ssh/exec.config b/gardenlinux/features/ssh/exec.config new file mode 100755 index 0000000..9bc64e8 --- /dev/null +++ b/gardenlinux/features/ssh/exec.config @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +systemctl enable ssh-keygen +chmod 0440 /etc/sudoers.d/wheel /etc/sudoers.d/keepssh diff --git a/gardenlinux/features/ssh/file.exclude b/gardenlinux/features/ssh/file.exclude new file mode 100644 index 0000000..f1d77d9 --- /dev/null +++ b/gardenlinux/features/ssh/file.exclude @@ -0,0 +1,2 @@ +/etc/init.d/ssh +/etc/ssh/ssh_host_* diff --git a/gardenlinux/features/ssh/file.include.stat b/gardenlinux/features/ssh/file.include.stat new file mode 100644 index 0000000..4d5ef9d --- /dev/null +++ b/gardenlinux/features/ssh/file.include.stat @@ -0,0 +1 @@ +root root 640 /etc/fail2ban/jail.conf diff --git a/gardenlinux/features/ssh/file.include/etc/fail2ban/jail.conf b/gardenlinux/features/ssh/file.include/etc/fail2ban/jail.conf new file mode 100644 index 0000000..f24fc71 --- /dev/null +++ b/gardenlinux/features/ssh/file.include/etc/fail2ban/jail.conf @@ -0,0 +1,969 @@ +# +# WARNING: heavily refactored in 0.9.0 release. Please review and +# customize settings for your setup. +# +# Changes: in most of the cases you should not modify this +# file, but provide customizations in jail.local file, +# or separate .conf files under jail.d/ directory, e.g.: +# +# HOW TO ACTIVATE JAILS: +# +# YOU SHOULD NOT MODIFY THIS FILE. +# +# It will probably be overwritten or improved in a distribution update. +# +# Provide customizations in a jail.local file or a jail.d/customisation.local. +# For example to change the default bantime for all jails and to enable the +# ssh-iptables jail the following (uncommented) would appear in the .local file. +# See man 5 jail.conf for details. +# +# [DEFAULT] +# bantime = 1h +# +# [sshd] +# enabled = true +# +# See jail.conf(5) man page for more information + + + +# Comments: use '#' for comment lines and ';' (following a space) for inline comments + + +[INCLUDES] + +#before = paths-distro.conf +before = paths-debian.conf + +# The DEFAULT allows a global definition of the options. They can be overridden +# in each jail afterwards. + +[DEFAULT] +banaction = nftables-multiport +banaction_allports = nftables-allports +chain = input + + +# +# MISCELLANEOUS OPTIONS +# + +# "bantime.increment" allows to use database for searching of previously banned ip's to increase a +# default ban time using special formula, default it is banTime * 1, 2, 4, 8, 16, 32... +#bantime.increment = true + +# "bantime.rndtime" is the max number of seconds using for mixing with random time +# to prevent "clever" botnets calculate exact time IP can be unbanned again: +#bantime.rndtime = + +# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further) +#bantime.maxtime = + +# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier, +# default value of factor is 1 and with default value of formula, the ban time +# grows by 1, 2, 4, 8, 16 ... +#bantime.factor = 1 + +# "bantime.formula" used by default to calculate next value of ban time, default value below, +# the same ban time growing will be reached by multipliers 1, 2, 4, 8, 16, 32... +#bantime.formula = ban.Time * (1<<(ban.Count if ban.Count<20 else 20)) * banFactor +# +# more aggressive example of formula has the same values only for factor "2.0 / 2.885385" : +#bantime.formula = ban.Time * math.exp(float(ban.Count+1)*banFactor)/math.exp(1*banFactor) + +# "bantime.multipliers" used to calculate next value of ban time instead of formula, coresponding +# previously ban count and given "bantime.factor" (for multipliers default is 1); +# following example grows ban time by 1, 2, 4, 8, 16 ... and if last ban count greater as multipliers count, +# always used last multiplier (64 in example), for factor '1' and original ban time 600 - 10.6 hours +#bantime.multipliers = 1 2 4 8 16 32 64 +# following example can be used for small initial ban time (bantime=60) - it grows more aggressive at begin, +# for bantime=60 the multipliers are minutes and equal: 1 min, 5 min, 30 min, 1 hour, 5 hour, 12 hour, 1 day, 2 day +#bantime.multipliers = 1 5 30 60 300 720 1440 2880 + +# "bantime.overalljails" (if true) specifies the search of IP in the database will be executed +# cross over all jails, if false (dafault), only current jail of the ban IP will be searched +#bantime.overalljails = false + +# -------------------- + +# "ignoreself" specifies whether the local resp. own IP addresses should be ignored +# (default is true). Fail2ban will not ban a host which matches such addresses. +#ignoreself = true + +# "ignoreip" can be a list of IP addresses, CIDR masks or DNS hosts. Fail2ban +# will not ban a host which matches an address in this list. Several addresses +# can be defined using space (and/or comma) separator. +#ignoreip = 127.0.0.1/8 ::1 + +# External command that will take an tagged arguments to ignore, e.g. , +# and return true if the IP is to be ignored. False otherwise. +# +# ignorecommand = /path/to/command +ignorecommand = + +# "bantime" is the number of seconds that a host is banned. +bantime = 10m + +# A host is banned if it has generated "maxretry" during the last "findtime" +# seconds. +findtime = 10m + +# "maxretry" is the number of failures before a host get banned. +maxretry = 5 + +# "maxmatches" is the number of matches stored in ticket (resolvable via tag in actions). +maxmatches = %(maxretry)s + +# "backend" specifies the backend used to get files modification. +# Available options are "pyinotify", "gamin", "polling", "systemd" and "auto". +# This option can be overridden in each jail as well. +# +# pyinotify: requires pyinotify (a file alteration monitor) to be installed. +# If pyinotify is not installed, Fail2ban will use auto. +# gamin: requires Gamin (a file alteration monitor) to be installed. +# If Gamin is not installed, Fail2ban will use auto. +# polling: uses a polling algorithm which does not require external libraries. +# systemd: uses systemd python library to access the systemd journal. +# Specifying "logpath" is not valid for this backend. +# See "journalmatch" in the jails associated filter config +# auto: will try to use the following backends, in order: +# pyinotify, gamin, polling. +# +# Note: if systemd backend is chosen as the default but you enable a jail +# for which logs are present only in its own log files, specify some other +# backend for that jail (e.g. polling) and provide empty value for +# journalmatch. See https://github.com/fail2ban/fail2ban/issues/959#issuecomment-74901200 +backend = systemd + +# "usedns" specifies if jails should trust hostnames in logs, +# warn when DNS lookups are performed, or ignore all hostnames in logs +# +# yes: if a hostname is encountered, a DNS lookup will be performed. +# warn: if a hostname is encountered, a DNS lookup will be performed, +# but it will be logged as a warning. +# no: if a hostname is encountered, will not be used for banning, +# but it will be logged as info. +# raw: use raw value (no hostname), allow use it for no-host filters/actions (example user) +usedns = warn + +# "logencoding" specifies the encoding of the log files handled by the jail +# This is used to decode the lines from the log file. +# Typical examples: "ascii", "utf-8" +# +# auto: will use the system locale setting +logencoding = auto + +# "enabled" enables the jails. +# By default all jails are disabled, and it should stay this way. +# Enable only relevant to your setup jails in your .local or jail.d/*.conf +# +# true: jail will be enabled and log files will get monitored for changes +# false: jail is not enabled +enabled = false + + +# "mode" defines the mode of the filter (see corresponding filter implementation for more info). +mode = normal + +# "filter" defines the filter to use by the jail. +# By default jails have names matching their filter name +# +filter = %(__name__)s[mode=%(mode)s] + + +# +# ACTIONS +# + +# Some options used for actions + +# Destination email address used solely for the interpolations in +# jail.{conf,local,d/*} configuration files. +destemail = root@localhost + +# Sender email address used solely for some actions +sender = root@ + +# E-mail action. Since 0.8.1 Fail2Ban uses sendmail MTA for the +# mailing. Change mta configuration parameter to mail if you want to +# revert to conventional 'mail'. +mta = sendmail + +# Default protocol +protocol = tcp + +# Specify chain where jumps would need to be added in ban-actions expecting parameter chain +#chain = + +# Ports to be banned +# Usually should be overridden in a particular jail +port = 0:65535 + +# Format of user-agent https://tools.ietf.org/html/rfc7231#section-5.5.3 +fail2ban_agent = Fail2Ban/%(fail2ban_version)s + +# +# Action shortcuts. To be used to define action parameter + +# Default banning action (e.g. iptables, iptables-new, +# iptables-multiport, shorewall, etc) It is used to define +# action_* variables. Can be overridden globally or per +# section within jail.local file +#banaction = iptables-multiport +#banaction_allports = iptables-allports + +# The simplest action to take: ban only +action_ = %(banaction)s[port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] + +# ban & send an e-mail with whois report to the destemail. +action_mw = %(action_)s + %(mta)s-whois[sender="%(sender)s", dest="%(destemail)s", protocol="%(protocol)s", chain="%(chain)s"] + +# ban & send an e-mail with whois report and relevant log lines +# to the destemail. +action_mwl = %(action_)s + %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"] + +# See the IMPORTANT note in action.d/xarf-login-attack for when to use this action +# +# ban & send a xarf e-mail to abuse contact of IP address and include relevant log lines +# to the destemail. +action_xarf = %(action_)s + xarf-login-attack[service=%(__name__)s, sender="%(sender)s", logpath="%(logpath)s", port="%(port)s"] + +# ban IP on CloudFlare & send an e-mail with whois report and relevant log lines +# to the destemail. +action_cf_mwl = cloudflare[cfuser="%(cfemail)s", cftoken="%(cfapikey)s"] + %(mta)s-whois-lines[sender="%(sender)s", dest="%(destemail)s", logpath="%(logpath)s", chain="%(chain)s"] + +# Report block via blocklist.de fail2ban reporting service API +# +# See the IMPORTANT note in action.d/blocklist_de.conf for when to use this action. +# Specify expected parameters in file action.d/blocklist_de.local or if the interpolation +# `action_blocklist_de` used for the action, set value of `blocklist_de_apikey` +# in your `jail.local` globally (section [DEFAULT]) or per specific jail section (resp. in +# corresponding jail.d/my-jail.local file). +# +action_blocklist_de = blocklist_de[email="%(sender)s", service="%(__name__)s", apikey="%(blocklist_de_apikey)s", agent="%(fail2ban_agent)s"] + +# Report ban via badips.com, and use as blacklist +# +# See BadIPsAction docstring in config/action.d/badips.py for +# documentation for this action. +# +# NOTE: This action relies on banaction being present on start and therefore +# should be last action defined for a jail. +# +action_badips = badips.py[category="%(__name__)s", banaction="%(banaction)s", agent="%(fail2ban_agent)s"] +# +# Report ban via badips.com (uses action.d/badips.conf for reporting only) +# +action_badips_report = badips[category="%(__name__)s", agent="%(fail2ban_agent)s"] + +# Report ban via abuseipdb.com. +# +# See action.d/abuseipdb.conf for usage example and details. +# +action_abuseipdb = abuseipdb + +# Choose default action. To change, just override value of 'action' with the +# interpolation to the chosen action shortcut (e.g. action_mw, action_mwl, etc) in jail.local +# globally (section [DEFAULT]) or per specific section +action = %(action_)s + + +# +# JAILS +# + +# +# SSH servers +# + +[sshd] + +# To use more aggressive sshd modes set filter parameter "mode" in jail.local: +# normal (default), ddos, extra or aggressive (combines all). +# See "tests/files/logs/sshd" or "filter.d/sshd.conf" for usage example and details. +#mode = normal +port = ssh +logpath = %(sshd_log)s +backend = %(sshd_backend)s + + +[dropbear] + +port = ssh +logpath = %(dropbear_log)s +backend = %(dropbear_backend)s + + +[selinux-ssh] + +port = ssh +logpath = %(auditd_log)s + + +# +# HTTP servers +# + +[apache-auth] + +port = http,https +logpath = %(apache_error_log)s + + +[apache-badbots] +# Ban hosts which agent identifies spammer robots crawling the web +# for email addresses. The mail outputs are buffered. +port = http,https +logpath = %(apache_access_log)s +bantime = 48h +maxretry = 1 + + +[apache-noscript] + +port = http,https +logpath = %(apache_error_log)s + + +[apache-overflows] + +port = http,https +logpath = %(apache_error_log)s +maxretry = 2 + + +[apache-nohome] + +port = http,https +logpath = %(apache_error_log)s +maxretry = 2 + + +[apache-botsearch] + +port = http,https +logpath = %(apache_error_log)s +maxretry = 2 + + +[apache-fakegooglebot] + +port = http,https +logpath = %(apache_access_log)s +maxretry = 1 +ignorecommand = %(ignorecommands_dir)s/apache-fakegooglebot + + +[apache-modsecurity] + +port = http,https +logpath = %(apache_error_log)s +maxretry = 2 + + +[apache-shellshock] + +port = http,https +logpath = %(apache_error_log)s +maxretry = 1 + + +[openhab-auth] + +filter = openhab +banaction = %(banaction_allports)s +logpath = /opt/openhab/logs/request.log + + +[nginx-http-auth] + +port = http,https +logpath = %(nginx_error_log)s + +# To use 'nginx-limit-req' jail you should have `ngx_http_limit_req_module` +# and define `limit_req` and `limit_req_zone` as described in nginx documentation +# http://nginx.org/en/docs/http/ngx_http_limit_req_module.html +# or for example see in 'config/filter.d/nginx-limit-req.conf' +[nginx-limit-req] +port = http,https +logpath = %(nginx_error_log)s + +[nginx-botsearch] + +port = http,https +logpath = %(nginx_error_log)s +maxretry = 2 + + +# Ban attackers that try to use PHP's URL-fopen() functionality +# through GET/POST variables. - Experimental, with more than a year +# of usage in production environments. + +[php-url-fopen] + +port = http,https +logpath = %(nginx_access_log)s + %(apache_access_log)s + + +[suhosin] + +port = http,https +logpath = %(suhosin_log)s + + +[lighttpd-auth] +# Same as above for Apache's mod_auth +# It catches wrong authentifications +port = http,https +logpath = %(lighttpd_error_log)s + + +# +# Webmail and groupware servers +# + +[roundcube-auth] + +port = http,https +logpath = %(roundcube_errors_log)s +# Use following line in your jail.local if roundcube logs to journal. +#backend = %(syslog_backend)s + + +[openwebmail] + +port = http,https +logpath = /var/log/openwebmail.log + + +[horde] + +port = http,https +logpath = /var/log/horde/horde.log + + +[groupoffice] + +port = http,https +logpath = /home/groupoffice/log/info.log + + +[sogo-auth] +# Monitor SOGo groupware server +# without proxy this would be: +# port = 20000 +port = http,https +logpath = /var/log/sogo/sogo.log + + +[tine20] + +logpath = /var/log/tine20/tine20.log +port = http,https + + +# +# Web Applications +# +# + +[drupal-auth] + +port = http,https +logpath = %(syslog_daemon)s +backend = %(syslog_backend)s + +[guacamole] + +port = http,https +logpath = /var/log/tomcat*/catalina.out +#logpath = /var/log/guacamole.log + +[monit] +#Ban clients brute-forcing the monit gui login +port = 2812 +logpath = /var/log/monit + /var/log/monit.log + + +[webmin-auth] + +port = 10000 +logpath = %(syslog_authpriv)s +backend = %(syslog_backend)s + + +[froxlor-auth] + +port = http,https +logpath = %(syslog_authpriv)s +backend = %(syslog_backend)s + + +# +# HTTP Proxy servers +# +# + +[squid] + +port = 80,443,3128,8080 +logpath = /var/log/squid/access.log + + +[3proxy] + +port = 3128 +logpath = /var/log/3proxy.log + + +# +# FTP servers +# + + +[proftpd] + +port = ftp,ftp-data,ftps,ftps-data +logpath = %(proftpd_log)s +backend = %(proftpd_backend)s + + +[pure-ftpd] + +port = ftp,ftp-data,ftps,ftps-data +logpath = %(pureftpd_log)s +backend = %(pureftpd_backend)s + + +[gssftpd] + +port = ftp,ftp-data,ftps,ftps-data +logpath = %(syslog_daemon)s +backend = %(syslog_backend)s + + +[wuftpd] + +port = ftp,ftp-data,ftps,ftps-data +logpath = %(wuftpd_log)s +backend = %(wuftpd_backend)s + + +[vsftpd] +# or overwrite it in jails.local to be +# logpath = %(syslog_authpriv)s +# if you want to rely on PAM failed login attempts +# vsftpd's failregex should match both of those formats +port = ftp,ftp-data,ftps,ftps-data +logpath = %(vsftpd_log)s + + +# +# Mail servers +# + +# ASSP SMTP Proxy Jail +[assp] + +port = smtp,465,submission +logpath = /root/path/to/assp/logs/maillog.txt + + +[courier-smtp] + +port = smtp,465,submission +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +[postfix] +# To use another modes set filter parameter "mode" in jail.local: +mode = more +port = smtp,465,submission +logpath = %(postfix_log)s +backend = %(postfix_backend)s + + +[postfix-rbl] + +filter = postfix[mode=rbl] +port = smtp,465,submission +logpath = %(postfix_log)s +backend = %(postfix_backend)s +maxretry = 1 + + +[sendmail-auth] + +port = submission,465,smtp +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +[sendmail-reject] +# To use more aggressive modes set filter parameter "mode" in jail.local: +# normal (default), extra or aggressive +# See "tests/files/logs/sendmail-reject" or "filter.d/sendmail-reject.conf" for usage example and details. +#mode = normal +port = smtp,465,submission +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +[qmail-rbl] + +filter = qmail +port = smtp,465,submission +logpath = /service/qmail/log/main/current + + +# dovecot defaults to logging to the mail syslog facility +# but can be set by syslog_facility in the dovecot configuration. +[dovecot] + +port = pop3,pop3s,imap,imaps,submission,465,sieve +logpath = %(dovecot_log)s +backend = %(dovecot_backend)s + + +[sieve] + +port = smtp,465,submission +logpath = %(dovecot_log)s +backend = %(dovecot_backend)s + + +[solid-pop3d] + +port = pop3,pop3s +logpath = %(solidpop3d_log)s + + +[exim] +# see filter.d/exim.conf for further modes supported from filter: +#mode = normal +port = smtp,465,submission +logpath = %(exim_main_log)s + + +[exim-spam] + +port = smtp,465,submission +logpath = %(exim_main_log)s + + +[kerio] + +port = imap,smtp,imaps,465 +logpath = /opt/kerio/mailserver/store/logs/security.log + + +# +# Mail servers authenticators: might be used for smtp,ftp,imap servers, so +# all relevant ports get banned +# + +[courier-auth] + +port = smtp,465,submission,imap,imaps,pop3,pop3s +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +[postfix-sasl] + +filter = postfix[mode=auth] +port = smtp,465,submission,imap,imaps,pop3,pop3s +# You might consider monitoring /var/log/mail.warn instead if you are +# running postfix since it would provide the same log lines at the +# "warn" level but overall at the smaller filesize. +logpath = %(postfix_log)s +backend = %(postfix_backend)s + + +[perdition] + +port = imap,imaps,pop3,pop3s +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +[squirrelmail] + +port = smtp,465,submission,imap,imap2,imaps,pop3,pop3s,http,https,socks +logpath = /var/lib/squirrelmail/prefs/squirrelmail_access_log + + +[cyrus-imap] + +port = imap,imaps +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +[uwimap-auth] + +port = imap,imaps +logpath = %(syslog_mail)s +backend = %(syslog_backend)s + + +# +# +# DNS servers +# + + +# !!! WARNING !!! +# Since UDP is connection-less protocol, spoofing of IP and imitation +# of illegal actions is way too simple. Thus enabling of this filter +# might provide an easy way for implementing a DoS against a chosen +# victim. See +# http://nion.modprobe.de/blog/archives/690-fail2ban-+-dns-fail.html +# Please DO NOT USE this jail unless you know what you are doing. +# +# IMPORTANT: see filter.d/named-refused for instructions to enable logging +# This jail blocks UDP traffic for DNS requests. +# [named-refused-udp] +# +# filter = named-refused +# port = domain,953 +# protocol = udp +# logpath = /var/log/named/security.log + +# IMPORTANT: see filter.d/named-refused for instructions to enable logging +# This jail blocks TCP traffic for DNS requests. + +[named-refused] + +port = domain,953 +logpath = /var/log/named/security.log + + +[nsd] + +port = 53 +action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"] + %(default/action_)s[name=%(__name__)s-udp, protocol="udp"] +logpath = /var/log/nsd.log + + +# +# Miscellaneous +# + +[asterisk] + +port = 5060,5061 +action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"] + %(default/action_)s[name=%(__name__)s-udp, protocol="udp"] +logpath = /var/log/asterisk/messages +maxretry = 10 + + +[freeswitch] + +port = 5060,5061 +action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"] + %(default/action_)s[name=%(__name__)s-udp, protocol="udp"] +logpath = /var/log/freeswitch.log +maxretry = 10 + + +# enable adminlog; it will log to a file inside znc's directory by default. +[znc-adminlog] + +port = 6667 +logpath = /var/lib/znc/moddata/adminlog/znc.log + + +# To log wrong MySQL access attempts add to /etc/my.cnf in [mysqld] or +# equivalent section: +# log-warnings = 2 +# +# for syslog (daemon facility) +# [mysqld_safe] +# syslog +# +# for own logfile +# [mysqld] +# log-error=/var/log/mysqld.log +[mysqld-auth] + +port = 3306 +logpath = %(mysql_log)s +backend = %(mysql_backend)s + + +# Log wrong MongoDB auth (for details see filter 'filter.d/mongodb-auth.conf') +[mongodb-auth] +# change port when running with "--shardsvr" or "--configsvr" runtime operation +port = 27017 +logpath = /var/log/mongodb/mongodb.log + + +# Jail for more extended banning of persistent abusers +# !!! WARNINGS !!! +# 1. Make sure that your loglevel specified in fail2ban.conf/.local +# is not at DEBUG level -- which might then cause fail2ban to fall into +# an infinite loop constantly feeding itself with non-informative lines +# 2. Increase dbpurgeage defined in fail2ban.conf to e.g. 648000 (7.5 days) +# to maintain entries for failed logins for sufficient amount of time +[recidive] + +logpath = /var/log/fail2ban.log +#banaction = %(banaction_allports)s +banaction = nftables-allports +bantime = 1w +findtime = 1d + + +# Generic filter for PAM. Has to be used with action which bans all +# ports such as iptables-allports, shorewall + +[pam-generic] +# pam-generic filter can be customized to monitor specific subset of 'tty's +banaction = %(banaction_allports)s +logpath = %(syslog_authpriv)s +backend = %(syslog_backend)s + + +[xinetd-fail] + +banaction = iptables-multiport-log +logpath = %(syslog_daemon)s +backend = %(syslog_backend)s +maxretry = 2 + + +# stunnel - need to set port for this +[stunnel] + +logpath = /var/log/stunnel4/stunnel.log + + +[ejabberd-auth] + +port = 5222 +logpath = /var/log/ejabberd/ejabberd.log + + +[counter-strike] + +logpath = /opt/cstrike/logs/L[0-9]*.log +tcpport = 27030,27031,27032,27033,27034,27035,27036,27037,27038,27039 +udpport = 1200,27000,27001,27002,27003,27004,27005,27006,27007,27008,27009,27010,27011,27012,27013,27014,27015 +action_ = %(default/action_)s[name=%(__name__)s-tcp, port="%(tcpport)s", protocol="tcp"] + %(default/action_)s[name=%(__name__)s-udp, port="%(udpport)s", protocol="udp"] + +[softethervpn] +port = 500,4500 +protocol = udp +logpath = /usr/local/vpnserver/security_log/*/sec.log + +[gitlab] +port = http,https +logpath = /var/log/gitlab/gitlab-rails/application.log + +[grafana] +port = http,https +logpath = /var/log/grafana/grafana.log + +[bitwarden] +port = http,https +logpath = /home/*/bwdata/logs/identity/Identity/log.txt + +[centreon] +port = http,https +logpath = /var/log/centreon/login.log + +# consider low maxretry and a long bantime +# nobody except your own Nagios server should ever probe nrpe +[nagios] + +logpath = %(syslog_daemon)s ; nrpe.cfg may define a different log_facility +backend = %(syslog_backend)s +maxretry = 1 + + +[oracleims] +# see "oracleims" filter file for configuration requirement for Oracle IMS v6 and above +logpath = /opt/sun/comms/messaging64/log/mail.log_current +banaction = %(banaction_allports)s + +[directadmin] +logpath = /var/log/directadmin/login.log +port = 2222 + +[portsentry] +logpath = /var/lib/portsentry/portsentry.history +maxretry = 1 + +[pass2allow-ftp] +# this pass2allow example allows FTP traffic after successful HTTP authentication +port = ftp,ftp-data,ftps,ftps-data +# knocking_url variable must be overridden to some secret value in jail.local +knocking_url = /knocking/ +filter = apache-pass[knocking_url="%(knocking_url)s"] +# access log of the website with HTTP auth +logpath = %(apache_access_log)s +blocktype = RETURN +returntype = DROP +action = %(action_)s[blocktype=%(blocktype)s, returntype=%(returntype)s, + actionstart_on_demand=false, actionrepair_on_unban=true] +bantime = 1h +maxretry = 1 +findtime = 1 + + +[murmur] +# AKA mumble-server +port = 64738 +action_ = %(default/action_)s[name=%(__name__)s-tcp, protocol="tcp"] + %(default/action_)s[name=%(__name__)s-udp, protocol="udp"] +logpath = /var/log/mumble-server/mumble-server.log + + +[screensharingd] +# For Mac OS Screen Sharing Service (VNC) +logpath = /var/log/system.log +logencoding = utf-8 + +[haproxy-http-auth] +# HAProxy by default doesn't log to file you'll need to set it up to forward +# logs to a syslog server which would then write them to disk. +# See "haproxy-http-auth" filter for a brief cautionary note when setting +# maxretry and findtime. +logpath = /var/log/haproxy.log + +[slapd] +port = ldap,ldaps +logpath = /var/log/slapd.log + +[domino-smtp] +port = smtp,ssmtp +logpath = /home/domino01/data/IBM_TECHNICAL_SUPPORT/console.log + +[phpmyadmin-syslog] +port = http,https +logpath = %(syslog_authpriv)s +backend = %(syslog_backend)s + + +[zoneminder] +# Zoneminder HTTP/HTTPS web interface auth +# Logs auth failures to apache2 error log +port = http,https +logpath = %(apache_error_log)s + +[traefik-auth] +# to use 'traefik-auth' filter you have to configure your Traefik instance, +# see `filter.d/traefik-auth.conf` for details and service example. +port = http,https +logpath = /var/log/traefik/access.log diff --git a/gardenlinux/features/ssh/file.include/etc/ssh/ssh_config b/gardenlinux/features/ssh/file.include/etc/ssh/ssh_config new file mode 100644 index 0000000..4a282cc --- /dev/null +++ b/gardenlinux/features/ssh/file.include/etc/ssh/ssh_config @@ -0,0 +1,17 @@ +Host * +Protocol 2 +ForwardAgent no +ForwardX11 no +HostbasedAuthentication no +StrictHostKeyChecking no + +# Use secure ciphers +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com +KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 + +Tunnel no + +# Compute Engine times out connections after 10 minutes of inactivity. +# Keep alive ssh connections by sending a packet every 7 minutes. +ServerAliveInterval 420 diff --git a/gardenlinux/features/ssh/file.include/etc/ssh/sshd_config b/gardenlinux/features/ssh/file.include/etc/ssh/sshd_config new file mode 100644 index 0000000..2121f59 --- /dev/null +++ b/gardenlinux/features/ssh/file.include/etc/ssh/sshd_config @@ -0,0 +1,46 @@ +# Disable root login. +PermitRootLogin no + +# Only use the more secure SSHv2 protocol. +Protocol 2 + +# No X11 forwarding. +X11Forwarding no + +# Check permissions of configuration files related to SSH on login. +# If this fails, the user won’t be able to login. +StrictModes yes + +# Disable host-based authentications. +IgnoreRhosts yes +HostbasedAuthentication no + +# Set log level to be verbose. +# we need to log the fingerprint +LogLevel VERBOSE + +# Ensure usage of PAM +UsePAM yes + +# Disable message of the day +PrintMotd no + +# Allow client to pass locale environment variables +AcceptEnv LANG + +# override default of no subsystems +Subsystem sftp /usr/lib/openssh/sftp-server -f AUTHPRIV -l INFO + +# autologout inactive users after 10 minutes +ClientAliveInterval 600 +ClientAliveCountMax 0 + +# Password based logins are disabled - only public key based logins are allowed. +AuthenticationMethods publickey + +# Supported HostKey algorithms by order of preference. +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key + +# Add ssh-rsa to allowed HostKeyAlgorithms +HostKeyAlgorithms=+ssh-rsa diff --git a/gardenlinux/features/ssh/file.include/etc/systemd/system/fail2ban.service.d/override.conf b/gardenlinux/features/ssh/file.include/etc/systemd/system/fail2ban.service.d/override.conf new file mode 100644 index 0000000..0911c36 --- /dev/null +++ b/gardenlinux/features/ssh/file.include/etc/systemd/system/fail2ban.service.d/override.conf @@ -0,0 +1,6 @@ +[Unit] +Requires=nftables.service +PartOf=nftables.service + +[Install] +WantedBy=multi-user.target nftables.service diff --git a/gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-keygen.service b/gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-keygen.service new file mode 100644 index 0000000..3995acd --- /dev/null +++ b/gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-keygen.service @@ -0,0 +1,19 @@ +[Unit] +Description=SSH Key Generation +ConditionPathExists=|!/etc/ssh/ssh_host_dsa_key +ConditionPathExists=|!/etc/ssh/ssh_host_dsa_key.pub +ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key +ConditionPathExists=|!/etc/ssh/ssh_host_ecdsa_key.pub +ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key +ConditionPathExists=|!/etc/ssh/ssh_host_ed25519_key.pub +ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key +ConditionPathExists=|!/etc/ssh/ssh_host_rsa_key.pub +Before=ssh.service + +[Service] +ExecStart=/usr/bin/ssh-keygen -A +Type=oneshot +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-moduli.service b/gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-moduli.service new file mode 100644 index 0000000..45d8433 --- /dev/null +++ b/gardenlinux/features/ssh/file.include/etc/systemd/system/ssh-moduli.service @@ -0,0 +1,13 @@ +[Unit] +Description=SSH Moduli Generation +ConditionPathExists=|!/etc/ssh/moduli + +[Service] +ExecStart=ssh-keygen -M generate -O bits=2048 /etc/ssh/candidates +ExecStart=ssh-keygen -M screen -f /etc/ssh/candidates /etc/ssh/moduli +ExecStart=rm /etc/ssh/candidates +Type=oneshot +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/gardenlinux/features/ssh/info.yaml b/gardenlinux/features/ssh/info.yaml new file mode 100644 index 0000000..0615463 --- /dev/null +++ b/gardenlinux/features/ssh/info.yaml @@ -0,0 +1,5 @@ +description: "OpenSSH server" +type: element +features: + include: + - firewall diff --git a/gardenlinux/features/ssh/pkg.include b/gardenlinux/features/ssh/pkg.include new file mode 100644 index 0000000..5d904ae --- /dev/null +++ b/gardenlinux/features/ssh/pkg.include @@ -0,0 +1,3 @@ +openssh-server +fail2ban +python3-systemd diff --git a/gardenlinux/features/ssh/test/test_ssh_authorized.py b/gardenlinux/features/ssh/test/test_ssh_authorized.py new file mode 100644 index 0000000..d3bdf31 --- /dev/null +++ b/gardenlinux/features/ssh/test/test_ssh_authorized.py @@ -0,0 +1 @@ +from helper.tests.ssh_authorized import ssh_authorized as test_ssh_authorized diff --git a/gardenlinux/features/ssh/test/test_sshd.py b/gardenlinux/features/ssh/test/test_sshd.py new file mode 100644 index 0000000..1a91234 --- /dev/null +++ b/gardenlinux/features/ssh/test/test_sshd.py @@ -0,0 +1,48 @@ +from helper.tests.sshd import sshd +import pytest + +@pytest.mark.parametrize( + "expected", + [ + # Supported HostKey algorithms. + "HostKey /etc/ssh/ssh_host_ed25519_key", + "HostKey /etc/ssh/ssh_host_rsa_key", + #"HostKey /etc/ssh/ssh_host_ecdsa_key", + "KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256", + + "Ciphers chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com", + + "MACs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1", + + # Password based logins are disabled - only public key based logins are allowed. + "AuthenticationMethods publickey", + + # LogLevel VERBOSE logs user's key fingerprint on login. Needed to have a clear audit track of which key was using to log in. + "LogLevel VERBOSE", + + # Log sftp level file access (read/write/etc.) that would not be easily logged otherwise - be aware that the path has to be adapted to the Distribution installed. + "Subsystem sftp /usr/lib/openssh/sftp-server -f AUTHPRIV -l INFO", + + # Root login is not allowed for auditing reasons. This is because it's difficult to track which process belongs to which root user: + # + # On Linux, user sessions are tracking using a kernel-side session id, however, this session id is not recorded by OpenSSH. + # Additionally, only tools such as systemd and auditd record the process session id. + # On other OSes, the user session id is not necessarily recorded at all kernel-side. + # Using regular users in combination with /bin/su or /usr/bin/sudo ensure a clear audit track. + "PermitRootLogin No", + + # Use kernel sandbox mechanisms where possible in unprivileged processes + # Systrace on OpenBSD, Seccomp on Linux, seatbelt on MacOSX/Darwin, rlimit elsewhere. + #"UsePrivilegeSeparation sandbox", + + # Disable Tunneling and Forwarding + # AllowTcpForwarding no # required for Gardener + "X11Forwarding no", + + #use PAM + "UsePAM yes" + ] +) + +def test_sshd(client, expected): + sshd(client, expected) \ No newline at end of file diff --git a/gardenlinux/features/vmware/README.md b/gardenlinux/features/vmware/README.md new file mode 100644 index 0000000..1e551ca --- /dev/null +++ b/gardenlinux/features/vmware/README.md @@ -0,0 +1,24 @@ +## Feature: vmware +### Description + +This platform feature creates an artifact for VMWare platforms. + + +### Features +This feature creates an VMWare platform compatible image artifact as an `.ova` file. During the build `.raw`, `.vmdk` and the converted `.ova` artifacts are created. + +All artifacts include further tools for orchestrating the image like `cloud-init` and `open-vm-tools`. + +Additionally, further changes for `growpart`, `ntp` etc. are applied and validated. + +### Unit testing +This platform feature supports unit testing and is based on the `chroot` and `kvm` fixture to validate the applied changes according its feature configuration. + + Testing of specific units can be avoided by placing the `non_container` fixture. +### Meta +||| +|---|---| +|type|platform| +|artifact|`.vmdk`,`.ova`| +|included_features|cloud| +|excluded_features|None| \ No newline at end of file diff --git a/gardenlinux/features/vmware/convert.ova b/gardenlinux/features/vmware/convert.ova new file mode 100755 index 0000000..aa5fc9f --- /dev/null +++ b/gardenlinux/features/vmware/convert.ova @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +input="$(realpath "$1")" +output="$(realpath "$2")" +cname="$(basename "$output" .ova)" + +dir="$(mktemp -d)" +pushd "$dir" + +qemu-img convert -o subformat=streamOptimized -o adapter_type=lsilogic -f raw -O vmdk "$input" "$cname.vmdk" +/builder/features/vmware/make-ova --vmdk "$cname.vmdk" --guest-id debian10_64Guest --template /builder/features/vmware/vmware.ovf.template + +tar -tf "$cname.ova" + +cp --sparse=always "$cname.ova" "$output" + +popd +rm -rf "$dir" diff --git a/gardenlinux/features/vmware/exec.config b/gardenlinux/features/vmware/exec.config new file mode 100755 index 0000000..c9dce7c --- /dev/null +++ b/gardenlinux/features/vmware/exec.config @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +# growpart is done in initramfs, growroot by systemd +mv /etc/cloud/cloud.cfg /etc/cloud/cloud.cfg.bak +cat /etc/cloud/cloud.cfg.bak | grep -v "^ - growpart$" | grep -v "^ - resizefs$" | grep -v "^ - ntp$" >/etc/cloud/cloud.cfg +rm /etc/cloud/cloud.cfg.bak diff --git a/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg b/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg new file mode 100644 index 0000000..b1504cd --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg @@ -0,0 +1,13 @@ +apt_preserve_sources_list: true +manage_etc_hosts: true +system_info: + default_user: + name: admin + sudo: ALL=(ALL) NOPASSWD:ALL + shell: /bin/bash + lock_passwd: True + gecos: Debian + groups: [adm, audio, cdrom, dialout, dip, floppy, netdev, plugdev, sudo, video] + sudo: ["ALL=(ALL) NOPASSWD:ALL"] + shell: /bin/bash + diff --git a/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg b/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg new file mode 100644 index 0000000..f144451 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg @@ -0,0 +1 @@ +network: {config: disabled} diff --git a/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_enabled-datasources.cfg b/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_enabled-datasources.cfg new file mode 100644 index 0000000..a79aae5 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/cloud/cloud.cfg.d/99_enabled-datasources.cfg @@ -0,0 +1,13 @@ +# Cloud-Init Datasource for VMware Guestinfo +# +# Copyright (c) 2018 VMware, Inc. All Rights Reserved. +# +# This product is licensed to you under the Apache 2.0 license (the "License"). +# You may not use this product except in compliance with the Apache 2.0 License. +# +# This product may include a number of subcomponents with separate copyright +# notices and license terms. Your use of these subcomponents is subject to the +# terms and conditions of the subcomponent's license, as noted in the LICENSE +# file. + +datasource_list: [ "VMwareGuestInfo", "OVF" ] diff --git a/gardenlinux/features/vmware/file.include/etc/default/grub.cfg b/gardenlinux/features/vmware/file.include/etc/default/grub.cfg new file mode 100644 index 0000000..f0ae409 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/default/grub.cfg @@ -0,0 +1,15 @@ +# If you change this file, run 'update-grub' afterwards to update +# /boot/grub/grub.cfg. +# For full documentation of the options in this file, see: +# info -f grub -n 'Simple configuration' + +GRUB_DEFAULT=0 +GRUB_TIMEOUT=0 +GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` +GRUB_CMDLINE_LINUX_DEFAULT="" +GRUB_CMDLINE_LINUX="" +GRUB_TERMINAL="" +GRUB_SERIAL_COMMAND="" +GRUB_DISABLE_LINUX_UUID=true +GRUB_DISABLE_LINUX_PARTUUID=true +GRUB_DEVICE="LABEL=ROOT" diff --git a/gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/10-console.cfg b/gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/10-console.cfg new file mode 100644 index 0000000..f2cd791 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/10-console.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="console=ttyS0,115200 console=tty0 $CMDLINE_LINUX" diff --git a/gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/50-ignition.cfg b/gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/50-ignition.cfg new file mode 100644 index 0000000..0996af1 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/kernel/cmdline.d/50-ignition.cfg @@ -0,0 +1 @@ +CMDLINE_LINUX="$CMDLINE_LINUX ignition.firstboot=1 ignition.platform.id=vmware" diff --git a/gardenlinux/features/vmware/file.include/etc/systemd/system/ignition-disable.service b/gardenlinux/features/vmware/file.include/etc/systemd/system/ignition-disable.service new file mode 100644 index 0000000..1ad9530 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/etc/systemd/system/ignition-disable.service @@ -0,0 +1,24 @@ +[Unit] +# Based on https://github.com/coreos/ignition/blob/main/systemd/ignition-delete-config.service +Description=Ignition disable after first boot + +ConditionFirstBoot=true +ConditionPathExists=/run/ignition.env +RequiresMountsFor=/boot/efi + +DefaultDependencies=no +Before=sysinit.target +After=systemd-tmpfiles-setup.service + +# TODO : do we really need to fail to boot? +OnFailure=emergency.target +OnFailureJobMode=isolate + +[Service] +Type=oneshot +EnvironmentFile=/run/ignition.env +ExecStart=/bin/bash -c "rm -f /etc/kernel/cmdline.d/50-ignition.cfg && /usr/local/sbin/update-bootloaders" +RemainAfterExit=yes + +[Install] +WantedBy=sysinit.target diff --git a/gardenlinux/features/vmware/file.include/usr/bin/dscheck_VMwareGuestInfo b/gardenlinux/features/vmware/file.include/usr/bin/dscheck_VMwareGuestInfo new file mode 100755 index 0000000..e226b9f --- /dev/null +++ b/gardenlinux/features/vmware/file.include/usr/bin/dscheck_VMwareGuestInfo @@ -0,0 +1,54 @@ +#!/bin/sh + +# Cloud-Init Datasource for VMware Guestinfo +# +# Copyright (c) 2019 VMware, Inc. All Rights Reserved. +# +# This product is licensed to you under the Apache 2.0 license (the "License"). +# You may not use this product except in compliance with the Apache 2.0 License. +# +# This product may include a number of subcomponents with separate copyright +# notices and license terms. Your use of these subcomponents is subject to the +# terms and conditions of the subcomponent's license, as noted in the LICENSE +# file. + +# +# This file should be installed to /usr/bin/dscheck_VMwareGuestInfo +# without the ".sh" extension. The extension only exists to make it easier +# to identify the file during development. +# +# This file provides cloud-init's ds-identify program a shell type that +# can be resolved with "type dscheck_VMwareGuestInfo" and used to validate +# where a datasource is installed and useable. +# +# Cloud-init's ds-identify program in /usr/lib/cloud-init includes functions +# to determine whether or not datasources can be used. Because the program +# is a shell script and uses "type dscheck_DATASOURCE_NAME" to determine +# if there is a matching bash type that can answer for the datasource, +# it's possible to respond with an external script. While other datasources +# have functions in ds-identify, the "type" command looks up types both +# in Bash's function table as well as script in the PATH. Therefore the +# ds-identify program, when looking up whether or not the datasource +# VMwareGuestInfo can be used, will defer to this file when it is in the +# PATH and named dscheck_VMwareGuestInfo. +# + +if [ -n "${VMX_GUESTINFO}" ]; then + if [ -n "${VMX_GUESTINFO_METADATA}" ] || \ + [ -n "${VMX_GUESTINFO_USERDATA}" ] || \ + [ -n "${VMX_GUESTINFO_VENDORDATA}" ]; then + exit 0 + fi +fi + +if ! command -v vmware-rpctool >/dev/null 2>&1; then + exit 1 +fi + +if { vmware-rpctool "info-get guestinfo.metadata" || \ + vmware-rpctool "info-get guestinfo.userdata" || \ + vmware-rpctool "info-get guestinfo.vendordata"; } >/dev/null 2>&1; then + exit 0 +fi + +exit 1 diff --git a/gardenlinux/features/vmware/file.include/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceVMwareGuestInfo.py b/gardenlinux/features/vmware/file.include/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceVMwareGuestInfo.py new file mode 100644 index 0000000..19d20a2 --- /dev/null +++ b/gardenlinux/features/vmware/file.include/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceVMwareGuestInfo.py @@ -0,0 +1,781 @@ +# Cloud-Init Datasource for VMware Guestinfo +# +# Copyright (c) 2018 VMware, Inc. All Rights Reserved. +# +# This product is licensed to you under the Apache 2.0 license (the "License"). +# You may not use this product except in compliance with the Apache 2.0 License. +# +# This product may include a number of subcomponents with separate copyright +# notices and license terms. Your use of these subcomponents is subject to the +# terms and conditions of the subcomponent's license, as noted in the LICENSE +# file. +# +# Authors: Anish Swaminathan +# Andrew Kutz +# + +''' +A cloud init datasource for VMware GuestInfo. +''' + +import base64 +import collections +import copy +from distutils.spawn import find_executable +from distutils.util import strtobool +import ipaddress +import json +import os +import socket +import string +import time +import zlib + +from cloudinit import log as logging +from cloudinit import sources +from cloudinit import util +from cloudinit import safeyaml + +import netifaces + +# from cloud-init >= 20.3 subp is in its own module +try: + from cloudinit.subp import subp, ProcessExecutionError +except ImportError: + from cloudinit.util import subp, ProcessExecutionError + + +LOG = logging.getLogger(__name__) +NOVAL = "No value found" +VMWARE_RPCTOOL = find_executable("vmware-rpctool") +VMX_GUESTINFO = "VMX_GUESTINFO" +GUESTINFO_EMPTY_YAML_VAL = "---" +LOCAL_IPV4 = 'local-ipv4' +LOCAL_IPV6 = 'local-ipv6' +CLEANUP_GUESTINFO = 'cleanup-guestinfo' +WAIT_ON_NETWORK = 'wait-on-network' +WAIT_ON_NETWORK_IPV4 = 'ipv4' +WAIT_ON_NETWORK_IPV6 = 'ipv6' + + +class NetworkConfigError(Exception): + ''' + NetworkConfigError is raised when there is an issue getting or + applying network configuration. + ''' + pass + + +class DataSourceVMwareGuestInfo(sources.DataSource): + ''' + This cloud-init datasource was designed for use with CentOS 7, + which uses cloud-init 0.7.9. However, this datasource should + work with any Linux distribution for which cloud-init is + avaialble. + + The documentation for cloud-init 0.7.9's datasource is + available at http://bit.ly/cloudinit-datasource-0-7-9. The + current documentation for cloud-init is found at + https://cloudinit.readthedocs.io/en/latest/. + + Setting the hostname: + The hostname is set by way of the metadata key "local-hostname". + + Setting the instance ID: + The instance ID may be set by way of the metadata key "instance-id". + However, if this value is absent then then the instance ID is + read from the file /sys/class/dmi/id/product_uuid. + + Configuring the network: + The network is configured by setting the metadata key "network" + with a value consistent with Network Config Versions 1 or 2, + depending on the Linux distro's version of cloud-init: + + Network Config Version 1 - http://bit.ly/cloudinit-net-conf-v1 + Network Config Version 2 - http://bit.ly/cloudinit-net-conf-v2 + + For example, CentOS 7's official cloud-init package is version + 0.7.9 and does not support Network Config Version 2. However, + this datasource still supports supplying Network Config Version 2 + data as long as the Linux distro's cloud-init package is new + enough to parse the data. + + The metadata key "network.encoding" may be used to indicate the + format of the metadata key "network". Valid encodings are base64 + and gzip+base64. + ''' + + dsname = 'VMwareGuestInfo' + + def __init__(self, sys_cfg, distro, paths, ud_proc=None): + sources.DataSource.__init__(self, sys_cfg, distro, paths, ud_proc) + if not get_data_access_method(): + LOG.error("Failed to find vmware-rpctool") + + def get_data(self): + """ + This method should really be _get_data in accordance with the most + recent versions of cloud-init. However, because the datasource + supports as far back as cloud-init 0.7.9, get_data is still used. + + Because of this the method attempts to do some of the same things + that the get_data functions in newer versions of cloud-init do, + such as calling persist_instance_data. + """ + data_access_method = get_data_access_method() + if not data_access_method: + LOG.error("vmware-rpctool is required to fetch guestinfo value") + return False + + # Get the metadata. + self.metadata = load_metadata() + + # Get the user data. + self.userdata_raw = guestinfo('userdata') + + # Get the vendor data. + self.vendordata_raw = guestinfo('vendordata') + + # Check to see if any of the guestinfo data should be removed. + if data_access_method == VMWARE_RPCTOOL and CLEANUP_GUESTINFO in self.metadata: + clear_guestinfo_keys(self.metadata[CLEANUP_GUESTINFO]) + + if self.metadata or self.userdata_raw or self.vendordata_raw: + return True + else: + return False + + def setup(self, is_new_instance): + """setup(is_new_instance) + + This is called before user-data and vendor-data have been processed. + + Unless the datasource has set mode to 'local', then networking + per 'fallback' or per 'network_config' will have been written and + brought up the OS at this point. + """ + + host_info = wait_on_network(self.metadata) + LOG.info("got host-info: %s", host_info) + + # Reflect any possible local IPv4 or IPv6 addresses in the guest + # info. + advertise_local_ip_addrs(host_info) + + # Ensure the metadata gets updated with information about the + # host, including the network interfaces, default IP addresses, + # etc. + self.metadata = merge_dicts(self.metadata, host_info) + + # Persist the instance data for versions of cloud-init that support + # doing so. This occurs here rather than in the get_data call in + # order to ensure that the network interfaces are up and can be + # persisted with the metadata. + try: + self.persist_instance_data() + except AttributeError: + pass + + @property + def network_config(self): + if 'network' in self.metadata: + LOG.debug("using metadata network config") + else: + LOG.debug("using fallback network config") + self.metadata['network'] = { + 'config': self.distro.generate_fallback_config(), + } + return self.metadata['network']['config'] + + def get_instance_id(self): + # Pull the instance ID out of the metadata if present. Otherwise + # read the file /sys/class/dmi/id/product_uuid for the instance ID. + if self.metadata and 'instance-id' in self.metadata: + return self.metadata['instance-id'] + with open('/sys/class/dmi/id/product_uuid', 'r') as id_file: + self.metadata['instance-id'] = str(id_file.read()).rstrip().lower() + return self.metadata['instance-id'] + + def get_public_ssh_keys(self): + public_keys_data = "" + if 'public-keys-data' in self.metadata: + public_keys_data = self.metadata['public-keys-data'].splitlines() + + public_keys = [] + if not public_keys_data: + return public_keys + + for public_key in public_keys_data: + public_keys.append(public_key) + + return public_keys + + +def decode(key, enc_type, data): + ''' + decode returns the decoded string value of data + key is a string used to identify the data being decoded in log messages + ---- + In py 2.7: + json.loads method takes string as input + zlib.decompress takes and returns a string + base64.b64decode takes and returns a string + ----- + In py 3.6 and newer: + json.loads method takes bytes or string as input + zlib.decompress takes and returns a bytes + base64.b64decode takes bytes or string and returns bytes + ----- + In py > 3, < 3.6: + json.loads method takes string as input + zlib.decompress takes and returns a bytes + base64.b64decode takes bytes or string and returns bytes + ----- + Given the above conditions the output from zlib.decompress and + base64.b64decode would be bytes with newer python and str in older + version. Thus we would covert the output to str before returning + ''' + LOG.debug("Getting encoded data for key=%s, enc=%s", key, enc_type) + + raw_data = None + if enc_type == "gzip+base64" or enc_type == "gz+b64": + LOG.debug("Decoding %s format %s", enc_type, key) + raw_data = zlib.decompress(base64.b64decode(data), zlib.MAX_WBITS | 16) + elif enc_type == "base64" or enc_type == "b64": + LOG.debug("Decoding %s format %s", enc_type, key) + raw_data = base64.b64decode(data) + else: + LOG.debug("Plain-text data %s", key) + raw_data = data + + if isinstance(raw_data, bytes): + return raw_data.decode('utf-8') + return raw_data + + +def get_none_if_empty_val(val): + ''' + get_none_if_empty_val returns None if the provided value, once stripped + of its trailing whitespace, is empty or equal to GUESTINFO_EMPTY_YAML_VAL. + + The return value is always a string, regardless of whether the input is + a bytes class or a string. + ''' + + # If the provided value is a bytes class, convert it to a string to + # simplify the rest of this function's logic. + if isinstance(val, bytes): + val = val.decode() + + val = val.rstrip() + if len(val) == 0 or val == GUESTINFO_EMPTY_YAML_VAL: + return None + return val + + +def advertise_local_ip_addrs(host_info): + ''' + advertise_local_ip_addrs gets the local IP address information from + the provided host_info map and sets the addresses in the guestinfo + namespace + ''' + if not host_info: + return + + # Reflect any possible local IPv4 or IPv6 addresses in the guest + # info. + local_ipv4 = host_info.get(LOCAL_IPV4) + if local_ipv4: + set_guestinfo_value(LOCAL_IPV4, local_ipv4) + LOG.info("advertised local ipv4 address %s in guestinfo", local_ipv4) + + local_ipv6 = host_info.get(LOCAL_IPV6) + if local_ipv6: + set_guestinfo_value(LOCAL_IPV6, local_ipv6) + LOG.info("advertised local ipv6 address %s in guestinfo", local_ipv6) + + +def handle_returned_guestinfo_val(key, val): + ''' + handle_returned_guestinfo_val returns the provided value if it is + not empty or set to GUESTINFO_EMPTY_YAML_VAL, otherwise None is + returned + ''' + val = get_none_if_empty_val(val) + if val: + return val + LOG.debug("No value found for key %s", key) + return None + + +def get_guestinfo_value(key): + ''' + Returns a guestinfo value for the specified key. + ''' + LOG.debug("Getting guestinfo value for key %s", key) + + data_access_method = get_data_access_method() + + if data_access_method == VMX_GUESTINFO: + env_key = ("vmx.guestinfo." + key).upper().replace(".", "_", -1) + return handle_returned_guestinfo_val(key, os.environ.get(env_key, "")) + + if data_access_method == VMWARE_RPCTOOL: + try: + (stdout, stderr) = subp( + [VMWARE_RPCTOOL, "info-get guestinfo." + key]) + if stderr == NOVAL: + LOG.debug("No value found for key %s", key) + elif not stdout: + LOG.error("Failed to get guestinfo value for key %s", key) + else: + return handle_returned_guestinfo_val(key, stdout) + except ProcessExecutionError as error: + if error.stderr == NOVAL: + LOG.debug("No value found for key %s", key) + else: + util.logexc( + LOG, "Failed to get guestinfo value for key %s: %s", key, error) + except Exception: + util.logexc( + LOG, "Unexpected error while trying to get guestinfo value for key %s", key) + + return None + + +def set_guestinfo_value(key, value): + ''' + Sets a guestinfo value for the specified key. Set value to an empty string + to clear an existing guestinfo key. + ''' + + # If value is an empty string then set it to a single space as it is not + # possible to set a guestinfo key to an empty string. Setting a guestinfo + # key to a single space is as close as it gets to clearing an existing + # guestinfo key. + if value == "": + value = " " + + LOG.debug("Setting guestinfo key=%s to value=%s", key, value) + + data_access_method = get_data_access_method() + + if data_access_method == VMX_GUESTINFO: + return True + + if data_access_method == VMWARE_RPCTOOL: + try: + subp([VMWARE_RPCTOOL, ("info-set guestinfo.%s %s" % (key, value))]) + return True + except ProcessExecutionError as error: + util.logexc( + LOG, "Failed to set guestinfo key=%s to value=%s: %s", key, value, error) + except Exception: + util.logexc( + LOG, "Unexpected error while trying to set guestinfo key=%s to value=%s", key, value) + + return None + + +def clear_guestinfo_keys(keys): + ''' + clear_guestinfo_keys clears guestinfo of all of the keys in the given list. + each key will have its value set to "---". Since the value is valid YAML, + cloud-init can still read it if it tries. + ''' + if not keys: + return + if not type(keys) in (list, tuple): + keys = [keys] + for key in keys: + LOG.info("clearing guestinfo.%s", key) + if not set_guestinfo_value(key, GUESTINFO_EMPTY_YAML_VAL): + LOG.error("failed to clear guestinfo.%s", key) + LOG.info("clearing guestinfo.%s.encoding", key) + if not set_guestinfo_value(key + ".encoding", ""): + LOG.error("failed to clear guestinfo.%s.encoding", key) + + +def guestinfo(key): + ''' + guestinfo returns the guestinfo value for the provided key, decoding + the value when required + ''' + data = get_guestinfo_value(key) + if not data: + return None + enc_type = get_guestinfo_value(key + '.encoding') + return decode('guestinfo.' + key, enc_type, data) + + +def load(data): + ''' + load first attempts to unmarshal the provided data as JSON, and if + that fails then attempts to unmarshal the data as YAML. If data is + None then a new dictionary is returned. + ''' + if not data: + return {} + try: + return json.loads(data) + except: + return safeyaml.load(data) + + +def load_metadata(): + ''' + load_metadata loads the metadata from the guestinfo data, optionally + decoding the network config when required + ''' + data = load(guestinfo('metadata')) + LOG.debug('loaded metadata %s', data) + + network = None + if 'network' in data: + network = data['network'] + del data['network'] + + network_enc = None + if 'network.encoding' in data: + network_enc = data['network.encoding'] + del data['network.encoding'] + + if network: + LOG.debug('network data found') + if isinstance(network, collections.Mapping): + LOG.debug("network data copied to 'config' key") + network = { + 'config': copy.deepcopy(network) + } + else: + LOG.debug("network data to be decoded %s", network) + dec_net = decode('metadata.network', network_enc, network) + network = { + 'config': load(dec_net), + } + + LOG.debug('network data %s', network) + data['network'] = network + + return data + + +def get_datasource_list(depends): + ''' + Return a list of data sources that match this set of dependencies + ''' + return [DataSourceVMwareGuestInfo] + + +def get_default_ip_addrs(): + ''' + Returns the default IPv4 and IPv6 addresses based on the device(s) used for + the default route. Please note that None may be returned for either address + family if that family has no default route or if there are multiple + addresses associated with the device used by the default route for a given + address. + ''' + gateways = netifaces.gateways() + if 'default' not in gateways: + return None, None + + default_gw = gateways['default'] + if netifaces.AF_INET not in default_gw and netifaces.AF_INET6 not in default_gw: + return None, None + + ipv4 = None + ipv6 = None + + gw4 = default_gw.get(netifaces.AF_INET) + if gw4: + _, dev4 = gw4 + addr4_fams = netifaces.ifaddresses(dev4) + if addr4_fams: + af_inet4 = addr4_fams.get(netifaces.AF_INET) + if af_inet4: + if len(af_inet4) > 1: + LOG.warn( + "device %s has more than one ipv4 address: %s", dev4, af_inet4) + elif 'addr' in af_inet4[0]: + ipv4 = af_inet4[0]['addr'] + + # Try to get the default IPv6 address by first seeing if there is a default + # IPv6 route. + gw6 = default_gw.get(netifaces.AF_INET6) + if gw6: + _, dev6 = gw6 + addr6_fams = netifaces.ifaddresses(dev6) + if addr6_fams: + af_inet6 = addr6_fams.get(netifaces.AF_INET6) + if af_inet6: + if len(af_inet6) > 1: + LOG.warn( + "device %s has more than one ipv6 address: %s", dev6, af_inet6) + elif 'addr' in af_inet6[0]: + ipv6 = af_inet6[0]['addr'] + + # If there is a default IPv4 address but not IPv6, then see if there is a + # single IPv6 address associated with the same device associated with the + # default IPv4 address. + if ipv4 and not ipv6: + af_inet6 = addr4_fams.get(netifaces.AF_INET6) + if af_inet6: + if len(af_inet6) > 1: + LOG.warn( + "device %s has more than one ipv6 address: %s", dev4, af_inet6) + elif 'addr' in af_inet6[0]: + ipv6 = af_inet6[0]['addr'] + + # If there is a default IPv6 address but not IPv4, then see if there is a + # single IPv4 address associated with the same device associated with the + # default IPv6 address. + if not ipv4 and ipv6: + af_inet4 = addr6_fams.get(netifaces.AF_INET) + if af_inet4: + if len(af_inet4) > 1: + LOG.warn( + "device %s has more than one ipv4 address: %s", dev6, af_inet4) + elif 'addr' in af_inet4[0]: + ipv4 = af_inet4[0]['addr'] + + return ipv4, ipv6 + +# patched socket.getfqdn() - see https://bugs.python.org/issue5004 + + +def getfqdn(name=''): + """Get fully qualified domain name from name. + An empty argument is interpreted as meaning the local host. + """ + name = name.strip() + if not name or name == '0.0.0.0': + name = socket.gethostname() + try: + addrs = socket.getaddrinfo( + name, None, 0, socket.SOCK_DGRAM, 0, socket.AI_CANONNAME) + except socket.error: + pass + else: + for addr in addrs: + if addr[3]: + name = addr[3] + break + return name + + +def is_valid_ip_addr(val): + """ + Returns false if the address is loopback, link local or unspecified; + otherwise true is returned. + """ + addr = None + try: + try: + addr = ipaddress.ip_address(val) + except ipaddress.AddressValueError: + addr = ipaddress.ip_address(unicode(val)) + except: + return False + if addr.is_link_local or addr.is_loopback or addr.is_unspecified: + return False + return True + + +def get_host_info(): + ''' + Returns host information such as the host name and network interfaces. + ''' + + host_info = { + 'network': { + 'interfaces': { + 'by-mac': collections.OrderedDict(), + 'by-ipv4': collections.OrderedDict(), + 'by-ipv6': collections.OrderedDict(), + }, + }, + } + + hostname = getfqdn(socket.gethostname()) + if hostname: + host_info['hostname'] = hostname + host_info['local-hostname'] = hostname + host_info['local_hostname'] = hostname + + default_ipv4, default_ipv6 = get_default_ip_addrs() + if default_ipv4: + host_info[LOCAL_IPV4] = default_ipv4 + if default_ipv6: + host_info[LOCAL_IPV6] = default_ipv6 + + by_mac = host_info['network']['interfaces']['by-mac'] + by_ipv4 = host_info['network']['interfaces']['by-ipv4'] + by_ipv6 = host_info['network']['interfaces']['by-ipv6'] + + ifaces = netifaces.interfaces() + for dev_name in ifaces: + addr_fams = netifaces.ifaddresses(dev_name) + af_link = addr_fams.get(netifaces.AF_LINK) + af_inet4 = addr_fams.get(netifaces.AF_INET) + af_inet6 = addr_fams.get(netifaces.AF_INET6) + + mac = None + if af_link and 'addr' in af_link[0]: + mac = af_link[0]['addr'] + + # Do not bother recording localhost + if mac == "00:00:00:00:00:00": + continue + + if mac and (af_inet4 or af_inet6): + key = mac + val = {} + if af_inet4: + af_inet4_vals = [] + for ip_info in af_inet4: + if not is_valid_ip_addr(ip_info['addr']): + continue + af_inet4_vals.append(ip_info) + val["ipv4"] = af_inet4_vals + if af_inet6: + af_inet6_vals = [] + for ip_info in af_inet6: + if not is_valid_ip_addr(ip_info['addr']): + continue + af_inet6_vals.append(ip_info) + val["ipv6"] = af_inet6_vals + by_mac[key] = val + + if af_inet4: + for ip_info in af_inet4: + key = ip_info['addr'] + if not is_valid_ip_addr(key): + continue + val = copy.deepcopy(ip_info) + del val['addr'] + if mac: + val['mac'] = mac + by_ipv4[key] = val + + if af_inet6: + for ip_info in af_inet6: + key = ip_info['addr'] + if not is_valid_ip_addr(key): + continue + val = copy.deepcopy(ip_info) + del val['addr'] + if mac: + val['mac'] = mac + by_ipv6[key] = val + + return host_info + + +def wait_on_network(metadata): + # Determine whether we need to wait on the network coming online. + wait_on_ipv4 = False + wait_on_ipv6 = False + if WAIT_ON_NETWORK in metadata: + wait_on_network = metadata[WAIT_ON_NETWORK] + if WAIT_ON_NETWORK_IPV4 in wait_on_network: + wait_on_ipv4_val = wait_on_network[WAIT_ON_NETWORK_IPV4] + if isinstance(wait_on_ipv4_val, bool): + wait_on_ipv4 = wait_on_ipv4_val + else: + wait_on_ipv4 = bool(strtobool(wait_on_ipv4_val)) + if WAIT_ON_NETWORK_IPV6 in wait_on_network: + wait_on_ipv6_val = wait_on_network[WAIT_ON_NETWORK_IPV6] + if isinstance(wait_on_ipv6_val, bool): + wait_on_ipv6 = wait_on_ipv6_val + else: + wait_on_ipv6 = bool(strtobool(wait_on_ipv6_val)) + + # Get information about the host. + host_info = None + while host_info == None: + host_info = get_host_info() + if wait_on_ipv4: + ipv4_ready = False + if 'network' in host_info: + if 'interfaces' in host_info['network']: + if 'by-ipv4' in host_info['network']['interfaces']: + if len(host_info['network']['interfaces']['by-ipv4']) > 0: + ipv4_ready = True + if not ipv4_ready: + LOG.info("ipv4 not ready") + host_info = None + if wait_on_ipv6: + ipv6_ready = False + if 'network' in host_info: + if 'interfaces' in host_info['network']: + if 'by-ipv6' in host_info['network']['interfaces']: + if len(host_info['network']['interfaces']['by-ipv6']) > 0: + ipv6_ready = True + if not ipv6_ready: + LOG.info("ipv6 not ready") + host_info = None + if host_info == None: + LOG.info("waiting on network") + time.sleep(1) + + return host_info + + +def get_data_access_method(): + if os.environ.get(VMX_GUESTINFO, ""): + return VMX_GUESTINFO + if VMWARE_RPCTOOL: + return VMWARE_RPCTOOL + return None + + +_MERGE_STRATEGY_ENV_VAR = 'CLOUD_INIT_VMWARE_GUEST_INFO_MERGE_STRATEGY' +_MERGE_STRATEGY_DEEPMERGE = 'deepmerge' + + +def merge_dicts(a, b): + merge_strategy = os.getenv(_MERGE_STRATEGY_ENV_VAR) + if merge_strategy == _MERGE_STRATEGY_DEEPMERGE: + try: + LOG.info('merging dictionaries with deepmerge strategy') + return merge_dicts_with_deep_merge(a, b) + except Exception as err: + LOG.error("deep merge failed: %s" % err) + LOG.info('merging dictionaries with stdlib strategy') + return merge_dicts_with_stdlib(a, b) + + +def merge_dicts_with_deep_merge(a, b): + from deepmerge import always_merger + return always_merger.merge(a, b) + + +def merge_dicts_with_stdlib(a, b): + for key, value in a.items(): + if isinstance(value, dict): + node = b.setdefault(key, {}) + merge_dicts_with_stdlib(value, node) + else: + b[key] = value + return b + + +def main(): + ''' + Executed when this file is used as a program. + ''' + try: + logging.setupBasicLogging() + except Exception: + pass + metadata = {'wait-on-network': {'ipv4': True, 'ipv6': "false"}, + 'network': {'config': {'dhcp': True}}} + host_info = wait_on_network(metadata) + metadata = merge_dicts(metadata, host_info) + print(util.json_dumps(metadata)) + + +if __name__ == "__main__": + main() + +# vi: ts=4 expandtab diff --git a/gardenlinux/features/vmware/file.include/usr/local/sbin/update-bootloaders b/gardenlinux/features/vmware/file.include/usr/local/sbin/update-bootloaders new file mode 100755 index 0000000..7b931fa --- /dev/null +++ b/gardenlinux/features/vmware/file.include/usr/local/sbin/update-bootloaders @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -e + +update-kernel-cmdline +update-syslinux +for kernel in /boot/vmlinuz-*; do + kernel-install add "${kernel#*-}" "${kernel}" +done diff --git a/gardenlinux/features/vmware/info.yaml b/gardenlinux/features/vmware/info.yaml new file mode 100644 index 0000000..d3d6b0a --- /dev/null +++ b/gardenlinux/features/vmware/info.yaml @@ -0,0 +1,6 @@ +description: "VMware platform (cloud-init, open-vm-tools)" +type: platform +features: + include: + - cloud + - _ignite diff --git a/gardenlinux/features/vmware/make-ova b/gardenlinux/features/vmware/make-ova new file mode 100755 index 0000000..8209dbd --- /dev/null +++ b/gardenlinux/features/vmware/make-ova @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 + +# See docs/vmware-ova.md for info + +import argparse +import os +import sys +import subprocess +import json +import hashlib +import tempfile +import tarfile + +try: + from mako.template import Template +except ImportError: + sys.exit("Mako Templates module not found") + +MIN_PYTHON = (3, 7) +GUEST_OS = { + "debian10_64Guest": { + "definition": "96", + "description": "Garden Linux 64 (Debian 11)", + "hostname": "gardenlinux" + }, + "sles15_64Guest": { + "definition": "85", + "description": "SUSE Linux Enterprise Server 15 SP2 (CHOST)", + "hostname": "sles15chost" + } +} +PROPERTIES = { + "vmdkFilename": "", + "vmdkFileSize": "", + "vmdkFileCapacity": "", + "virtualSystemId": "", + "osType": "", + "osDefinition": "", + "osDescription": "", + "applianceHostname": "" +} + + +class OVAImageBuild: + def getInfo(self, filename): + result = subprocess.run(["qemu-img", "info", "--output", "json", filename], capture_output=True) + if result.returncode != 0: + sys.exit("Error getting image info for " + filename + ": " + result.stdout) + + doc = json.loads(result.stdout) + fileSize = doc["actual-size"] + virtualSize = doc["format-specific"]["data"]["extents"][0]["virtual-size"] + return (fileSize, virtualSize) + + def makeAttributes(self, PROPERTIES): + class P: + pass + p = P() + for k in PROPERTIES: + setattr(p, k, PROPERTIES[k]) + return p + + def sha256(self, filename): + blocksize = 65536 + sha = hashlib.sha256() + with open(filename, 'rb') as file: + fileBuffer = file.read(blocksize) + while len(fileBuffer) > 0: + sha.update(fileBuffer) + fileBuffer = file.read(blocksize) + + return sha.hexdigest() + + def run(self, template): + vmdkFullFilename = PROPERTIES["vmdkFullFilename"] + vmdkPath = os.path.dirname(vmdkFullFilename) + vmdkFilename = os.path.basename(PROPERTIES["vmdkFilename"]) + ovaFilename = os.path.splitext(vmdkFilename)[0] + ".ova" + ovaFullFilename = os.path.join(vmdkPath, ovaFilename) + ovfFilename = os.path.splitext(vmdkFilename)[0] + ".ovf" + print(ovaFullFilename) + fileSize, virtualSize = self.getInfo(PROPERTIES["vmdkFullFilename"]) + PROPERTIES["vmdkFileSize"] = fileSize + PROPERTIES["vmdkFileCapacity"] = virtualSize + p = self.makeAttributes(PROPERTIES) + tmpl = Template(filename=template) + ovf = tmpl.render(p=p) + vmdkSha256 = self.sha256(vmdkFullFilename) + with tempfile.TemporaryDirectory() as tmpDir: + + ovfFullFilename = os.path.join(tmpDir, ovfFilename) + with open(ovfFullFilename, "w") as file: + file.write(ovf) + ovfSha256 = self.sha256(ovfFullFilename) + + mfFilename = os.path.splitext(vmdkFilename)[0] + ".mf" + mfFullFilename = os.path.join(tmpDir, mfFilename) + + mf = "SHA256(" + vmdkFilename + ")= " + vmdkSha256 + "\n" + "SHA256(" + ovfFilename + ")= " + ovfSha256 + "\n" + + with open(mfFullFilename, "w") as file: + file.write(mf) + + with tarfile.open(ovaFullFilename, "w") as tar: + tar.add(ovfFullFilename, arcname=ovfFilename) + tar.add(vmdkFullFilename, arcname=vmdkFilename) + tar.add(mfFullFilename, arcname=mfFilename) + + @classmethod + def _argparse_register(cls, parser): + parser.add_argument( + '--vmdk', + type=str, + dest='vmdk', + help='vmdk filename', + required=True + ) + parser.add_argument( + "--guest-id", + type=str, + dest='guest_id', + help='Guest operating system identifier', + choices=list(GUEST_OS.keys()), + required=True + ) + parser.add_argument( + "--template", + type=str, + dest='template', + help='OVF template file path (see ../templates for examples)', + required=True + ) + + @classmethod + def _main(cls): + parser = argparse.ArgumentParser() + cls._argparse_register(parser) + args = parser.parse_args() + + if sys.version_info < MIN_PYTHON: + sys.exit("Python %s.%s or newer is required." % MIN_PYTHON) + + PROPERTIES["vmdkFullFilename"] = args.vmdk + PROPERTIES["vmdkFilename"] = os.path.basename(args.vmdk) + PROPERTIES["virtualSystemId"] = os.path.splitext(os.path.basename(args.vmdk))[0] + PROPERTIES["osType"] = args.guest_id + PROPERTIES["osDefinition"] = GUEST_OS[args.guest_id]["definition"] + PROPERTIES["osDescription"] = GUEST_OS[args.guest_id]["description"] + PROPERTIES["applianceHostname"] = GUEST_OS[args.guest_id]["hostname"] + oVAImageBuild = cls() + oVAImageBuild.run(template=args.template) + + +if __name__ == '__main__': + OVAImageBuild._main() diff --git a/gardenlinux/features/vmware/pkg.include b/gardenlinux/features/vmware/pkg.include new file mode 100644 index 0000000..6a6fdd7 --- /dev/null +++ b/gardenlinux/features/vmware/pkg.include @@ -0,0 +1,6 @@ +python3-cffi-backend +python3-boto +cloud-init +dmidecode +open-vm-tools +python3-netifaces diff --git a/gardenlinux/features/vmware/test/capabilities.d/capabilities.list b/gardenlinux/features/vmware/test/capabilities.d/capabilities.list new file mode 100644 index 0000000..249cef4 --- /dev/null +++ b/gardenlinux/features/vmware/test/capabilities.d/capabilities.list @@ -0,0 +1 @@ +/usr/bin/arping cap_net_raw=ep diff --git a/gardenlinux/features/vmware/test/test_check_files.py b/gardenlinux/features/vmware/test/test_check_files.py new file mode 100644 index 0000000..7a44389 --- /dev/null +++ b/gardenlinux/features/vmware/test/test_check_files.py @@ -0,0 +1,20 @@ +from helper.utils import check_file +import pytest + + +@pytest.mark.parametrize( + "file_name", + [ + "/usr/bin/dscheck_VMwareGuestInfo", + "/usr/lib/python3/dist-packages/cloudinit/sources/DataSourceVMwareGuestInfo.py", + "/etc/default/grub.cfg", + "/etc/cloud/cloud.cfg.d/01_debian-cloud.cfg", + "/etc/cloud/cloud.cfg.d/99_disable-network-config.cfg", + "/etc/cloud/cloud.cfg.d/99_enabled-datasources.cfg" + ] +) + + +def test_check_file(client, file_name): + exists = check_file(client, file_name) + assert exists, f"File {file_name} could not be found." diff --git a/gardenlinux/features/vmware/test/test_content_in_file.py b/gardenlinux/features/vmware/test/test_content_in_file.py new file mode 100644 index 0000000..bd87655 --- /dev/null +++ b/gardenlinux/features/vmware/test/test_content_in_file.py @@ -0,0 +1,17 @@ +import pytest +from helper.tests.file_content import file_content +from helper.utils import execute_remote_command + + +@pytest.mark.parametrize( + "file,args", + [ + ("/etc/cloud/cloud.cfg", "ntp"), + ("/etc/cloud/cloud.cfg", "resizefs"), + ("/etc/cloud/cloud.cfg", "growpart") + ] +) + + +def test_file_content(client, file, args): + file_content(client, file, args, only_line_match=True, invert=True) diff --git a/gardenlinux/features/vmware/test/test_packages_musthave.py b/gardenlinux/features/vmware/test/test_packages_musthave.py new file mode 100644 index 0000000..4b2783d --- /dev/null +++ b/gardenlinux/features/vmware/test/test_packages_musthave.py @@ -0,0 +1 @@ +from helper.tests.packages_musthave import packages_musthave as test_packages_musthave diff --git a/gardenlinux/features/vmware/vmware.ovf.template b/gardenlinux/features/vmware/vmware.ovf.template new file mode 100644 index 0000000..9a5058f --- /dev/null +++ b/gardenlinux/features/vmware/vmware.ovf.template @@ -0,0 +1,190 @@ + + + + + + + Virtual disk information + + + + The list of logical networks + + The VM Network network + + + + A virtual machine + ${p.virtualSystemId} + + The kind of installed guest operating system + "${p.osDescription}" + + + + Cloud-Init customization + "${p.osDescription}" + + + Specifies the instance id. This is required and used to determine if the machine should take "first boot" actions + + + Specifies the hostname for the appliance + + + + This field is optional, but indicates that the instance should 'seed' user-data and meta-data from the given url. If set to 'http://tinyurl.com/sm-' is given, meta-data will be pulled from http://tinyurl.com/sm-meta-data and user-data from http://tinyurl.com/sm-user-data. Leave this empty if you do not want to seed from a url. + + + + This field is optional, but indicates that the instance should populate the default user's 'authorized_keys' with this value + + + + In order to fit into a xml attribute, this value is base64 encoded . It will be decoded, and then processed normally as user-data. + + + + + If set, the default user's password will be set to this value to allow password based login. The password will be good for only a single login. If set to the string 'RANDOM' then a random password will be generated, and written to the console. + + + + Inline Ignition config data + + + + Encoding for Ignition config data + + + + URL to Ignition config + + + + + Virtual hardware requirements + + Virtual Hardware Family + 0 + ${p.virtualSystemId} + vmx-14 + + + hertz * 10^6 + Number of Virtual CPUs + 2 virtual CPU(s) + 1 + 3 + 2 + + + byte * 2^20 + Memory Size + 1024MB of memory + 2 + 4 + 1024 + + + 0 + SCSI Controller + SCSI Controller 0 + 3 + VirtualSCSI + 6 + + + 1 + IDE Controller + VirtualIDEController 1 + 4 + 5 + + + 0 + IDE Controller + VirtualIDEController 0 + 5 + 5 + + + false + VirtualVideoCard + 6 + 24 + + + + + + + + false + VirtualVMCIDevice + 7 + vmware.vmci + 1 + + + + 0 + false + CD-ROM 1 + 8 + 4 + vmware.cdrom.remotepassthrough + 15 + + + + 0 + Hard Disk 1 + ovf:/disk/vmdisk1 + 9 + 3 + 17 + + + + 0 + false + Floppy Drive + Floppy 1 + 10 + vmware.floppy.remotedevice + 14 + + + 7 + true + VM Network + VmxNet3 ethernet adapter on "VM Network" + Ethernet 1 + 11 + VmxNet3 + 10 + + + + + + + + + + + + + + + + + + + + + + diff --git a/gardenlinux/gardenlinux.asc b/gardenlinux/gardenlinux.asc new file mode 100644 index 0000000..8dd30bc --- /dev/null +++ b/gardenlinux/gardenlinux.asc @@ -0,0 +1,57 @@ +pub rsa4096 2022-11-18 [SCEA] [expires: 2025-11-17] + F0A6DD39C31D56E5350F06A68BAD76A391E2BB51 +uid [ultimate] Garden Linux Automatic Signing Key (2022) + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBGN3UG4BEACUPRC/gZekjtoaszk7+TdJUi4E6U9asuUu2p9TvXpItQHcjBc4 +XZhKvrtJotft/KJQf7/hkS587QfaRzMqzIJe7WC3ttm/SWNQee9VDUOzNCBaIPrq +9iv0wZn+UtfbnKqUj8oknuo4BIKBdMJML4WiAsueP2wIrl0K37axoXfBFRXXmIhd +48xZKGw3MeoZKhv5buATwv7tnJAlWXmSAn1lJolVhcdsl6npN1RPWAUPbhUoeaYQ +zA2crak8PSe9B2foCoJ/7a4wtN4f9aI2+XYbMa97/9UzbcH8c1Cx4hSQpc7p0Csf +5Ig0h/dLAFQ11xPbCgchh6EqZY46327H5vEXxhNVOAoQqIp+MEW12Gok6fDJX4Ts +zA8k4X8w5SEdhCKd4sUBYU8CzlejXqykHRkqlYi/kR4qAUsbaNy3naIqGb+hdeSs +1Ch7X5sXArK1ua6a2xTlzpV7UesQddR31XBxg1y8zuqkD4YhxbcLx9i27kC9pyYm +5aCE215k9NXnmeBI80VsEWbfxRomwIZ4XK0QiVqOS8f/yQtU2dZbAOEEb5hPhLvK +fhfFwBuLCuu4BQLBJCYX4tEvwtecXG31/3w+EbEBoFZ9HKRxIQgNM8u5pS4uKk9S +fBJRbliCtyynOkTRRBy8nUgoPhHAIruBfVo5CiRF8j/NRnCjp5glPNTuoQARAQAB +tEJHYXJkZW4gTGludXggQXV0b21hdGljIFNpZ25pbmcgS2V5ICgyMDIyKSA8Y29u +dGFjdEBnYXJkZW5saW51eC5pbz6JAlQEEwEKAD4WIQTwpt05wx1W5TUPBqaLrXaj +keK7UQUCY3dQbgIbLwUJBaOagAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRCL +rXajkeK7UTcQD/9/Jxg34xhrzUpOStFtxo9JrsAUc38rAUEbFL30LSX9DG5fCHff +6MCOGSI9zenc95O3hXOFb9mlQJrEFc7oYlqePbxqXCstOvyavKw53KddEKrpp+zZ +EIfbcXIsMh6c3G0fxPaAlFnjXprEzEDtPMfr3aa7fLZJENQbpOzyt//8AtFwYv2u +3sEgYwPo07PQf60g7pIfkC6rkg3RexwMkhquh2gdBQo1I/jloWLsDhn+Hi+/zsbs +ny/IC8YZl3iMu+5pY5eRL+Uu3lslA8IB6Pjp1x2kBIP6PeVANAg/5vXqq1qn8aoG +z6+8JcIMDUETlnx19b6SqZd1shFlxlodU8qUtoAisk+WRoHCetkFSPvLr4EVloTb +SUB7gHPBWqjrCP39lYPsN52fgBiG2Nko+bnU9BSPt5VIvYQfvX3gx0FXQbW1zeZ6 +gq4w3lWajqilmS5F8x2pBJAFdLZw7f5t4w0NoGA2edJEc51+JW0/BgKmImQOjiOg +dD34FwP49P6orG14l/q3meKfJB0O660N09+liEHLo9zJtOUp8v+EwAjB81/LRLpV +HNRHTVOxDJRDDC2wjbhN38BtfjnLKFz2S/hZNIDeqz53fSgpGftdiBNhLS8wI2E1 +ciUIHaRWRlvgpEo1KsHk3Knn+t+f+5Fxyi9Yj9QMU5Hi7zKex9hBDReBrA== +=93j9 +-----END PGP PUBLIC KEY BLOCK----- + +pub rsa2048 2021-11-30 [SC] [expires: 2023-11-30] + 9B9DADFA1D8F85E59A41896A0EFC9C931FBE3668 +uid [ultimate] Garden Linux Automatic Signing Key (2021) + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBGGmAY0BCAChaxJKvQByqc1fDYRopv+xdRMqvAW2vktGEInBjUHADszjgLua ++oYaLveWznZIf6XtYxjejSa4vKwQyqxT1+cAxg/+0tMXg1YsFAOYAH/UDWx1kNn0 +/ZYrzTxHD9Pc2AZr+kC/eUdOM2TuUCsOa9SiZ5sfgV1/QO2b00gW1AVKP/bIEScT +dO3RJEiy+ehl6z8okOGMxs+jLulCRRYb3jmsb++x2/s6S/GENu4A7tr1nrscHVLg +2x8ZqgrMdsm4hPQqm7Edp7pNYGQWjOj9RzyczYezBN81YPfIk/fcHY9+I7ENTiW8 +PDswYjmnKGCUBwnvtPhMYUCMAnFPErNzesQTABEBAAG0QkdhcmRlbiBMaW51eCBB +dXRvbWF0aWMgU2lnbmluZyBLZXkgKDIwMjEpIDxjb250YWN0QGdhcmRlbmxpbnV4 +LmlvPokBVAQTAQoAPhYhBJudrfodj4XlmkGJag78nJMfvjZoBQJhpgGNAhsDBQkD +wmcABQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEA78nJMfvjZoj40H/3NZBRsT +ysLZtwygmbIxncVZQL5hxynNBSvOIdPBUuZSi2oe/R9mphFMR3HMvI+ouWyOl6je +41/DazGjmcLnIIIX1lQHh5hgWih+da7Me3xWp/8XseNkNzeTNeGzVEM9FkI8+xWQ +uhpMnnA3qYuOr+XPZenaBHN985MUYiaYMpEA62LjT/c60Q7vqh9MUz6qD2BXqyTw +EJQ8XAPKsLS3suBTMUSjEuoTnG1IE2IsJqZfgW8jtvHG6P63A2X7ag0hh7NnFv7g +7d9OEqRUth4TdSJ5LqFifH2WYjzc+ZRK+l7EzHqwnmS8Tf8saj+MCXlB3MxYZrge +fNc0zrh3qLuTN+4= +=q11k +-----END PGP PUBLIC KEY BLOCK----- diff --git a/gardenlinux/get_commit b/gardenlinux/get_commit new file mode 100755 index 0000000..09ec10c --- /dev/null +++ b/gardenlinux/get_commit @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +dir="$(dirname "${BASH_SOURCE[0]}")" +cd "$dir" +[ -z "$(git status --porcelain 2> /dev/null)" ] && git rev-parse HEAD 2> /dev/null || echo local diff --git a/gardenlinux/get_repo b/gardenlinux/get_repo new file mode 100755 index 0000000..281adb9 --- /dev/null +++ b/gardenlinux/get_repo @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +echo "http://repo.gardenlinux.io/gardenlinux" diff --git a/gardenlinux/get_timestamp b/gardenlinux/get_timestamp new file mode 100755 index 0000000..9f5f28b --- /dev/null +++ b/gardenlinux/get_timestamp @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +dir="$(dirname "${BASH_SOURCE[0]}")" +"$dir/bin/garden-version" --epoch diff --git a/gardenlinux/get_version b/gardenlinux/get_version new file mode 100755 index 0000000..05b38fb --- /dev/null +++ b/gardenlinux/get_version @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -eufo pipefail + +dir="$(dirname "${BASH_SOURCE[0]}")" +"$dir/bin/garden-version" diff --git a/gardenlinux/keyring.gpg b/gardenlinux/keyring.gpg new file mode 100644 index 0000000000000000000000000000000000000000..5d2533db52159bdff65e39a8eb198110351902aa GIT binary patch literal 2210 zcmbu9c{J2(AHaXV-^^Gh!)P#Bh9NYev6Q51nJKcCLWS&0vJFC#U8LUZWD8|qLRm7( zI<6>&u1nW8*V>eAXi>IswY~5A$35@4=e*B3&v~}*_j&&Kex45i0C*4tAcJFO7lEDT zsK?#$wSS#|ZwN93ew$eX0Q@BY0wV8<{#5-HcaEvd!&Y>=;!1wvRKuB_2>kc>od_-f zfI@aw$G+`=0Q|?vzlWbMWI!mmga2s?hz3&B(JkR=1#u%nHLAa?spuIK;j=~-)lA~Z zJ(H$ux{pCd4EBgsx_)zmUfv+Y#W_J4;Wf$xrfrLrl^|svkOT!gN#8mf)#;vAI;&!8WQO3O@Dl=x&~#K* zMX8$lz9x3(8DvB#t5TtMOzczM;k7HmiVp*~Umf(IeepS6HN}@u*gN~1tk~zvP=FZKQL{S;gK==}anw=*j*pln_x_p2TXmb|}RX=`GnK{bG z?7b6sW*+S~;Qz|Y`?_Ug?_2@T4e@8xU7bU9i=?JvX=&#D$Wd#@xCIF)|L?eTHHdL6s{$s@10@(wMDF(BN&AXb2Cs zP(?~=f6(kX;gw&UE~PfGm<#ZN0QhK!9A_`*@4NxE$8_v2J33MDhAtGpt9Y`XucwEd zuZsiT#KoE7LUG0$I0fJdN(xF!`|)ZHo)lj@2Vau&w-ooU8AKOPjUP8c-_g4TnZhw3 z63`G3hyNYL2h>3?&&z(Za*T#|fX(#R{1#e)UrZ89AU;S!^#zJ> zB?(nVU&0}f%6nItx%La(f?J~UExjEJ_vD}N>JUu38q=`o;*gdo&m@jrkb<>=GpnJG@uHTw|HF1`PpFA{7~4~0}cOR!hXRGtD6|R1M&)c z?UXM|ao`HhowzVjpsy@Ld(9|)yFPwzBhkA{-YkBA%Or7XNhzmuh>XI9mp@_yU_00X zOA^}14Yyt)m#A6|l9`(sU+v*l0D%3MK%s8}y_;RJj*E|CW|D8Ya(~H67Hd9yX$MgJ z4?lhrDEQ9=3Zme4MPMw*0pz;v)^7qXm-g78A}vAE?-kw9LYBjxu~_N)#&UF5xyj&Lpo3@Y_kQg4b8>vJbRcv9TBrN@0#eXai@IM0iyDB1}uhoErVF*rGn=|mg1oE@+@v-dNKIRBv zOjp}5mFs>+wPN-&b5gTi=l&jqY_E7f&G?P+ZCBAvv5Q5#hC1H##`uya?r~`)W79Uj zx+&&ow7tbgVE-^>k50F>AcXi;(gTCe6>np!rX5vOl9eZGOpmJz98=riPk1V1nB%2V zdOPkR^QF`52;3+5V=|w;bh=U^FmFF?v~skMd#Oy!&_tt*$M*aK$#>`!HA`66qgrb9 zgo&b7{_Z(UmWM9JUQW8mCfsvx-TRK2Eh@}u!xbeSUE?gUHNhG`wY$KyP88!O5kSnf z4+>7TS7WTD=&xJ1)2}_sacn%fE$6Bk!x=zk(9JH^iv|rI>aF)LX-HOC{P}_K5}~Ta T6d@sM`#R?&E%r6=6uJ2qN$8xM literal 0 HcmV?d00001 diff --git a/gardenlinux/requirements.txt b/gardenlinux/requirements.txt new file mode 100644 index 0000000..0c14e75 --- /dev/null +++ b/gardenlinux/requirements.txt @@ -0,0 +1,5 @@ +# Basic Python requirements for Garden Linux +# that can be installed by Python pip. +# Note: You should always prefer your local package manager. +PyYAML +networkx