+ add technical document
This commit is contained in:
parent
1ac9419c94
commit
bc81a0ecff
|
@ -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": "<Current dynamic IP address>", // 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 <CURRENT INTERFACE NAME>. In order to use DHCP server a static IP address must be set. Your current IP address is <static_ip.ip>. 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
|
Loading…
Reference in New Issue