diff --git a/devshell.nix b/devshell.nix index e03777d..7bb2304 100644 --- a/devshell.nix +++ b/devshell.nix @@ -12,7 +12,7 @@ devshell = { name = "GIVC"; motd = '' - ❄️ Welcome to the {14}{bold}Welcome to givc-devshell{reset} devshell ❄️ + {14}{bold}❄️ Welcome to the givc devshell ❄️{reset} $(type -p menu &>/dev/null && menu) $(type -p update-pre-commit-hooks &>/dev/null && update-pre-commit-hooks) ''; @@ -21,8 +21,10 @@ config.treefmt.build.wrapper reuse go - gotests gopls + gosec + gotests + go-tools golangci-lint rustc rustfmt @@ -50,6 +52,11 @@ name = "gcl"; command = "grpcurl"; } + { + help = "Check golang vulnerabilities"; + name = "go-checksec"; + command = "gosec ./..."; + } { help = "Update go dependencies"; name = "go-update"; diff --git a/internal/pkgs/wifimanager/controller.go b/internal/pkgs/wifimanager/controller.go index 2048310..0384e43 100644 --- a/internal/pkgs/wifimanager/controller.go +++ b/internal/pkgs/wifimanager/controller.go @@ -379,6 +379,13 @@ func (c *WifiController) GetWifiDevices() ([]dbus.BusObject, error) { for _, devicePath := range devicePaths { device := c.conn.Object("org.freedesktop.NetworkManager", devicePath) + deviceManaged, err := device.GetProperty("org.freedesktop.NetworkManager.Device.Managed") + if err != nil { + return nil, fmt.Errorf("failed to get device type: %s", err) + } + if !deviceManaged.Value().(bool) { + continue // skip unmanaged devices + } deviceTypeVriant, err := device.GetProperty("org.freedesktop.NetworkManager.Device.DeviceType") if err != nil { return nil, fmt.Errorf("failed to get device type: %s", err) diff --git a/nixos/tests/default.nix b/nixos/tests/default.nix index 8f2ba78..ea600fd 100644 --- a/nixos/tests/default.nix +++ b/nixos/tests/default.nix @@ -1 +1,6 @@ -{ imports = [ ./admin.nix ]; } +{ + imports = [ + ./admin.nix + ./netvm.nix + ]; +} diff --git a/nixos/tests/netvm.nix b/nixos/tests/netvm.nix new file mode 100644 index 0000000..154ec39 --- /dev/null +++ b/nixos/tests/netvm.nix @@ -0,0 +1,286 @@ +{ + self, + lib, + inputs, + ... +}: +let + tls = false; + snakeoil = ./snakeoil; + addrs = { + netvm = "192.168.101.1"; + adminvm = "192.168.101.2"; + }; + adminSettings = { + name = "admin-vm"; + addr = addrs.adminvm; + port = "9001"; + protocol = "tcp"; # go version expect word "tcp" here, but it unused + }; + mkTls = name: { + enable = tls; + caCertPath = "${snakeoil}/${name}/ca-cert.pem"; + certPath = "${snakeoil}/${name}/${name}-cert.pem"; + keyPath = "${snakeoil}/${name}/${name}-key.pem"; + }; +in +{ + perSystem = _: { + vmTests.tests.netvm = { + module = { + nodes = { + adminvm = + { pkgs, ... }: + { + imports = [ self.nixosModules.admin ]; + + networking.interfaces.eth1.ipv4.addresses = lib.mkOverride 0 [ + { + address = addrs.adminvm; + prefixLength = 24; + } + ]; + environment.systemPackages = [ pkgs.grpcurl ]; + givc.admin = { + enable = true; + name = "admin-vm"; + addr = addrs.adminvm; + port = "9001"; + tls = mkTls "admin-vm"; + }; + }; + + netvm = + { pkgs, ... }: + let + inherit (import "${inputs.nixpkgs.outPath}/nixos/tests/ssh-keys.nix" pkgs) + snakeOilPublicKey + ; + in + { + imports = [ self.nixosModules.sysvm ]; + + # Setup users and keys + users.groups.ghaf = { }; + users.users = { + ghaf = { + isNormalUser = true; + group = "ghaf"; + openssh.authorizedKeys.keys = [ snakeOilPublicKey ]; + }; + }; + services.getty.autologinUser = "ghaf"; + # End of users + + # Emulate wireless interfaces + boot.kernelModules = [ "mac80211_hwsim" ]; + # boot.kernelParams = [ "mac80211_hwsim.radios=3" ]; + + # Setup fake access point + services.hostapd = { + enable = true; + radios.wlan0 = { + wifi6.enable = true; + networks = { + wlan0 = { + bssid = "02:00:00:00:00:01"; + ssid = "Test Network 1"; + authentication = { + mode = "wpa3-sae"; + saePasswords = [ { password = "secret-password"; } ]; + }; + }; + wlan0-1 = { + bssid = "02:00:00:00:00:02"; + ssid = "Test Network 2"; + authentication = { + mode = "wpa2-sha256"; + wpaPassword = "secret-password2"; + }; + }; + }; + }; + }; + + # Configure DHCP server + services.dnsmasq = { + enable = true; + settings = { + dhcp-range = [ + "interface:wlan0,192.168.1.10,192.168.1.200,255.255.255.0,1h" + "interface:wlan0-1,192.168.2.10,192.168.2.200,255.255.255.0,1h" + ]; + port = 0; + }; + }; + systemd.services.dnsmasq = { + after = [ "multi-user.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig.restart = "always"; + }; + + # Network settings + networking = { + firewall.enable = false; + interfaces = { + eth1.ipv4.addresses = [ + { + address = addrs.netvm; + prefixLength = 24; + } + ]; + wlan0 = { + useDHCP = false; + ipv4.addresses = [ + { + address = "192.168.1.1"; + prefixLength = 24; + } + ]; + }; + wlan0-1 = { + useDHCP = false; + ipv4.addresses = [ + { + address = "192.168.2.1"; + prefixLength = 24; + } + ]; + }; + wlan1.useDHCP = true; + }; + + # Enable NetworkManager + networkmanager = { + enable = true; + unmanaged = [ + "eth0" + "eth1" + "hwsim0" + "wlan0" + "wlan0-1" + ]; + }; + enableIPv6 = false; + wireless.enable = lib.mkForce false; + }; + + givc.sysvm = { + enable = true; + admin = adminSettings; + addr = addrs.netvm; + name = "net-vm"; + tls = mkTls "net-vm"; + wifiManager = true; + hwidService = true; + hwidIface = "wlan1"; + debug = true; + }; + }; + }; + testScript = + { nodes, ... }: + let + grpcurl_cmd = "/run/current-system/sw/bin/grpcurl "; + grpcurl_args = + if tls then + "-cacert ${nodes.netvm.givc.sysvm.tls.caCertPath} -cert ${nodes.netvm.givc.sysvm.tls.certPath} -key ${nodes.netvm.givc.sysvm.tls.keyPath}" + else + "-plaintext"; + grpcurl_addr = "${nodes.netvm.givc.sysvm.addr}:${nodes.netvm.givc.sysvm.port} "; + in + '' + import time + + with subtest("boot_completed"): + adminvm.wait_for_unit("multi-user.target") + netvm.wait_for_unit("multi-user.target") + print(netvm.succeed("systemd-analyze critical-chain")) + print(netvm.succeed("systemd-analyze blame")) + + with subtest("wifimanager_listnetworks"): + nwlistMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.ListNetwork") + print(nwlistMsg) + if not "networks" in nwlistMsg: + print("RPC 'wifimanager.WifiService.ListNetworks' failed, no networks found") + exit(1) + + with subtest("wifimanager_connect"): + # Test connection to network 1 + connectMsg = adminvm.succeed("${grpcurl_cmd} -d \'{\"SSID\":\"Test Network 1\",\"Password\":\"secret-password\"}\' ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.ConnectNetwork") + print(connectMsg) + if not "Connected" in connectMsg: + print("RPC 'wifimanager.WifiService.ConnectNetwork' failed") + exit(1) + + activeNetworkMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.GetActiveConnection") + print(activeNetworkMsg) + if not "\"Connection\": true" in activeNetworkMsg: + print("RPC 'wifimanager.WifiService.GetActiveConnection' failed") + exit(1) + + disconnectMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.DisconnectNetwork") + print(disconnectMsg) + if not "disconnected" in disconnectMsg: + print("RPC 'wifimanager.WifiService.DisconnectNetwork' failed") + exit(1) + + # Test connection to network 2 + connectMsg = adminvm.succeed("${grpcurl_cmd} -d \'{\"SSID\":\"Test Network 2\",\"Password\":\"secret-password2\"}\' ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.ConnectNetwork") + print(connectMsg) + if not "Connected" in connectMsg: + print("RPC 'wifimanager.WifiService.ConnectNetwork' failed") + exit(1) + + activeNetworkMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.GetActiveConnection") + print(activeNetworkMsg) + if not "\"Connection\": true" in activeNetworkMsg: + print("RPC 'wifimanager.WifiService.GetActiveConnection' failed") + exit(1) + + disconnectMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.DisconnectNetwork") + print(disconnectMsg) + if not "disconnected" in disconnectMsg: + print("RPC 'wifimanager.WifiService.DisconnectNetwork' failed") + exit(1) + + with subtest("wifimanager_turnoff"): + + # Test turn off wifi + turnoffMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.TurnOff") + print(turnoffMsg) + if not "Wireless disabled successfully" in turnoffMsg: + print("RPC 'wifimanager.WifiService.TurnOff' failed") + exit(1) + + nwlistMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.ListNetwork") + print(nwlistMsg) + if "networks" in nwlistMsg: + print("RPC 'wifimanager.WifiService.ListNetworks' failed, networks found") + exit(1) + + # Test turn on wifi + turnonMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.TurnOn") + print(turnonMsg) + if not "Wireless enabled successfully" in turnonMsg: + print("RPC 'wifimanager.WifiService.TurnOn' failed") + exit(1) + time.sleep(5) + nwlistMsg = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} wifimanager.WifiService.ListNetwork") + print(nwlistMsg) + if not "networks" in nwlistMsg: + print("RPC 'wifimanager.WifiService.ListNetworks' failed, no networks found") + exit(1) + + with subtest("test_hwid_manager"): + # Test hwid manager service + hwId = adminvm.succeed("${grpcurl_cmd} ${grpcurl_args} ${grpcurl_addr} hwid.HwidService.GetHwId") + print(hwId) + if not "Identifier" in hwId: + print("RPC 'hwid.HwidService.GetHwId' failed") + exit(1) + ''; + }; + }; + }; +} diff --git a/shell.nix b/shell.nix index 92e2c33..238c5c3 100644 --- a/shell.nix +++ b/shell.nix @@ -7,6 +7,7 @@ pkgs.mkShell { packages = with pkgs; [ go gopls + gosec gotests go-tools golangci-lint