imfreedom/terraform

Initial revision

9 months ago, Gary Kramlich
3262b2b3d528
Parents
Children e248bd1a35ba
Initial revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,5 @@
+syntax: regexp
+\.terraform\/
+\.terraform\.lock\.hcl
+local\.tfvars
+^nodes\/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,45 @@
+NODE =
+PLAN_LIMIT = $(LIMIT:%= -target=%)
+TFARGS = -var-file nodes/$(NODE).tfvars
+PARALLELISM := 4
+
+all: plan
+
+plan: _require_node _state
+ terraform plan -parallelism=$(PARALLELISM) -out $(NODE).tfplan $(TFARGS) $(PLAN_LIMIT)
+
+showplan: _require_node _state
+ terraform showplan $(NODE).tfplan
+
+destroy: _require_node _state
+ terraform plan -destroy -out $(NODE).tfplan $(TFARGS)
+
+apply: _require_node _state
+ terraform apply -input=true $(NODE).tfplan
+ rm $(NODE).tfplan
+
+refresh: _require_node _state
+ terraform refresh $(TFARGS)
+
+clean:
+ rm -f *.tfplan*
+ rm -f *.tfstate*
+
+_require_node: nodes/$(NODE).tfvars
+ $(warning *)
+ $(warning *)
+ $(warning Executing on host $(NODE))
+ $(warning *)
+ $(warning *)
+
+_state: _require_node
+ifneq ("$(wildcard .terraform/terraform.tfstate)","")
+ rm .terraform/terraform.tfstate
+endif
+ terraform init --backend-config="key=$(NODE).tfstate"
+
+nodes/$(NODE).tfvars:
+ @echo unknown node \"$(NODE)\"
+ @exit 1
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,117 @@
+# IMFreedom Terraform
+
+This repository contains [terraform](https://www.terraform.io/) for imfreedom
+resources.
+
+All machines are configured to be logged into via the `admin` user account.
+There are a number of reasons for this, but the most important one is that
+`sudo` uses get logged for normal users which the `admin` user is.
+
+# Setup
+
+TLDR: install mkisofs, libvirt, [terraform](https://www.terraform.io/),
+[terraform-libvirt-provider](https://github.com/dmacvicar/terraform-provider-libvirt),
+and make sure the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment
+variables are properly set to the imfreedom [minio](https://data.imfreedom.org)
+instance.
+
+`Terraform` can be run from anywhere that this repository is cloned to with a
+few caveats. First you need to install [terraform](https://www.terraform.io/).
+I typically install it to `~/.local/bin/`. Verify `Terraform` is installed and
+on your path by running `terraform version`.
+
+~~Once you have a working `Terraform` install you need to install the
+[terraform-libvirt-provider](https://github.com/dmacvicar/terraform-provider-libvirt).
+This should be installed into `~/.terraform.d/plugins`.~~
+This might be done by `terraform init` now.
+
+Next we need to make sure that `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`
+environment variables are set to your credentials to the imfreedom
+[minio](https://data.imfreedom.org/) instance. If you need credentials, talk
+to grim. To make sure these environment variables are set, I recommend using
+[direnv](https://direnv.net/) and saving them to your `.envrc` in your clone.
+This file is already ignored in `.hgignore`
+
+On Debian based hosts there is an apparmor policy that is missing that will
+cause permission errors. To fix it you need to add the following to
+`/etc/apparmor.d/abstractions/libvirt-qemu`. I've been putting it at the end
+of the file and everything is fine.
+
+```
+ # for terraform-libvirt
+ /var/lib/libvirt/images/* rwk,
+```
+
+# Node Configuration
+
+To actually `Terraform` a node, we first need to create a `<node>.tfvars` in
+the `nodes/` directory. A bare minimal example is below.
+
+```
+libvirt_uri = "qemu+ssh://host/system"
+```
+
+`libvirt_uri` is the connect string for libvirt.
+
+You can also use the `volume_pool` variable to use a different volume pool.
+
+There is an optional `node_prefix` setting you can use to prefix all of the
+host names for the machines. This prefix will not change the names of the
+machines in libvirt, it's meant to disambiguate the machines in tailscale. It's
+obvious what node they belong to when you're looking at the nodes but in
+tailscale we don't have that luxury.
+
+Once that file is created you can provision the node via:
+
+```
+make plan NODE=node1
+make apply NODE=node1
+```
+
+Based on the number of vms, this can take quite a bit of time so make sure to
+run it in screen/tmux and maintain a stable internet connection.
+
+Additional configuration for the `<node>.tfvars` file is below in the section
+for each machine type.
+
+## FreeBSD AMD64
+
+The FreeBSD AMD64 machines have a number of options which are explained below.
+These should be set in your `<node>.tfvars` file.
+
+**freebsd_amd64_count**: How many FreeBSD AMD64 machines to create. The default
+is `0`.
+
+**freebsd_amd64_volume_pool**: The name of which `libvirt` volume pool to use.
+This is seldomly changed, but defaults to `default`.
+
+**freebsd_amd64_ssh_pubkeys**: A list of SSH public keys that are allowed to
+connect to this machine. The default is an empty list which means you will not
+be able to get into the machine.
+
+**freebsd_amd64_base_image**: A dictionary containing information about the
+base image.
+
+This has keys for the name, source URI, and format of the image.
+
+The name is the name to use in the libvirt storage pool. This defaults to
+`freebsd-amd64-base`.
+
+The `source` key is the URI to the file itself. We are currently uses images
+from [bsd-cloud-image.org](https://bsd-cloud-image.org/). Getting the URIs from
+there is kind of annoying as you have to start a download to get it.
+
+Finally the `format` key the format of the image pointed to by the `source`
+key. Currently this is `qcow2`.
+
+**freebsd_amd64_volume_size**: The size of the HDD for the machine. This
+defaults to `10GB`.
+
+**freebsd_amd64_memory_size**: The amount of memory to give to the machine.
+Defaults to `1GB`.
+
+**freebsd_amd64_cpu_count**: The number of CPUs to give the machine. Defaults
+to `1`.
+
+**freebsd_amd64_network_name**: The name of the libvirt network to use. This is
+seldomly changed, but defaults to `default`.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/instance-freebsd-amd64.tf Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,117 @@
+variable freebsd_amd64_count { default = 0 }
+variable freebsd_amd64_volume_pool { default = "default" }
+variable freebsd_amd64_ssh_pubkeys { default = [] }
+variable freebsd_amd64_base_image { default = {
+ "name": "freebsd-amd64-base",
+ "source": "https://object-storage.public.mtl1.vexxhost.net/swift/v1/1dbafeefbd4f4c80864414a441e72dd2/bsd-cloud-image.org/images/freebsd/13.2/2023-04-22/ufs/freebsd-13.2-ufs-2023-04-22.qcow2",
+ "format": "qcow2",
+}}
+variable freebsd_amd64_volume_size { default = "10737418240" } # 10gb
+variable freebsd_amd64_memory_size { default = "1024" } # 1gb
+variable freebsd_amd64_cpu_count { default = "1" }
+variable freebsd_amd64_network_name { default = "default" }
+
+data "template_file" "freebsd_amd64_cloudinit" {
+ count = var.freebsd_amd64_count
+
+ template = <<EOF
+#cloud-config
+fqdn: $${fqdn}
+groups:
+ - admin
+users:
+ - default
+ - name: admin
+ primary_group: admin
+ ssh_authorized_keys: $${admin_ssh_pubkeys}
+ sudo: ALL=(ALL) NOPASSWD:ALL
+growpart:
+ mode: auto
+ devices:
+ - /
+ ignore_growroot_disabled: false
+disable_root: true
+# Uncomment this if you need to debug stuff
+#disable_root: false
+#chpasswd:
+# expire: false
+# users:
+# - name: root
+# password: abc123
+# type: text
+EOF
+ vars = {
+ "fqdn": format("%sfreebsd-amd64-%02d", var.node_prefix, count.index),
+ "admin_ssh_pubkeys": jsonencode(var.freebsd_amd64_ssh_pubkeys),
+ }
+}
+
+resource "libvirt_cloudinit_disk" "freebsd_amd64_cloudinit" {
+ count = var.freebsd_amd64_count
+
+ name = format("freebsd-amd64-%02d-cloudinit.iso", count.index)
+ pool = var.freebsd_amd64_volume_pool
+
+ user_data = element(data.template_file.freebsd_amd64_cloudinit.*.rendered, count.index)
+}
+
+resource "libvirt_volume" "freebsd_amd64_base_image" {
+ count = (var.freebsd_amd64_count > 0 ? 1 : 0)
+
+ pool = var.freebsd_amd64_volume_pool
+
+ name = format("%s.%s", var.freebsd_amd64_base_image["name"], var.freebsd_amd64_base_image["format"])
+ source = var.freebsd_amd64_base_image["source"]
+ format = var.freebsd_amd64_base_image["format"]
+}
+
+resource "libvirt_volume" "freebsd_amd64_image" {
+ count = var.freebsd_amd64_count
+
+ pool = var.freebsd_amd64_volume_pool
+
+ name = format("freebsd-amd64-%02d.%s", count.index, var.freebsd_amd64_base_image["format"])
+ base_volume_id = libvirt_volume.freebsd_amd64_base_image[0].id
+ format = var.freebsd_amd64_base_image["format"]
+ size = var.freebsd_amd64_volume_size
+}
+
+resource "libvirt_domain" "freebsd_amd64" {
+ count = var.freebsd_amd64_count
+
+ name = format("freebsd-amd64-%02d", count.index)
+
+ cpu = {
+ mode = "host-passthrough"
+ }
+
+ memory = var.freebsd_amd64_memory_size
+ vcpu = var.freebsd_amd64_cpu_count
+ autostart = true
+
+ cloudinit = libvirt_cloudinit_disk.freebsd_amd64_cloudinit[count.index].id
+
+ console {
+ type = "pty"
+ target_port = "0"
+ target_type = "serial"
+ }
+
+ disk {
+ volume_id = libvirt_volume.freebsd_amd64_image[count.index].id
+ }
+
+ network_interface {
+ network_name = var.freebsd_amd64_network_name
+ wait_for_lease = true
+ }
+
+ boot_device {
+ dev = ["hd"]
+ }
+}
+
+output "freebsd_ips" {
+ value = "${flatten(libvirt_domain.freebsd_amd64.*.network_interface.0.addresses)}"
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/nodes.tf Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,1 @@
+variable node_prefix { default = "" }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/provider-libvirt.tf Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,6 @@
+variable "libvirt_uri" { default = "qemu:///system" }
+
+provider "libvirt" {
+ uri = "${var.libvirt_uri}"
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/terraform.tf Tue Jul 25 00:09:53 2023 -0500
@@ -0,0 +1,20 @@
+terraform {
+ required_providers {
+ libvirt = {
+ source = "dmacvicar/libvirt"
+ version = "0.6.10"
+ }
+ }
+
+ backend "s3" {
+ region = "main"
+ bucket = "terraform"
+ endpoint = "https://data.imfreedom.org"
+ force_path_style = true
+ skip_credentials_validation = true
+ skip_region_validation = true
+ skip_get_ec2_platforms = true
+ skip_metadata_api_check = false
+ }
+}
+