Added install methods to openapi.yaml
Print all net interfaces when bind_host is 0.0.0.0
This commit is contained in:
parent
e8898811fe
commit
4e1c1618cb
33
app.go
33
app.go
@ -208,8 +208,7 @@ func run(args options) {
|
||||
},
|
||||
}
|
||||
|
||||
URL := fmt.Sprintf("https://%s", address)
|
||||
log.Println("Go to " + URL)
|
||||
printHTTPAddresses("https")
|
||||
err = httpsServer.server.ListenAndServeTLS("", "")
|
||||
if err != http.ErrServerClosed {
|
||||
log.Fatal(err)
|
||||
@ -220,10 +219,10 @@ func run(args options) {
|
||||
|
||||
// this loop is used as an ability to change listening host and/or port
|
||||
for {
|
||||
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
||||
URL := fmt.Sprintf("http://%s", address)
|
||||
log.Println("Go to " + URL)
|
||||
printHTTPAddresses("http")
|
||||
|
||||
// we need to have new instance, because after Shutdown() the Server is not usable
|
||||
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
||||
httpServer = &http.Server{
|
||||
Addr: address,
|
||||
}
|
||||
@ -395,3 +394,27 @@ func loadOptions() options {
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
// prints IP addresses which user can use to open the admin interface
|
||||
// proto is either "http" or "https"
|
||||
func printHTTPAddresses(proto string) {
|
||||
var address string
|
||||
if config.BindHost == "0.0.0.0" {
|
||||
log.Println("AdGuard Home is available on the following addresses:")
|
||||
ifaces, err := getValidNetInterfacesForWeb()
|
||||
if err != nil {
|
||||
// That's weird, but we'll ignore it
|
||||
address = net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
||||
log.Printf("Go to %s://%s", proto, address)
|
||||
return
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
address = net.JoinHostPort(iface.Addresses[0], strconv.Itoa(config.BindPort))
|
||||
log.Printf("Go to %s://%s", proto, address)
|
||||
}
|
||||
} else {
|
||||
address = net.JoinHostPort(config.BindHost, strconv.Itoa(config.BindPort))
|
||||
log.Printf("Go to %s://%s", proto, address)
|
||||
}
|
||||
}
|
||||
|
10
config.go
10
config.go
@ -32,11 +32,11 @@ type configuration struct {
|
||||
ourWorkingDir string // Location of our directory, used to protect against CWD being somewhere else
|
||||
firstRun bool // if set to true, don't run any services except HTTP web inteface, and serve only first-run html
|
||||
|
||||
BindHost string `yaml:"bind_host"`
|
||||
BindPort int `yaml:"bind_port"`
|
||||
AuthName string `yaml:"auth_name"`
|
||||
AuthPass string `yaml:"auth_pass"`
|
||||
Language string `yaml:"language"` // two-letter ISO 639-1 language code
|
||||
BindHost string `yaml:"bind_host"` // BindHost is the IP address of the HTTP server to bind to
|
||||
BindPort int `yaml:"bind_port"` // BindPort is the port the HTTP server
|
||||
AuthName string `yaml:"auth_name"` // AuthName is the basic auth username
|
||||
AuthPass string `yaml:"auth_pass"` // AuthPass is the basic auth password
|
||||
Language string `yaml:"language"` // two-letter ISO 639-1 language code
|
||||
DNS dnsConfig `yaml:"dns"`
|
||||
TLS tlsConfig `yaml:"tls"`
|
||||
Filters []filter `yaml:"filters"`
|
||||
|
53
control.go
53
control.go
@ -909,17 +909,6 @@ type firstRunData struct {
|
||||
|
||||
func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) {
|
||||
data := firstRunData{}
|
||||
ifaces, err := getValidNetInterfaces()
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err)
|
||||
return
|
||||
}
|
||||
if len(ifaces) == 0 {
|
||||
httpError(w, http.StatusServiceUnavailable, "Couldn't find any legible interface, plase try again later")
|
||||
return
|
||||
}
|
||||
|
||||
// fill out the fields
|
||||
|
||||
// find out if port 80 is available -- if not, fall back to 3000
|
||||
if checkPortAvailable("", 80) == nil {
|
||||
@ -934,41 +923,15 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) {
|
||||
data.DNS.Warning = "Port 53 is not available for binding -- this will make DNS clients unable to contact AdGuard Home."
|
||||
}
|
||||
|
||||
ifaces, err := getValidNetInterfacesForWeb()
|
||||
if err != nil {
|
||||
httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
data.Interfaces = make(map[string]interface{})
|
||||
for _, iface := range ifaces {
|
||||
addrs, e := iface.Addrs()
|
||||
if e != nil {
|
||||
httpError(w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
jsonIface := netInterface{
|
||||
Name: iface.Name,
|
||||
MTU: iface.MTU,
|
||||
HardwareAddr: iface.HardwareAddr.String(),
|
||||
}
|
||||
|
||||
if iface.Flags != 0 {
|
||||
jsonIface.Flags = iface.Flags.String()
|
||||
}
|
||||
|
||||
// we don't want link-local addresses in json, so skip them
|
||||
for _, addr := range addrs {
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
// not an IPNet, should not happen
|
||||
httpError(w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr)
|
||||
return
|
||||
}
|
||||
// ignore link-local
|
||||
if ipnet.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
jsonIface.Addresses = append(jsonIface.Addresses, ipnet.IP.String())
|
||||
}
|
||||
if len(jsonIface.Addresses) != 0 {
|
||||
data.Interfaces[iface.Name] = jsonIface
|
||||
}
|
||||
data.Interfaces[iface.Name] = iface
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
@ -983,7 +946,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) {
|
||||
newSettings := firstRunData{}
|
||||
err := json.NewDecoder(r.Body).Decode(&newSettings)
|
||||
if err != nil {
|
||||
httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err)
|
||||
httpError(w, http.StatusBadRequest, "Failed to parse new config json: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
52
helpers.go
52
helpers.go
@ -15,6 +15,8 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/joomcode/errorx"
|
||||
)
|
||||
|
||||
// ----------------------------------
|
||||
@ -237,6 +239,56 @@ func getValidNetInterfaces() ([]net.Interface, error) {
|
||||
return netIfaces, nil
|
||||
}
|
||||
|
||||
// getValidNetInterfacesMap returns interfaces that are eligible for DNS and WEB only
|
||||
// we do not return link-local addresses here
|
||||
func getValidNetInterfacesForWeb() ([]netInterface, error) {
|
||||
ifaces, err := getValidNetInterfaces()
|
||||
if err != nil {
|
||||
return nil, errorx.Decorate(err, "Couldn't get interfaces")
|
||||
}
|
||||
if len(ifaces) == 0 {
|
||||
return nil, errors.New("couldn't find any legible interface")
|
||||
}
|
||||
|
||||
var netInterfaces []netInterface
|
||||
|
||||
for _, iface := range ifaces {
|
||||
addrs, e := iface.Addrs()
|
||||
if e != nil {
|
||||
return nil, errorx.Decorate(e, "Failed to get addresses for interface %s", iface.Name)
|
||||
}
|
||||
|
||||
netIface := netInterface{
|
||||
Name: iface.Name,
|
||||
MTU: iface.MTU,
|
||||
HardwareAddr: iface.HardwareAddr.String(),
|
||||
}
|
||||
|
||||
if iface.Flags != 0 {
|
||||
netIface.Flags = iface.Flags.String()
|
||||
}
|
||||
|
||||
// we don't want link-local addresses in json, so skip them
|
||||
for _, addr := range addrs {
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
// not an IPNet, should not happen
|
||||
return nil, fmt.Errorf("SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr)
|
||||
}
|
||||
// ignore link-local
|
||||
if ipnet.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
netIface.Addresses = append(netIface.Addresses, ipnet.IP.String())
|
||||
}
|
||||
if len(netIface.Addresses) != 0 {
|
||||
netInterfaces = append(netInterfaces, netIface)
|
||||
}
|
||||
}
|
||||
|
||||
return netInterfaces, nil
|
||||
}
|
||||
|
||||
// checkPortAvailable is not a cheap test to see if the port is bindable, because it's actually doing the bind momentarily
|
||||
func checkPortAvailable(host string, port int) error {
|
||||
ln, err := net.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(port)))
|
||||
|
25
helpers_test.go
Normal file
25
helpers_test.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hmage/golibs/log"
|
||||
)
|
||||
|
||||
func TestGetValidNetInterfacesForWeb(t *testing.T) {
|
||||
ifaces, err := getValidNetInterfacesForWeb()
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot get net interfaces: %s", err)
|
||||
}
|
||||
if len(ifaces) == 0 {
|
||||
t.Fatalf("No net interfaces found")
|
||||
}
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if len(iface.Addresses) == 0 {
|
||||
t.Fatalf("No addresses found for %s", iface.Name)
|
||||
}
|
||||
|
||||
log.Printf("%v", iface)
|
||||
}
|
||||
}
|
@ -39,6 +39,9 @@ tags:
|
||||
-
|
||||
name: dhcp
|
||||
description: 'Built-in DHCP server controls'
|
||||
-
|
||||
name: install
|
||||
description: 'First-time install configuration handlers'
|
||||
paths:
|
||||
|
||||
# API TO-DO LIST
|
||||
@ -713,6 +716,42 @@ paths:
|
||||
text/plain:
|
||||
en
|
||||
|
||||
# --------------------------------------------------
|
||||
# First-time install configuration methods
|
||||
# --------------------------------------------------
|
||||
|
||||
/install/get_addresses:
|
||||
get:
|
||||
tags:
|
||||
- install
|
||||
operationId: installGetAddresses
|
||||
summary: "Gets the network interfaces information."
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
schema:
|
||||
$ref: "#/definitions/AddressesInfo"
|
||||
/install/configure:
|
||||
post:
|
||||
tags:
|
||||
- install
|
||||
operationId: installConfigure
|
||||
summary: "Applies the initial configuration."
|
||||
parameters:
|
||||
- in: "body"
|
||||
name: "body"
|
||||
description: "Initial configuration JSON"
|
||||
required: true
|
||||
schema:
|
||||
$ref: "#/definitions/InitialConfiguration"
|
||||
responses:
|
||||
200:
|
||||
description: OK
|
||||
400:
|
||||
description: "Failed to parse initial configuration or cannot listen to the specified addresses"
|
||||
500:
|
||||
description: "Cannot start the DNS server"
|
||||
|
||||
definitions:
|
||||
ServerStatus:
|
||||
type: "object"
|
||||
@ -1207,4 +1246,70 @@ definitions:
|
||||
warning_validation:
|
||||
type: "string"
|
||||
example: "You have specified an empty certificate"
|
||||
description: "warning_validation is a validation warning message with the issue description"
|
||||
description: "warning_validation is a validation warning message with the issue description"
|
||||
NetInterface:
|
||||
type: "object"
|
||||
description: "Network interface info"
|
||||
properties:
|
||||
flags:
|
||||
type: "string"
|
||||
example: "up|broadcast|multicast"
|
||||
hardware_address:
|
||||
type: "string"
|
||||
example: "52:54:00:11:09:ba"
|
||||
mtu:
|
||||
type: "integer"
|
||||
format: "int32"
|
||||
example: 1500
|
||||
name:
|
||||
type: "string"
|
||||
example: "eth0"
|
||||
ip_addresses:
|
||||
type: "array"
|
||||
items:
|
||||
type: "string"
|
||||
example:
|
||||
- "127.0.0.1"
|
||||
AddressInfo:
|
||||
type: "object"
|
||||
description: "Port information"
|
||||
properties:
|
||||
ip:
|
||||
type: "string"
|
||||
example: "127.0.01"
|
||||
port:
|
||||
type: "integer"
|
||||
format: "int32"
|
||||
example: 53
|
||||
warning:
|
||||
type: "string"
|
||||
example: "Cannot bind to this port"
|
||||
AddressesInfo:
|
||||
type: "object"
|
||||
description: "AdGuard Home addresses configuration"
|
||||
properties:
|
||||
dns:
|
||||
$ref: "#/definitions/AddressInfo"
|
||||
web:
|
||||
$ref: "#/definitions/AddressInfo"
|
||||
interfaces:
|
||||
type: "object"
|
||||
description: "Network interfaces dictionary (key is the interface name)"
|
||||
additionalProperties:
|
||||
$ref: "#/definitions/NetInterface"
|
||||
InitialConfiguration:
|
||||
type: "object"
|
||||
description: "AdGuard Home initial configuration (for the first-install wizard)"
|
||||
properties:
|
||||
dns:
|
||||
$ref: "#/definitions/AddressInfo"
|
||||
web:
|
||||
$ref: "#/definitions/AddressInfo"
|
||||
username:
|
||||
type: "string"
|
||||
description: "Basic auth username"
|
||||
example: "admin"
|
||||
password:
|
||||
type: "string"
|
||||
description: "Basic auth password"
|
||||
example: "password"
|
Loading…
Reference in New Issue
Block a user