summary refs log tree commit diff
path: root/configuration/vm/incus.nix
blob: 02fcd77dc1fef1b7eae17ac67c36a948989f8ddb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
{
  config,
  lib,
  pkgs,
  credentials,
  ...
}:

let
  # qemu 9.1.2 no longer supports strings being passed instead of some
  # integer parameters. incus already has the fix, but it hasn't made it
  # into a release yet.
  # see: https://github.com/lxc/incus/issues/1522
  incusPatch = pkgs.fetchpatch {
    name = "1531.patch";
    url = "https://github.com/lxc/incus/pull/1531.patch";
    sha256 = "sha256-tM/+JRH0OwR3bM8gk3yNo9SSAEMqpS2HP+OzooV3DJY=";
  };
  incus = pkgs.incus.overrideAttrs (attrs: {
    patches = (attrs.patches or [ ]) ++ [ incusPatch ];
  });

  toYAML = lib.generators.toYAML { };

  cloudInitConfiguration = {
    users = [
      (with credentials.mel; {
        name = "mel";
        groups = "users";
        sudo = "ALL=(ALL) NOPASSWD:ALL";
        passwd = password;
        lock_passwd = false;
        ssh_authorized_keys = keys;
      })
      (with credentials.philip; {
        name = "philip";
        groups = "users";
        sudo = "ALL=(ALL) NOPASSWD:ALL";
        passwd = password;
        lock_passwd = false;
        ssh_authorized_keys = keys;
      })
    ];

    # ssh configuration
    allow_public_ssh_keys = true;
    disable_root = true;
  };
in
{
  # needed so that the nixos firewall does not block
  # DHCP+DNS requests from incus, and to prevent conflicts
  # between the two firewalls.
  networking.firewall.trustedInterfaces = [ "incusbr0" ];
  virtualisation.incus = {
    enable = true;
    package = incus;
    preseed = {
      networks = [
        {
          # we don't really need internal ipv6 here, i think.
          config = {
            "ipv4.address" = "10.0.100.1/24";
            "ipv4.nat" = "true";
          };
          name = "incusbr0";
          type = "bridge";
        }
      ];
      profiles = [
        # this default profile gets applied to all
        # new instances without an explicitly set profile.
        {
          name = "default";
          # config applied to new instances,
          # this is how we can best control
          # vm provisioning semi-declaratively.
          # for options, see: https://linuxcontainers.org/incus/docs/main/reference/instance_options/
          config = {
            # `vendor` is usually for defaults, but it doesn't actually matter here.
            # NOTE: cloud-init requires either the incus-agent to be running,
            # or that the image is a special cloud image. i.e. `images:ubuntu/22.04/cloud`.
            "cloud-init.vendor-data" = ''
              #cloud-config
              ${toYAML cloudInitConfiguration}
            '';
          };
          devices = {
            # this is the internal vm network,
            # not the hosts.
            eth0 = {
              name = "eth0";
              network = "incusbr0";
              type = "nic";
            };
            root = {
              path = "/";
              pool = "default";
              size = "5GiB";
              type = "disk";
            };
          };
        }
      ];
      storage_pools = [
        {
          config = {
            source = "/var/lib/incus/storage-pools/default";
          };
          driver = "dir";
          name = "default";
        }
      ];
    };
  };

  # `incus-admin` essentially gives you root access anyway,
  # let users in `wheel` use it freely.
  users.groups."incus-admin".members = config.users.groups."wheel".members;
}