mirror of
https://github.com/jbowdre/runtimeterror.git
synced 2024-11-26 17:02:17 +00:00
163 lines
5.6 KiB
Markdown
163 lines
5.6 KiB
Markdown
|
---
|
||
|
title: "Building Proxmox Templates with Packer and GitHub Actions"
|
||
|
date: 2024-06-12
|
||
|
# lastmod: 2024-06-12
|
||
|
draft: true
|
||
|
description: "Using Packer, Vault, a GitHub Actions workflow, and self-hosted runners to automatically build VM templates for my Proxmox homelab."
|
||
|
featured: false
|
||
|
toc: true
|
||
|
reply: true
|
||
|
categories: Tips
|
||
|
tags:
|
||
|
- automation
|
||
|
- cicd
|
||
|
- docker
|
||
|
- homelab
|
||
|
- iac
|
||
|
- linux
|
||
|
- packer
|
||
|
- proxmox
|
||
|
- tailscale
|
||
|
- vault
|
||
|
---
|
||
|
|
||
|
I've been [using Proxmox](/ditching-vsphere-for-proxmox/) in my [homelab](/homelab/) for a little while now, and I recently expanded the environment a bit with the addition of two HP Elite Mini 800 G9 computers. I figured it was time to start automating the process of building and maintaining my VM templates. I already had functional [Packer templates for VMware](https://github.com/jbowdre/packer-vsphere-templates) so I used that content as a starting point for the builds themselves. Once I had the builds working locally, I just had to explore how to automate them.
|
||
|
|
||
|
This post will describe how I did it.
|
||
|
|
||
|
### Component Overview
|
||
|
There are a lot of parts to this setup, so let's start by quickly running through those:
|
||
|
- a **Vault instance** running in a container in the lab to hold the secrets needed for the builds,
|
||
|
- a **Proxmox host** to serve the virtual infrastructure and provide compute for the new templates,
|
||
|
- an **on-premise self-hosted GitHub runner** to simplify connectivity between GitHub and my homelab,
|
||
|
- and a **private GitHub repo** to hold the code and tell the runner when it's time to get to work.
|
||
|
|
||
|
{{% notice note "Private Repo!" %}}
|
||
|
GitHub [strongly recommends](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#self-hosted-runner-security) that self-hosted runners *only* be used with private repositories.
|
||
|
> This is because forks of your public repository can potentially run dangerous code on your self-hosted runner machine by creating a pull request that executes the code in a workflow.
|
||
|
|
||
|
I don't like the idea of randos running arbitrary code on my home infrastructure. So while I'm sharing my work publicly [in this repo](https://github.com/jbowdre/packer-proxmox-templates), the workflows there are disabled and there are no connected runners. I'm running my builds out of a private repo and recommend that you do the same.
|
||
|
{{% /notice %}}
|
||
|
|
||
|
### Vault
|
||
|
I use [Vault](https://github.com/hashicorp/vault) to hold the configuration details for the template builds - not just traditional secrets like usernames and passwords, but basically *every environment-specific setting* as well. This way the Packer templates can be used in different environments without having to change much (if *any*) of the committed code.
|
||
|
|
||
|
I'm using [Vault in Docker](https://hub.docker.com/r/hashicorp/vault), and I'm also making it available within my tailnet with [Tailscale Serve](/tailscale-serve-docker-compose-sidecar/) using the following `docker-compose.yaml`
|
||
|
|
||
|
```yaml
|
||
|
# torchlight! {"lineNumbers":true}
|
||
|
services:
|
||
|
tailscale:
|
||
|
image: tailscale/tailscale:latest
|
||
|
container_name: vault-tailscaled
|
||
|
restart: unless-stopped
|
||
|
environment:
|
||
|
TS_AUTHKEY: ${TS_AUTHKEY:?err}
|
||
|
TS_HOSTNAME: vault
|
||
|
TS_STATE_DIR: "/var/lib/tailscale/"
|
||
|
TS_SERVE_CONFIG: /config/serve-config.json
|
||
|
volumes:
|
||
|
- ./ts_data:/var/lib/tailscale/
|
||
|
- ./serve-config.json:/config/serve-config.json
|
||
|
|
||
|
vault:
|
||
|
image: hashicorp/vault
|
||
|
container_name: vault
|
||
|
restart: unless-stopped
|
||
|
environment:
|
||
|
VAULT_ADDR: 'https://0.0.0.0:8200'
|
||
|
cap_add:
|
||
|
- IPC_LOCK
|
||
|
volumes:
|
||
|
- ./data:/vault/data
|
||
|
- ./config:/vault/config
|
||
|
- ./log:/vault/log
|
||
|
command: vault server -config=/vault/config/vault.hcl
|
||
|
network_mode: "service:tailscale"
|
||
|
```
|
||
|
|
||
|
Vault's `./config/vault.hcl`:
|
||
|
|
||
|
```hcl
|
||
|
ui = true
|
||
|
|
||
|
listener "tcp" {
|
||
|
address = "0.0.0.0:8200"
|
||
|
tls_disable = "true"
|
||
|
}
|
||
|
|
||
|
storage "file" {
|
||
|
path = "/vault/data"
|
||
|
}
|
||
|
```
|
||
|
|
||
|
And Tailscale's `./serve-config.json`:
|
||
|
|
||
|
```json
|
||
|
# torchlight! {"lineNumbers":true}
|
||
|
{
|
||
|
"TCP": {
|
||
|
"443": {
|
||
|
"HTTPS": true
|
||
|
}
|
||
|
},
|
||
|
"Web": {
|
||
|
"vault.tailnet-name.ts.net:443": {
|
||
|
"Handlers": {
|
||
|
"/": {
|
||
|
"Proxy": "http://127.0.0.1:8200"
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
After performing the initial Vault setup, I then create a [kv-v2](https://developer.hashicorp.com/vault/docs/secrets/kv/kv-v2) secrets engine
|
||
|
for Packer to use:
|
||
|
|
||
|
```shell
|
||
|
vault secrets enable -path=packer kv-v2 # [tl! .cmd]
|
||
|
Success! Enabled the kv-v2 secrets engine at: packer/ # [tl! .nocopy]
|
||
|
```
|
||
|
|
||
|
And I define a [policy](https://developer.hashicorp.com/vault/docs/concepts/policies) which will grant the bearer read-only access to the data stored in the `packer` secrets as well as the ability to create and update its own tokens:
|
||
|
|
||
|
```shell
|
||
|
cat << EOF | vault policy write packer -
|
||
|
path "packer/*" {
|
||
|
capabilities = ["read", "list"]
|
||
|
}
|
||
|
|
||
|
path "auth/token/renew-self" {
|
||
|
capabilities = ["update"]
|
||
|
}
|
||
|
|
||
|
path "auth/token/create" {
|
||
|
capabilities = ["create", "update"]
|
||
|
}
|
||
|
EOF # [tl! .cmd:-12,1]
|
||
|
|
||
|
Success! Uploaded policy: packer2 # [tl! .nocopy]
|
||
|
```
|
||
|
|
||
|
Now I just need to create a token attached to the policy:
|
||
|
|
||
|
```shell
|
||
|
vault token create -policy=packer -no-default-policy
|
||
|
-orphan -ttl=4h -period=336h -display-name=packer # [tl! .cmd:-1,1 ]
|
||
|
|
||
|
Key Value # [tl! .nocopy:8]
|
||
|
--- -----
|
||
|
token hvs.CAES[...]GSFQ
|
||
|
token_accessor aleV[...]xu5I
|
||
|
token_duration 336h
|
||
|
token_renewable true
|
||
|
token_policies ["packer"]
|
||
|
identity_policies []
|
||
|
policies ["packer"]
|
||
|
```
|
||
|
|
||
|
Within the `packer` secrets engine, I have two secrets which each have a number of subkeys.
|
||
|
|