diff --git a/.gitignore b/.gitignore index 6f0876d..d05c7be 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ src/cache/ +.vagrant diff --git a/README.md b/README.md index c6bfdd1..82aaefa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ``` - ██████ ██ ▄█▀▒███████▒ ▒█████ ██▓███ ▄▄▄ ▄████▄ ██ ▄█▀ + ██████ ██ ▄█▀▒███████▒ ▒█████ ██▓███ ██▄ ▄████▄ ██ ▄█▀ ▒██ ▒ ██▄█▒ ▒ ▒ ▒ ▄▀░ ▒██▒ ██▒▓██░ ██ ▒████▄ ▒██▀ ▀█ ██▄█▒ ░ ▓██▄ ▓███▄░ ░ ▒ ▄▀▒ ▓▒█ ▒██░ ██▒▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒ ██▒▓██ █▄ ▄▀▒ ░▒ ▒██ ██░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ @@ -12,7 +12,9 @@ ----- *Samuel 'sk4nz' AUBERTIN* -**skz-opack** enables simple execution of [OpenBSD](https://www.openbsd.org) virtual machines from a Linux host or Google Cloud Compute, leveraging Packer and Vagrant, from a Makefile target named `opack`. +**skz-opack** is an automated [OpenBSD](https://www.openbsd.org) bootstrapper. + +Leveraging Packer, Vagrant boxes and a bit of black magic (also known as scripting) it enables frictionless execution of this wonderful operating system within a local virtual machine from a Linux host. It can also emit a compliant Google Compute Engine image, ran in the cloud using Terraform. ## Instructions @@ -28,21 +30,45 @@ To get started with **skz-opack**, follow these steps: - [Vagrant](https://www.vagrantup.com/) - [Vagrant-Libvirt module](https://github.com/vagrant-libvirt/vagrant-libvirt) -3. Use the project by including `path/to/skz-opack/src/opack.mk` and invoking the `opack` target in your Makefile to create and manage OpenBSD virtual machines with Packer and Vagrant. - -## Architecture - - +3. Use the project by including `path/to/skz-opack/src/opack.mk` and invoking the `opack` target in your Makefile to create a local OpenBSD virtual machine with Packer and Vagrant. Use the 'opack-cloud' target to run the same virtual machine in GCP with Terraform. ## Available make targets -Available make target are: -- `opack` -- `opack-cloud` -- `opack-clean` -- `opack-cleancache` -- `opack-cleanall` +- `opack`: downloads, installs an run OpenBSD in a local VM. +- `opack-cloud`: same as 'opack' but in the GCP cloud. +- `opack-clean`: Destroys the local/cloud VM. +- `opack-cleancache`: Removes the cache containing installation isos and Vagrant boxes. +- `opack-cleanall`: 'opack-clean' and 'opack-cleancache' + +## Overridable default options + +Every default option ending with an `?` in "src/options.mk" can be overrided in the invoking Makefile. Here is an short list of theses: + +- `OPACK_SYS_HOSTNAME`: VM hostname +- `OPACK_SYS_USER`: username to create +- `OPACK_SYS_PASSWORD`: user password +- `OPACK_SYS_DISK_SIZE`: VM disk size, in megabytes +- `OPACK_SYS_MEMORY`: VM RAM, in megabytes +- `OPACK_SYS_CPU`: VM cpu count +- `OPACK_SYS_SERVER`: OpenBSD package mirror +- `OPACK_SYS_ARCHITECTURE`: OpenBSD platform to use (only *amd64* has been tested) +- `OPACK_SYS_RELEASE`: OpenBSD release name aka *74* +- `OPACK_SYS_VERSION`: OpenBSD version aka *7.4* or *snapshots* +- `OPACK_SYS_XENOCARA`: Install Xenocara, the OpenBSD X11 fork +- `OPACK_SYS_SETS`: OpenBSD sets to install +- `OPACK_SYS_TIMEZONE`: Timezone decalred at installation +- `OPACK_SYS_SSH_KEY`: The SSH public key added to the created user +- `OPACK_SYS_ISO_URL`: The installation iso URL +- `OPACK_SYS_ISO_SHA256SUM`: The installation iso SHA256 sum +- `OPACK_SYS_SETS_LOCATION`: The location of installed OpenBSD sets eg *http* or *cd0* +- `OPACK_NO_SIGCHK`: Ignore unsigned sets +- `OPACK_SYS_HEADLESS`: Run Packer installation in headless mode +- `OPACK_AUTODISKLABEL_FILE`: OpenBSD partitioning scheme for installation +- `OPACK_PROVISION_FILE`: Script executed after installation +- `OPACK_RUNTIME_PROVISION_FILE`: Script executed after VM creation + ## Examples + ### Quick example Here is the shortest Makefile that can be used to leverage skz-opack: @@ -52,14 +78,13 @@ OPACK_TARGET= demo # the VM name include path/to/src/opack.mk # mandatory ``` -After invokation with `make opack`, it will download, install, and run the latest OpenBSD release in a local VM. +After invokation with `make opack`, it will download, install, and run the latest OpenBSD release in a local VM with the default options. Once executed, the VM is running and it is possible to log into it using the `vagrant ssh` command. -Then, `make clean` will remove the VM, but not the vagrant box (in order to clean everythin, use `make cleancache`). +Then, `make clean` will remove the VM, but not the vagrant box (in order to clean everything, use `make cleancache`). -### Complex Example - -In this example, we will demonstrate how to use **skz-opack** to create an OpenBSD -current virtual machine with 4 CPUs, 1GB of RAM, and name it "run-current." +### Overrides Example +In this example, we will demonstrate how to use overrides in **skz-opack** to create an OpenBSD *-current* virtual machine named "run-current" with 4 CPUs and 1GB of RAM. ```make OPACK_TARGET= run-current OPACK_SYS_VERSION= snapshots @@ -81,7 +106,7 @@ In this Makefile: Making "opack" a dependency target of "all" will ensure **skz-opack** operates before any command in the "all" target. Try it with `make all` ! -Once the "opack" target dependency is finished, the Vm is running and the system kernel version is outputted. This example showcases how to customize and run **skz-opack** for your specific needs. Every option ending with an `?` in "src/options.mk" can be overrided in the invoking Makefile. +Once the "opack" target dependency is finished, the VM is running and the system kernel version is outputted. This example showcases how to customize and run **skz-opack** for your specific needs. ### GCP example @@ -89,13 +114,22 @@ TODO ## Troubleshooting -Overrides should happen before the include of "opack.mk". +- To enable debug mode, you can declare 'OPACK_DEBUG=yes' either in your Makefile or at runtime with: 'OPACK_DEBUG=yes make opack'. +- Overrides should happen before the include of "opack.mk". -## Structure +## Architecture + +### Security + +`OPACK_SYS_USER` can become *root* using the `doas` command. + +The `autodisklabel` used for this project is flat, so partitions don't benefit from *nodev*, *nosuid* and *wxallowed* protections. + +### Code structure In the "src" directory, are located the following files: -- **autodisklabel**: This file provides the partitioning layout for the installer and can be customized by using the `OPACK_AUTODISKLABEL_FILE` option. +- **autodisklabel**: This file provides the partitioning layout for the installer and can be customized by using the `OPACK_AUTODISKLABEL_FILE` option. - **defines.mk**: Contains internal skz-opack definitions for Packer and Vagrant. - **opack.mk**: To use skz-opack in your projects, simply include this file in your Makefiles. - **opack-provision.sh**: This file is executed for post-installation provisioning with Packer and can be customized using the `OPACK_PROVISION_FILE` option. diff --git a/src/opack-provision.sh b/src/opack-provision.sh index 0c4b85b..949357a 100644 --- a/src/opack-provision.sh +++ b/src/opack-provision.sh @@ -6,11 +6,11 @@ set +x echo OPACK: Starting provisioning. sysctl -n kern.version printf 'permit nopass :wheel\n' > /etc/doas.conf - printf 'PasswordAuthentication yes\nPermitRootLogin yes\n' >> /etc/ssh/sshd_config pkg_add -u 2> /dev/null || pkg_add -u -D snap 2> /dev/null rm -f /etc/ssh/ssh_host* find /var/log -type f | while read f; do echo -ne '' > $f; done find /tmp -type f | while read f; do echo -ne '' > $f; done + for part in $(df | tail -n+2 | awk '{print $6}'); do dd if=/dev/zero of=$part/EMPTY bs=1M || true; rm -f $part/EMPTY || true; done sync sync echo OPACK: provisionning done. diff --git a/src/opack.mk b/src/opack.mk index e3ac6f1..04b9e93 100644 --- a/src/opack.mk +++ b/src/opack.mk @@ -1,9 +1,17 @@ .PHONY: clean cleancache cleanall opack .DEFAULT_GOAL := opack + +# Enable debugging mode if OPACK_DEBUG is set to 'yes' +OPACK_DEBUG=yes + +ifndef OPACK_DEBUG +.SILENT: +endif + OPACK_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) define OPACK_BANNER - ██████ ██ ▄█▀▒███████▒ ▒█████ ██▓███ ▄▄▄ ▄████▄ ██ ▄█▀ + ██████ ██ ▄█▀▒███████▒ ▒█████ ██▓███ ██▄ ▄████▄ ██ ▄█▀ ▒██ ▒ ██▄█▒ ▒ ▒ ▒ ▄▀░ ▒██▒ ██▒▓██░ ██ ▒████▄ ▒██▀ ▀█ ██▄█▒ ░ ▓██▄ ▓███▄░ ░ ▒ ▄▀▒ ▓▒█ ▒██░ ██▒▓██░ ██▓▒▒██ ▀█▄ ▒▓█ ▄ ▓███▄░ ▒ ██▒▓██ █▄ ▄▀▒ ░▒ ▒██ ██░▒██▄█▓▒ ▒░██▄▄▄▄██ ▒▓▓▄ ▄██▒▓██ █▄ @@ -29,65 +37,88 @@ include $(OPACK_DIR)/vagrantfile.mk $(OPACK_PACKER_HTTP_DIR) $(OPACK_PACKER_DIR) ../$(OPACK_CACHE_DIR): - @mkdir -p $@ + mkdir -p $@ $(OPACK_PACKER_DIR)/vagrant.key: | $(OPACK_PACKER_DIR) - @curl -s -o $@ https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant + curl -s -o $@ https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant $(OPACK_PACKER_DIR)/opack.json: | $(OPACK_PACKER_DIR) - @printf '$(subst $(newline),\n,$(OPACK_PACKER_CONTENT))' > $@ + printf '$(subst $(newline),\n,$(OPACK_PACKER_CONTENT))' > $@ $(OPACK_PACKER_DIR)/bucket.json: - @printf '{ "name": "$(GCE_BUCKET)", "location": "$(GCE_BUCKET_LOCATION)", "storageClass": "STANDARD", "iamConfiguration": {"uniformBucketLevelAccess": { "enabled": true }, } }' > $@ + printf '{ "name": "$(GCE_BUCKET)", "location": "$(GCE_BUCKET_LOCATION)", "storageClass": "STANDARD", "iamConfiguration": {"uniformBucketLevelAccess": { "enabled": true }, } }' > $@ $(OPACK_PACKER_DIR)/opack-cloud.json: | $(OPACK_PACKER_DIR) $(OPACK_PACKER_DIR)/bucket.json - @printf '$(subst $(newline),\n,$(OPACK_PACKER_CLOUD_CONTENT))' > $@ - @curl -X POST -s -o /dev/null \ + printf '$(subst $(newline),\n,$(OPACK_PACKER_CLOUD_CONTENT))' > $@ + curl -X POST -s -o /dev/null \ --data-binary @$(OPACK_PACKER_DIR)/bucket.json \ -H "Authorization: Bearer $(STORAGE_TOKEN)" \ -H "Content-Type: application/json" \ "https://storage.googleapis.com/storage/v1/b?project=$(GCE_PROJECT)" - @curl -X DELETE -s -o /dev/null \ + curl -X DELETE -s -o /dev/null \ -H "Authorization: Bearer $(IMAGE_TOKEN)" \ "https://compute.googleapis.com/compute/v1/projects/$(GCE_PROJECT)/global/images/$(OPACK_TARGET)" $(OPACK_PACKER_HTTP_DIR)/install.conf: | $(OPACK_PACKER_HTTP_DIR) - @printf '$(subst $(newline),\n,$(OPACK_INSTALL_CONTENT))' > $@ + printf '$(subst $(newline),\n,$(OPACK_INSTALL_CONTENT))' > $@ $(OPACK_PACKER_HTTP_DIR)/autodisklabel: | $(OPACK_PACKER_HTTP_DIR) - @cp $(OPACK_AUTODISKLABEL_FILE) $@ + cp $(OPACK_AUTODISKLABEL_FILE) $@ $(OPACK_PROVISION_FILE): - @echo you need to write $@ && exit 1 + echo you need to write $@ && exit 1 ../id_ed25519: - @ssh-keygen -q -t ed25519 -f $@ -N '""' + ssh-keygen -q -t ed25519 -f $@ -N '""' $(OPACK_BOX_FILE): | $(OPACK_PACKER_DIR)/opack.json $(OPACK_PACKER_DIR)/vagrant.key $(OPACK_PROVISION_FILE) $(OPACK_PACKER_HTTP_DIR)/install.conf $(OPACK_PACKER_HTTP_DIR)/autodisklabel - @$(call spinner,cd $(OPACK_PACKER_DIR) && CHECKPOINT_DISABLE=1 PACKER_CACHE_DIR=../../$(OPACK_CACHE_DIR) packer build -timestamp-ui opack.json > opack.log || echo Error Autoinstalling: check $(OPACK_PACKER_DIR)/opack.log,█ Autoinstalling $(OPACK_BOX_TAG) with Packer) +ifndef OPACK_DEBUG + $(call spinner,cd $(OPACK_PACKER_DIR) && (CHECKPOINT_DISABLE=1 PACKER_CACHE_DIR=../../$(OPACK_CACHE_DIR) packer build -timestamp-ui opack.json > opack.log && rm -rf $(OPACK_PACKER_DIR)) || echo Error Autoinstalling: check $(OPACK_PACKER_DIR)/opack.log,█ Autoinstalling $(OPACK_BOX_TAG) with Packer) +else + cd $(OPACK_PACKER_DIR) && CHECKPOINT_DISABLE=1 PACKER_CACHE_DIR=../../$(OPACK_CACHE_DIR) packer build -timestamp-ui opack.json && rm -rf $(OPACK_PACKER_DIR) +endif $(OPACK_META_FILE): $(OPACK_BOX_FILE) - @printf '$(subst $(OPACK_BOX_FILE_SHA256),$(shell sha256sum $(OPACK_BOX_FILE) | awk '{print $$1}'),$(subst $(newline),\n,$(OPACK_METADATA_CONTENT)))' > $@ - @$(call spinner,vagrant box add -f --name $(OPACK_BOX_TAG) $(OPACK_META_FILE) > /dev/null,█ Adding $(OPACK_BOX_FILE) to Vagrant) + printf '$(subst $(OPACK_BOX_FILE_SHA256),$(shell sha256sum $(OPACK_BOX_FILE) | awk '{print $$1}'),$(subst $(newline),\n,$(OPACK_METADATA_CONTENT)))' > $@ +ifndef OPACK_DEBUG + $(call spinner,vagrant box add -f --name $(OPACK_BOX_TAG) $(OPACK_META_FILE) > /dev/null,█ Adding $(OPACK_BOX_FILE) to Vagrant) +else + vagrant box add -f --name $(OPACK_BOX_TAG) $(OPACK_META_FILE) +endif $(OPACK_VAGRANT_FILE): - @printf '$(subst $(newline),\n,$(OPACK_VAGRANT_CONTENT))' > $@ - + printf '$(subst $(newline),\n,$(OPACK_VAGRANT_CONTENT))' > $@ opack: $(OPACK_META_FILE) $(OPACK_VAGRANT_FILE) - @$(call spinner,vagrant up > /dev/null,█ Starting $(OPACK_HOSTNAME)-$(OPACK_TARGET) with Vagrant) +ifndef OPACK_DEBUG + $(call spinner,vagrant up > /dev/null,█ Starting $(OPACK_HOSTNAME)-$(OPACK_TARGET) with Vagrant) +else + vagrant up +endif + echo "█ skz-opack complete, run 'vagrant ssh' to connect to the machine" opack-cloud: | $(OPACK_PACKER_DIR)/opack-cloud.json ../id_ed25519 $(OPACK_PROVISION_FILE) $(OPACK_PACKER_HTTP_DIR)/install.conf $(OPACK_PACKER_HTTP_DIR)/autodisklabel - @cd $(OPACK_PACKER_DIR) &&\ + cd $(OPACK_PACKER_DIR) &&\ CHECKPOINT_DISABLE=1 PACKER_CACHE_DIR=../../$(OPACK_CACHE_DIR)\ packer build\ -timestamp-ui opack-cloud.json | tee -a ../$@ &&\ rm -rf $(OPACK_PACKER_DIR) opack-clean: - @-$(call spinner,vagrant destroy -f >/dev/null; cd terraform_??????? 2>/dev/null && terraform destroy -auto-approve && cd .. && rm -rf terraform_???????; rm -rf $(OPACK_VAGRANT_FILE) opack_build_$(OPACK_SHORT_REV) *.log ssh-config .vagrant *.json,█ Cleaning up) +ifndef OPACK_DEBUG + -$(call spinner,vagrant destroy -f >/dev/null; cd terraform_??????? 2>/dev/null && terraform destroy -auto-approve && cd .. && rm -rf terraform_???????; rm -rf $(OPACK_VAGRANT_FILE) opack_build_$(OPACK_SHORT_REV) *.log ssh-config .vagrant *.json,█ Cleaning up) +else + -vagrant destroy -f + -cd terraform_??????? 2>/dev/null && terraform destroy -auto-approve && cd .. && rm -rf terraform_??????? + -rm -rf $(OPACK_VAGRANT_FILE) opack_installer_$(OPACK_SHORT_REV) *.log ssh-config .vagrant *.json +endif opack-cleancache: - @-$(call spinner,vagrant box remove -f --all $(OPACK_BOX_TAG) 2>/dev/null; rm -rf $(OPACK_CACHE_DIR),█ Cleaning up cache) +ifndef OPACK_DEBUG + -$(call spinner,vagrant box remove -f --all $(OPACK_BOX_TAG) 2>/dev/null; rm -rf $(OPACK_CACHE_DIR),█ Cleaning up cache) +else + -vagrant box remove -f --all $(OPACK_BOX_TAG) 2>/dev/null + -rm -rf $(OPACK_CACHE_DIR) +endif -opack-cleanall: clean cleancache +opack-cleanall: opack-clean opack-cleancache diff --git a/src/options.mk b/src/options.mk index b007366..7e2c771 100644 --- a/src/options.mk +++ b/src/options.mk @@ -1,20 +1,11 @@ -# Enable debugging mode if OPACK_DEBUG is set to 'yes' -OPACK_DEBUG=yes - -ifndef OPACK_DEBUG -.SILENT: -$(info OPACK_DEBUG) -else -endif - # Set the hostname of the virtual machine to 'opack' if not specified OPACK_SYS_HOSTNAME?= opack # Set the username for the virtual machine to 'opack' if not specified -OPACK_SYS_USER?= opack +OPACK_SYS_USER?= opack # Set the password for the virtual machine to 'opack' if not specified -OPACK_SYS_PASSWORD?= opack +OPACK_SYS_PASSWORD?= opack # Set the disk size of the virtual machine to 4096 MB if not specified OPACK_SYS_DISK_SIZE?= 4096 @@ -116,7 +107,7 @@ OPACK_AUTODISKLABEL_FILE?=$(OPACK_DIR)autodisklabel OPACK_VAGRANT_FILE=Vagrantfile # Define the runtime provisioning script for Vagrant -OPACK_RUNTIME_PROVISION_FILE?=$(OPACK_DIR)vagrant-provision.sh +OPACK_RUNTIME_PROVISION_FILE?=$(OPACK_DIR)vagrant-provision.sh ifdef OPACK_DEBUG $(info ░ BOX $(OPACK_BOX_FILE)) diff --git a/src/vagrantfile.mk b/src/vagrantfile.mk index 35675fd..0e2b674 100644 --- a/src/vagrantfile.mk +++ b/src/vagrantfile.mk @@ -7,7 +7,7 @@ Vagrant.configure("2") do |config| config.vm.define "$(OPACK_TARGET)" config.vm.box = "$(OPACK_BOX_TAG)" config.ssh.shell = "ksh -l" - config.ssh.username = "root" + config.ssh.username = "$(OPACK_SYS_USER)" config.ssh.extra_args = "-tt" config.ssh.sudo_command = "doas env %c" config.vm.synced_folder ".", "/vagrant", disabled: true