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

From: Roman Scherer <[email-masked]>
Date: Sun, 16 Mar 2025 12:31:33 +0100

Hi Sergyey,

thanks for working on this. I just started to test your cross build
patch without offloading. It's building and I will report back when it
went through ...

The other patches also look fine to me, but I think they are missing
tests. Could you please add some tests for the functionality you added?

There are 2 types of tests, some of them are mocked and some of them run
against the Hetzner API and deploy 2 machines.

To run all tests you need to set the GUIX_HETZNER_API_TOKEN variable,
otherwise only the mock tests are run. You can run those tests with:

./pre-inst-env make check TESTS="tests/machine/hetzner/http.scm"
./pre-inst-env make check TESTS="tests/machine/hetzner.scm"

Thanks, Roman.

Sergey Trofimov <[email-masked]> writes:

> 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---