Using Kestra to update my proxmox IPSet
I've been using Kestra for a while now to automate some of the more mundane tasks around my network. One of these tasks is keeping the firewall up to date. This blog sits behind cloudflare, and as such, only cloudflare needs to access it. All other sources should be be denied (except internally).
I'm assuming for this that you have kestra setup, and you have used ansible via kestra before. If not, I do have other posts about ansible and kestra, but you might want to find an online tutorial for kestra. I run mine via docker and docker compose.
Cloudflare changes it's IP addresses from time to time and to keep this blog working, I need to track those changes. This doesn't happen every day, but I don't really want to be checking all the time either. Since the changes are infrequent and small, running the task once a day should keep everything in line.
Fortunately, cloudflare publishes it's IP ranges here. Since these are published as 2 files, we can use kestra to grab them and concatenate them.
id: proxmoxCloudflareIPSET
namespace: proxmox
description: Download and update the Cloudflare IPSet in Proxmox.
labels:
env: prod
project: proxmox
inputs:
- id: ipv4file
type: STRING
defaults: "https://www.cloudflare.com/ips-v4/#"
required: true
- id: ipv6file
type: STRING
defaults: "https://www.cloudflare.com/ips-v6/#"
required: true
tasks:
- id: getIPV4File
type: io.kestra.plugin.core.http.Download
uri: "{{ inputs.ipv4file }}"
- id: getIPV6File
type: io.kestra.plugin.core.http.Download
uri: "{{ inputs.ipv6file }}"
- id: concatIPSET
type: "io.kestra.plugin.core.storage.Concat"
files:
- "{{ outputs.getIPV4File.uri }}"
- "{{ outputs.getIPV6File.uri }}"
separator: "\n"
This allows us to have one big list for the IP block. We need to turn this block in to an IPSET in proxmox parlance. This is a set of IPs that we can then reference in our security groups later. First thing, we need to give it a name. I chose the descriptive one of "cloudflare". I added this as a variable, in case I want to change it later.
The next stage is setting up your template. Your cluster's firewall will look different to mine. The firewall configuration can be found in /etc/pve/firewall/cluster.fw - we will need this later on. While testing things out, I suggest you actually write files to /tmp and use the command:
diff -Naur /tmp/cluster.fw /etc/pve/firewall/cluster.fw
This way, you'll see any potential differences. When I was looking at automating this, I already had the IPSET of 'cloudflare'.
Copy your existing cluster.fw file to Kestra's data directory and in to the _files directory of the namespace you set for this. I chose the space "proxmox". I also created a folder called templates for it to live in. I also copied over my ansible hosts file to proxmox _files directory and stripped down to only proxmox. The cluster.fw file should have a .j2 file extension, so I called mine cluster-fw.j2.
Before we move on tot he ansible playbook, we need to add the following task to kestra:
Next, I created the file proxmox-cloudflare.yaml in the kestra namespace next to the hosts file. The playbook is quite simple:
All this playbook does is go through all the hosts 1 by 1, using sudo to become root and write the file to /tmp/cluster.fw based on the template we just created.
In order to have the IPSET be created, I editing my cluster-fw.j2 file to include the following (you may need to remove or modify a previous definition if you have one):
[IPSET {{ ipsetname }}] # Cloudflare IPs
{{ ipsetlist }}
Once complete, you can then run the task and verify that the file /tmp/cluster.fw is created and correct, compared to your original firewall file.
NB, the lack of space between the }} and the ] is intentional. If you add one, the name will be "cloudflare " rather than "cloudflare", and since a space in a name is illegal, it won't show up in proxmox.
For completeness, these are my files:
I don't trigger the task directly, as I prefer to control when tasks run via a set of triggers dedicated for the task. That way, I can see what jobs are running and when.
Once you are happy with task running, I added a securitygroup, also called cloudflare, and assigned it to my blog vm and removed the previos security group allowing the world in on port 80 and 443. This groups is included in the cluster-fw.j2 file.
Once you have checked all is up and running, you now have kestra keeping proxmox's firewall up to date with cloudflare changes!