Subject: [bug#75144] [PATCH] machine: Implement 'hetzner-environment-type'.

From: Sergey Trofimov <[email-masked]>
Date: Sat, 15 Mar 2025 13:22:31 +0100

Hi Roman,

Roman Scherer <[email-masked]> writes:

>
> I also have seen these long deploy times. You probably run into a
> situation where substitutes were not available for the thing you are
> deploying. It's a know issue with aarch64.

It was simply %hetzner-os-arm, nothing special. As I were deploying from
my local guix checkout, it could've caused more things to be built. I
recall most time got spent on ...-module-import derivations.

>>
>> gnu/build/linux-modules.scm:278:5: kernel module not found "pata_acpi" "/gnu/store/nh5icvr5qvlaq1y54gpkqndy0rv2cq9r-linux-libre-6.13.6/lib/modules"
>> --8<---------------cut here---------------end--------------->8---
>>
>> This seem to be caused by `deploy` not supporting `--target` parameter.
>> Adding these looked simple and I've jotted a small patch:
>>
>
> Nice! Do you plan to submit this as a patch, once you got it working?
>
https://issues.guix.gnu.org/77033

>>
>> Finally, I want to highlight a couple things that I haven't figured out
>> for my use-case yet:
>> 1. My private ssh key is stored in GnuPG and I'd like to keep it that
>> way. Afaik `managed-host-environment-type` can utilise the running
>> ssh-agent, could it be also implemented for hetzner machines?
>
> Your public key needs to be added as an SSH key via the Hetzner API. I
> believe the guix deploy command is doing the same here as the digital
> ocean one. It takes the ssh key from the machine config and creates the
> public key with the Hetzner API on the server.
>
> Maybe we could also support specifiy a fingerprint in the machine
> configuration and somehow get the public ssh key for it somehow from
> your GPG agent in Guile. Not sure how to do this though.
>
> I think the difference to managed-host-environment-type, is that with
> managed-host-environment-type someone already put the public key on the
> server (and authorized it) and Guix is using the private key from the
> SSH agent when it connects to it.
>

Only the public key is necessary to provision the VM. The private key
could be taken from ~/.ssh/config or ssh-agent by guile-ssh, the same as
it works for the managed-host. See the fix here: https://issues.guix.gnu.org/77013

>
>> 2. My use-case is an on-demand wireguard VPN. In my current setup I have
>> created a static ipv6 address which I attach to the VM created using
>> `hcloud`. The wireguard config hardcodes the same ipv6 and is installed
>> on the VM during cloud-init provision (`--user-data-from-file`
>> parameter). To replicate the same in guix deploy,
>> `hetzner-configuration` should be more flexible in regards to public ip
>> addresses. I.e. it should allow to use either v4 or v6 and to accept
>> existing one provided by the user.
>>
>
> Enabling/disabling IPv4/IPv4 should be easy to implement. The public_net
> option has settings for enable_ipv4 and enable_ipv6. They both default
> to #t, but it should be easy to add a configuration option for it.
>

Disabling ipv4 is a bit cumbersome - firstly the VM would have to rely
only on v6 and then the code would need to be adjusted to support
v6-only setups.

> https://docs.hetzner.cloud/#servers-create-a-server
>
> The public_net also support ipv4 and ipv6 fields. The docs say:
>
> ID of the ipv4 Primary IP to use. If omitted and enable_ipv4 is true, a
> new ipv4 Primary IP will automatically be created.
>
> And this seems to be the endpoint for creating those IPs:
>
> https://docs.hetzner.cloud/#primary-ips-create-a-primary-ip
>
> We don't have code to manage primary IPs in the Hetzner modules yet, but
> it shouldn't be hard to add it.
>

Here is the first revision of such change:
https://issues.guix.gnu.org/77019

Using all 3 patches I've been able to deploy such configuration:
./pre-inst-env guix deploy ~/.dotfiles/guix/hetzner-deploy.scm --system=aarch64-linux

--8<---------------cut here---------------start------------->8---
(machine
    (operating-system hetzner-os)
    (environment hetzner-environment-type)
    (configuration (hetzner-configuration
                    (server-type "cax11")
                    (build-locally? #f)
                    (location "hel1")
                    (ssh-public-key
                    (string->public-key "AAAA..<omitted>..==" 'rsa))
                    (ipv6 "vpn_ipv6"))))
--8<---------------cut here---------------end--------------->8---

However I had to adjust the operating-system to configure ipv6 upon
reboot:

--8<---------------cut here---------------start------------->8---
(service static-networking-service-type
    (list (static-networking
            (provision '(networking-ipv6))
            (requirement '(networking))
            (addresses
            (list (network-address
                    (device "eth0")
                    ; hetzner allocates /64, a static addr has to be
                    ; selected, ::1 in this case
                    (value "2a01:000:0000:0000::1/64"))))
            (routes
            (list (network-route
                    (destination "default")
                    (device "eth0")
                    (gateway "fe80::1"))))
            (name-servers
            '("1.1.1.1" "2a01:4ff:ff00::add:2" "2a01:4ff:ff00::add:1")))))
--8<---------------cut here---------------end--------------->8---