diff --git a/AGHTechDoc.md b/AGHTechDoc.md new file mode 100644 index 00000000..556ef859 --- /dev/null +++ b/AGHTechDoc.md @@ -0,0 +1,327 @@ +# 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