summary refs log tree commit diff
path: root/configuration/vm/incus.nix
blob: 62293e89b50ac7cee1752008e110ac668ee6bc0b (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
{
  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;
    packages = [ "openssh-server" ];
    runcmd = [
      [
        "systemctl"
        "enable"
        "ssh.service"
      ]
    ];
  };
in
{
  networking.firewall = {
    # needed so that the nixos firewall does not block
    # DHCP+DNS requests from incus, and to prevent conflicts
    # between the two firewalls.
    trustedInterfaces = [ "incusbr0" ];
    allowedTCPPorts = [ 23 ];
  };
  # needed so inscus instances can connect to the proxy.
  boot.kernelModules = [ "br_netfilter" ];
  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 = { };
          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";
            };
          };
        }
        # this profile is the one we want to apply to the ubuntu example vm.
        # it is provisioned with a static ipv4 (for nat-ted proxy)
        # and cloud-init configuration
        {
          name = "vm-1";
          # 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";

              # this is necessary for our nat proxy configuration.
              # see: https://linuxcontainers.org/incus/docs/main/reference/devices_proxy/#nat-mode
              "ipv4.address" = "10.0.100.123";
            };
            proxy = {
              type = "proxy";
              listen = "tcp:162.19.227.249:23";
              connect = "tcp:0.0.0.0:22";
              nat = true;
            };
            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;
}