# AdGuard Home Technical Document The document describes technical details and internal algorithms of AdGuard Home. Contents: * First startup * Installation wizard * "Get install settings" command * "Check configuration" command * Disable DNSStubListener * "Apply configuration" command * Enable DHCP server * "Check DHCP" command * "Enable DHCP" command * Static IP check/set ## First startup The first application startup is detected when there's no .yaml configuration file. We check if the user is root, otherwise we fail with an error. Web server is started up on port 3000 and automatically redirects requests to `/` to Installation wizard. After Installation wizard steps are completed, we write configuration to a file and start normal operation. ## Installation wizard This is the collection of UI screens that are shown to a user on first application startup. The screens are: 1. Welcome 2. Set up network interface and listening ports for Web and DNS servers 3. Set up administrator username and password 4. Configuration complete 5. Done Algorithm: Screen 2: * UI asks server for initial information and shows it * User edits the default settings, clicks on "Next" button * UI asks server to check new settings * Server searches for the known issues * UI shows information about the known issues and the means to fix them * Server applies automatic fixes of the known issues on command from UI Screen 3: * UI asks server to apply the configuration * Server restarts DNS server ### "Get install settings" command Request: GET /control/install/get_addresses Response: 200 OK { "web_port":80, "dns_port":53, "interfaces":{ "enp2s0":{"name":"enp2s0","mtu":1500,"hardware_address":"","ip_addresses":["",""],"flags":"up|broadcast|multicast"}, "lo":{"name":"lo","mtu":65536,"hardware_address":"","ip_addresses":["127.0.0.1","::1"],"flags":"up|loopback"}, } } If `interfaces.flags` doesn't contain `up` flag, UI must show `(Down)` status next to its IP address in interfaces selector. ### "Check configuration" command Request: POST /control/install/check_config { "web":{"port":80,"ip":"192.168.11.33"}, "dns":{"port":53,"ip":"127.0.0.1","autofix":false}, } Server should check whether a port is available only in case it itself isn't already listening on that port. Server replies on success: 200 OK { "web":{"status":""}, "dns":{"status":""}, } Server replies on error: 200 OK { "web":{"status":"ERROR MESSAGE"}, "dns":{"status":"ERROR MESSAGE", "can_autofix": true|false}, } ### Disable DNSStubListener On Linux, if 53 port is not available, server performs several additional checks to determine if the issue can be fixed automatically. #### Phase 1 Request: POST /control/install/check_config { "dns":{"port":53,"ip":"127.0.0.1","autofix":false} } Check if DNSStubListener is enabled: systemctl is-enabled systemd-resolved Check if DNSStubListener is active: grep -E '#?DNSStubListener=yes' /etc/systemd/resolved.conf If the issue can be fixed automatically, server replies with `"can_autofix":true` 200 OK { "dns":{"status":"ERROR MESSAGE", "can_autofix":true}, } In this case UI shows "Fix" button next to error message. #### Phase 2 If user clicks on "Fix" button, UI sends request to perform an automatic fix POST /control/install/check_config { "dns":{"port":53,"ip":"127.0.0.1","autofix":true}, } Deactivate (save backup as `resolved.conf.orig`) and stop DNSStubListener: sed -r -i.orig 's/#?DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf systemctl reload-or-restart systemd-resolved Server replies: 200 OK { "dns":{"status":""}, } ### "Apply configuration" command Request: POST /control/install/configure { "web":{"port":80,"ip":"192.168.11.33"}, "dns":{"port":53,"ip":"127.0.0.1"}, "username":"u", "password":"p", } Server checks the parameters once again, restarts DNS server, replies: 200 OK On error, server responds with code 400 or 500. In this case UI should show error message and reset to the beginning. 400 Bad Request ERROR MESSAGE ## Enable DHCP server Algorithm: * UI shows DHCP configuration screen with "Enabled DHCP" button disabled, and "Check DHCP" button enabled * User clicks on "Check DHCP"; UI sends request to server * Server may fail to detect whether there is another DHCP server working in the network. In this case UI shows a warning. * Server may detect that a dynamic IP configuration is used for this interface. In this case UI shows a warning. * UI enables "Enable DHCP" button * User clicks on "Enable DHCP"; UI sends request to server * Server sets a static IP (if necessary), enables DHCP server, sends the status back to UI * UI shows the status ### "Check DHCP" command Request: POST /control/dhcp/find_active_dhcp vboxnet0 Response: 200 OK { "other_server": { "found": "yes|no|error", "error": "Error message", // set if found=error }, "static_ip": { "static": "yes|no|error", "ip": "", // set if static=no } } If `other_server.found` is: * `no`: everything is fine - there is no other DHCP server * `yes`: we found another DHCP server. UI shows a warning. * `error`: we failed to determine whether there's another DHCP server. `other_server.error` contains error details. UI shows a warning. If `static_ip.static` is: * `yes`: everything is fine - server uses static IP address. * `no`: `static_ip.ip` contains the current dynamic IP address which we may set as static. In this case UI shows a warning: Your system uses dynamic IP address configuration for interface . In order to use DHCP server a static IP address must be set. Your current IP address is . We will automatically set this IP address as static if you press Enable DHCP button. * `error`: this means that the server failed to check for a static IP. In this case UI shows a warning: In order to use DHCP server a static IP address must be set. We failed to determine if this network interface is configured using static IP address. Please set a static IP address manually. ### "Enable DHCP" command Request: POST /control/dhcp/set_config { "enabled":true, "interface_name":"vboxnet0", "gateway_ip":"192.169.56.1", "subnet_mask":"255.255.255.0", "range_start":"192.169.56.3", "range_end":"192.169.56.3", "lease_duration":60, "icmp_timeout_msec":0 } Response: 200 OK OK ### Static IP check/set Before enabling DHCP server we have to make sure the network interface we use has a static IP configured. #### Phase 1 On Debian systems DHCP is configured by `/etc/dhcpcd.conf`. To detect if a static IP is used currently we search for line interface eth0 and then look for line static ip_address=... If the interface already has a static IP, everything is set up, we don't have to change anything. To get the current IP address along with netmask we execute ip -oneline -family inet address show eth0 which will print: 2: eth0 inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0\ valid_lft forever preferred_lft forever To get the current gateway address: ip route show dev enp2s0 which will print: default via 192.168.0.1 proto dhcp metric 100 #### Phase 2 This method only works on Raspbian. On Ubuntu DHCP for a network interface can't be disabled via `dhcpcd.conf`. This must be configured in `/etc/netplan/01-netcfg.yaml`. Fedora doesn't use `dhcpcd.conf` configuration at all. Step 1. To set a static IP address we add these lines to `dhcpcd.conf`: interface eth0 static ip_address=192.168.0.1/24 static routers=192.168.0.1 static domain_name_servers=192.168.0.1 * Don't set 'routers' if we couldn't find gateway IP * Set 'domain_name_servers' equal to our IP Step 2. If we would set a different IP address, we'd need to replace the IP address for the current network configuration. But currently this step isn't necessary. ip addr replace dev eth0 192.168.0.1/24