mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-26 02:26:30 +00:00
DNS plugin maintainance (#2179)
* DNS plugin maintainance * cleanup old data * move fallback to plugin * smaller * Allow plugin to write into config persistent * Fix hosts * create issue on dns loop
This commit is contained in:
parent
db260dfbde
commit
50d36b857a
@ -48,7 +48,7 @@ class APICoreDNS(CoreSysAttributes):
|
||||
ATTR_UPDATE_AVAILABLE: self.sys_plugins.dns.need_update,
|
||||
ATTR_HOST: str(self.sys_docker.network.dns),
|
||||
ATTR_SERVERS: self.sys_plugins.dns.servers,
|
||||
ATTR_LOCALS: self.sys_host.network.dns_servers,
|
||||
ATTR_LOCALS: self.sys_plugins.dns.locals,
|
||||
}
|
||||
|
||||
@api_process
|
||||
|
@ -1,34 +0,0 @@
|
||||
.:53 {
|
||||
log
|
||||
errors
|
||||
loop
|
||||
{% if debug %}debug{% endif %}
|
||||
hosts /config/hosts {
|
||||
fallthrough
|
||||
}
|
||||
template ANY AAAA local.hass.io hassio {
|
||||
rcode NOERROR
|
||||
}
|
||||
mdns
|
||||
forward . {{ locals | join(" ") }} dns://127.0.0.1:5553 {
|
||||
except local.hass.io
|
||||
policy sequential
|
||||
health_check 5s
|
||||
}
|
||||
fallback REFUSED . dns://127.0.0.1:5553
|
||||
fallback SERVFAIL . dns://127.0.0.1:5553
|
||||
fallback NXDOMAIN . dns://127.0.0.1:5553
|
||||
cache 10
|
||||
}
|
||||
|
||||
.:5553 {
|
||||
log
|
||||
errors
|
||||
{% if debug %}debug{% endif %}
|
||||
forward . tls://1.1.1.1 tls://1.0.0.1 {
|
||||
tls_servername cloudflare-dns.com
|
||||
except local.hass.io
|
||||
health_check 10s
|
||||
}
|
||||
cache 30
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
$supervisor hassio supervisor.local.hass.io hassio.local.hass.io
|
||||
$homeassistant homeassistant homeassistant.local.hass.io home-assistant.local.hass.io
|
||||
{% for entry in entries %}
|
||||
{{- entry.ip_address }} {{ entry.names | join(" ") }}
|
||||
{% endfor %}
|
||||
|
@ -46,7 +46,7 @@ class DockerDNS(DockerInterface, CoreSysAttributes):
|
||||
detach=True,
|
||||
environment={ENV_TIME: self.sys_config.timezone},
|
||||
volumes={
|
||||
str(self.sys_config.path_extern_dns): {"bind": "/config", "mode": "ro"}
|
||||
str(self.sys_config.path_extern_dns): {"bind": "/config", "mode": "rw"}
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -292,11 +292,8 @@ class Tasks(CoreSysAttributes):
|
||||
return
|
||||
_LOGGER.warning("Watchdog found a problem with CoreDNS plugin!")
|
||||
|
||||
# Reset of failed
|
||||
if await self.sys_plugins.dns.is_failed():
|
||||
_LOGGER.error("CoreDNS plugin is in failed state, resetting configuration")
|
||||
await self.sys_plugins.dns.reset()
|
||||
await self.sys_plugins.dns.loop_detection()
|
||||
# Detect loop
|
||||
await self.sys_plugins.dns.loop_detection()
|
||||
|
||||
try:
|
||||
await self.sys_plugins.dns.start()
|
||||
|
@ -18,15 +18,16 @@ from ..const import ATTR_IMAGE, ATTR_SERVERS, ATTR_VERSION, DNS_SUFFIX, LogLevel
|
||||
from ..coresys import CoreSys, CoreSysAttributes
|
||||
from ..docker.dns import DockerDNS
|
||||
from ..docker.stats import DockerStats
|
||||
from ..exceptions import CoreDNSError, CoreDNSUpdateError, DockerError
|
||||
from ..utils.json import JsonConfig
|
||||
from ..exceptions import CoreDNSError, CoreDNSUpdateError, DockerError, JsonFileError
|
||||
from ..resolution.const import ContextType, IssueType, SuggestionType
|
||||
from ..utils.json import JsonConfig, write_json_file
|
||||
from ..validate import dns_url
|
||||
from .const import FILE_HASSIO_DNS
|
||||
from .validate import SCHEMA_DNS_CONFIG
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
|
||||
COREDNS_TMPL: Path = Path(__file__).parents[1].joinpath("data/coredns.tmpl")
|
||||
HOSTS_TMPL: Path = Path(__file__).parents[1].joinpath("data/hosts.tmpl")
|
||||
RESOLV_TMPL: Path = Path(__file__).parents[1].joinpath("data/resolv.tmpl")
|
||||
HOST_RESOLV: Path = Path("/etc/resolv.conf")
|
||||
|
||||
@ -49,22 +50,35 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
super().__init__(FILE_HASSIO_DNS, SCHEMA_DNS_CONFIG)
|
||||
self.coresys: CoreSys = coresys
|
||||
self.instance: DockerDNS = DockerDNS(coresys)
|
||||
self.coredns_template: Optional[jinja2.Template] = None
|
||||
self.resolv_template: Optional[jinja2.Template] = None
|
||||
self.hosts_template: Optional[jinja2.Template] = None
|
||||
|
||||
self._hosts: List[HostEntry] = []
|
||||
self._loop: bool = False
|
||||
|
||||
@property
|
||||
def corefile(self) -> Path:
|
||||
"""Return Path to corefile."""
|
||||
return Path(self.sys_config.path_dns, "corefile")
|
||||
|
||||
@property
|
||||
def hosts(self) -> Path:
|
||||
"""Return Path to corefile."""
|
||||
return Path(self.sys_config.path_dns, "hosts")
|
||||
|
||||
@property
|
||||
def coredns_config(self) -> Path:
|
||||
"""Return Path to coredns config file."""
|
||||
return Path(self.sys_config.path_dns, "coredns.json")
|
||||
|
||||
@property
|
||||
def locals(self) -> List[str]:
|
||||
"""Return list of local system DNS servers."""
|
||||
servers: List[str] = []
|
||||
for server in self.sys_host.network.dns_servers:
|
||||
if server in servers:
|
||||
continue
|
||||
with suppress(vol.Invalid):
|
||||
dns_url(server)
|
||||
servers.append(server)
|
||||
|
||||
return servers
|
||||
|
||||
@property
|
||||
def servers(self) -> List[str]:
|
||||
"""Return list of DNS servers."""
|
||||
@ -140,22 +154,18 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
self.save_data()
|
||||
|
||||
# Initialize CoreDNS Template
|
||||
try:
|
||||
self.coredns_template = jinja2.Template(COREDNS_TMPL.read_text())
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't read coredns.tmpl: %s", err)
|
||||
try:
|
||||
self.resolv_template = jinja2.Template(RESOLV_TMPL.read_text())
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't read resolve.tmpl: %s", err)
|
||||
try:
|
||||
self.hosts_template = jinja2.Template(HOSTS_TMPL.read_text())
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't read hosts.tmpl: %s", err)
|
||||
|
||||
# Run CoreDNS
|
||||
# If running, restart to update config/hosts
|
||||
# this get shipped with Supervisor
|
||||
with suppress(CoreDNSError):
|
||||
if await self.instance.is_running():
|
||||
await self.restart()
|
||||
else:
|
||||
if not await self.instance.is_running():
|
||||
await self.start()
|
||||
|
||||
# Update supervisor
|
||||
@ -215,7 +225,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
|
||||
async def restart(self) -> None:
|
||||
"""Restart CoreDNS plugin."""
|
||||
self._write_corefile()
|
||||
self._write_config()
|
||||
_LOGGER.info("Restarting CoreDNS plugin")
|
||||
try:
|
||||
await self.instance.restart()
|
||||
@ -225,7 +235,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Run CoreDNS."""
|
||||
self._write_corefile()
|
||||
self._write_config()
|
||||
|
||||
# Start Instance
|
||||
_LOGGER.info("Starting CoreDNS plugin")
|
||||
@ -268,47 +278,48 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
if b"plugin/loop: Loop" in log:
|
||||
_LOGGER.error("Detected a DNS loop in local Network!")
|
||||
self._loop = True
|
||||
self.sys_resolution.create_issue(
|
||||
IssueType.DNS_LOOP,
|
||||
ContextType.PLUGIN,
|
||||
reference=self.slug,
|
||||
suggestions=[SuggestionType.EXECUTE_RESET],
|
||||
)
|
||||
else:
|
||||
self._loop = False
|
||||
|
||||
def _write_corefile(self) -> None:
|
||||
def _write_config(self) -> None:
|
||||
"""Write CoreDNS config."""
|
||||
debug: bool = self.sys_config.logging == LogLevel.DEBUG
|
||||
dns_servers: List[str] = []
|
||||
local_dns: List[str] = []
|
||||
servers: List[str] = []
|
||||
dns_locals: List[str] = []
|
||||
|
||||
# Prepare DNS serverlist: Prio 1 Manual, Prio 2 Local, Prio 3 Fallback
|
||||
if not self._loop:
|
||||
local_dns = self.sys_host.network.dns_servers or ["dns://127.0.0.11"]
|
||||
servers = self.servers + local_dns
|
||||
dns_servers = self.servers
|
||||
dns_locals = self.locals
|
||||
else:
|
||||
_LOGGER.warning("Ignoring user DNS settings because of loop")
|
||||
|
||||
# Print some usefully debug data
|
||||
_LOGGER.debug(
|
||||
"config-dns = %s, local-dns = %s , backup-dns = CloudFlare DoT",
|
||||
self.servers,
|
||||
local_dns,
|
||||
)
|
||||
|
||||
# Make sure, they are valid
|
||||
for server in servers:
|
||||
try:
|
||||
dns_url(server)
|
||||
if server not in dns_servers:
|
||||
dns_servers.append(server)
|
||||
except vol.Invalid:
|
||||
_LOGGER.warning("Ignoring invalid DNS Server: %s", server)
|
||||
|
||||
# Generate config file
|
||||
data = self.coredns_template.render(
|
||||
locals=dns_servers, debug=self.sys_config.logging == LogLevel.DEBUG
|
||||
"config-dns = %s, local-dns = %s , backup-dns = CloudFlare DoT / debug: %s",
|
||||
dns_servers,
|
||||
dns_locals,
|
||||
debug,
|
||||
)
|
||||
|
||||
# Write config to plugin
|
||||
try:
|
||||
self.corefile.write_text(data)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't update corefile: %s", err)
|
||||
write_json_file(
|
||||
self.coredns_config,
|
||||
{
|
||||
"servers": dns_servers,
|
||||
"locals": dns_locals,
|
||||
"debug": debug,
|
||||
},
|
||||
)
|
||||
except JsonFileError as err:
|
||||
_LOGGER.error("Can't update coredns config: %s", err)
|
||||
raise CoreDNSError() from err
|
||||
|
||||
def _init_hosts(self) -> None:
|
||||
@ -328,12 +339,13 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
|
||||
def write_hosts(self) -> None:
|
||||
"""Write hosts from memory to file."""
|
||||
# Generate config file
|
||||
data = self.hosts_template.render(entries=self._hosts)
|
||||
|
||||
try:
|
||||
with self.hosts.open("w") as hosts:
|
||||
for entry in self._hosts:
|
||||
hosts.write(f"{entry.ip_address!s} {' '.join(entry.names)}\n")
|
||||
self.hosts.write_text(data)
|
||||
except OSError as err:
|
||||
_LOGGER.error("Can't write hosts file: %s", err)
|
||||
_LOGGER.error("Can't update hosts: %s", err)
|
||||
raise CoreDNSError() from err
|
||||
|
||||
def add_host(self, ipv4: IPv4Address, names: List[str], write: bool = True) -> None:
|
||||
@ -428,7 +440,7 @@ class CoreDNS(JsonConfig, CoreSysAttributes):
|
||||
|
||||
def _write_resolv(self, resolv_conf: Path) -> None:
|
||||
"""Update/Write resolv.conf file."""
|
||||
nameservers = [f"{self.sys_docker.network.dns!s}", "127.0.0.11"]
|
||||
nameservers = [str(self.sys_docker.network.dns), "127.0.0.11"]
|
||||
|
||||
# Read resolv config
|
||||
data = self.resolv_template.render(servers=nameservers)
|
||||
|
@ -41,6 +41,7 @@ class IssueType(str, Enum):
|
||||
UPDATE_FAILED = "update_failed"
|
||||
UPDATE_ROLLBACK = "update_rollback"
|
||||
FATAL_ERROR = "fatal_error"
|
||||
DNS_LOOP = "dns_loop"
|
||||
|
||||
|
||||
class SuggestionType(str, Enum):
|
||||
@ -50,3 +51,4 @@ class SuggestionType(str, Enum):
|
||||
CREATE_FULL_SNAPSHOT = "create_full_snapshot"
|
||||
EXECUTE_UPDATE = "execute_update"
|
||||
EXECUTE_REPAIR = "execute_repair"
|
||||
EXECUTE_RESET = "execute_reset"
|
||||
|
Loading…
x
Reference in New Issue
Block a user