mirror of https://github.com/jetkvm/kvm.git
198 lines
5.4 KiB
Go
198 lines
5.4 KiB
Go
package nmlite
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"html/template"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/jetkvm/kvm/internal/network/types"
|
|
"github.com/rs/zerolog"
|
|
)
|
|
|
|
const (
|
|
resolvConfPath = "/etc/resolv.conf"
|
|
resolvConfFileMode = 0644
|
|
resolvConfTemplate = `# the resolv.conf file is managed by the jetkvm network manager
|
|
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
|
|
|
|
{{ if .searchList }}
|
|
search {{ join .searchList " " }} # {{ .iface }}
|
|
{{- end -}}
|
|
{{ if .domain }}
|
|
domain {{ .domain }} # {{ .iface }}
|
|
{{- end -}}
|
|
{{ range .nameservers }}
|
|
nameserver {{ printf "%s" . }} # {{ $.iface }}
|
|
{{- end }}
|
|
`
|
|
)
|
|
|
|
var (
|
|
tplFuncMap = template.FuncMap{
|
|
"join": strings.Join,
|
|
}
|
|
)
|
|
|
|
// ResolvConfManager manages the resolv.conf file
|
|
type ResolvConfManager struct {
|
|
logger *zerolog.Logger
|
|
}
|
|
|
|
// NewResolvConfManager creates a new resolv.conf manager
|
|
func NewResolvConfManager(logger *zerolog.Logger) *ResolvConfManager {
|
|
if logger == nil {
|
|
// Create a no-op logger if none provided
|
|
logger = &zerolog.Logger{}
|
|
}
|
|
|
|
return &ResolvConfManager{
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// UpdateFromLease updates resolv.conf from a DHCP lease
|
|
func (rcm *ResolvConfManager) UpdateFromLease(lease *types.DHCPLease) error {
|
|
if lease == nil {
|
|
return fmt.Errorf("lease cannot be nil")
|
|
}
|
|
|
|
rcm.logger.Info().
|
|
Str("interface", lease.InterfaceName).
|
|
Msg("updating resolv.conf from DHCP lease")
|
|
|
|
return rcm.Update(lease.InterfaceName, lease.DNS, lease.SearchList, lease.Domain)
|
|
}
|
|
|
|
// UpdateFromStaticConfig updates resolv.conf from static configuration
|
|
func (rcm *ResolvConfManager) UpdateFromStaticConfig(iface string, dns []string) error {
|
|
if len(dns) == 0 {
|
|
rcm.logger.Debug().Str("interface", iface).Msg("no DNS servers in static config")
|
|
return nil
|
|
}
|
|
|
|
// Parse DNS servers
|
|
var dnsIPs []net.IP
|
|
for _, dnsStr := range dns {
|
|
dnsIP := net.ParseIP(dnsStr)
|
|
if dnsIP == nil {
|
|
rcm.logger.Warn().Str("dns", dnsStr).Msg("invalid DNS server, skipping")
|
|
continue
|
|
}
|
|
dnsIPs = append(dnsIPs, dnsIP)
|
|
}
|
|
|
|
if len(dnsIPs) == 0 {
|
|
rcm.logger.Debug().Str("interface", iface).Msg("no valid DNS servers in static config")
|
|
return nil
|
|
}
|
|
|
|
rcm.logger.Info().
|
|
Str("interface", iface).
|
|
Interface("dns", dnsIPs).
|
|
Msg("updating resolv.conf from static config")
|
|
|
|
return rcm.Update(iface, dnsIPs, nil, "")
|
|
}
|
|
|
|
// Update updates the resolv.conf file
|
|
func (rcm *ResolvConfManager) Update(iface string, nameservers []net.IP, searchList []string, domain string) error {
|
|
rcm.logger.Debug().
|
|
Str("interface", iface).
|
|
Interface("nameservers", nameservers).
|
|
Interface("searchList", searchList).
|
|
Str("domain", domain).
|
|
Msg("updating resolv.conf")
|
|
|
|
// Generate resolv.conf content
|
|
content, err := rcm.generateResolvConf(iface, nameservers, searchList, domain)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to generate resolv.conf: %w", err)
|
|
}
|
|
|
|
// Write to file
|
|
if err := os.WriteFile(resolvConfPath, content, resolvConfFileMode); err != nil {
|
|
return fmt.Errorf("failed to write resolv.conf: %w", err)
|
|
}
|
|
|
|
rcm.logger.Info().
|
|
Str("interface", iface).
|
|
Int("nameservers", len(nameservers)).
|
|
Msg("resolv.conf updated successfully")
|
|
|
|
return nil
|
|
}
|
|
|
|
// generateResolvConf generates resolv.conf content
|
|
func (rcm *ResolvConfManager) generateResolvConf(iface string, nameservers []net.IP, searchList []string, domain string) ([]byte, error) {
|
|
tmpl, err := template.New("resolv.conf").Funcs(tplFuncMap).Parse(resolvConfTemplate)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse template: %w", err)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := tmpl.Execute(&buf, map[string]interface{}{
|
|
"iface": iface,
|
|
"nameservers": nameservers,
|
|
"searchList": searchList,
|
|
"domain": domain,
|
|
}); err != nil {
|
|
return nil, fmt.Errorf("failed to execute template: %w", err)
|
|
}
|
|
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
// Clear clears the resolv.conf file (removes all entries)
|
|
func (rcm *ResolvConfManager) Clear() error {
|
|
rcm.logger.Info().Msg("clearing resolv.conf")
|
|
|
|
content := []byte("# the resolv.conf file is managed by the jetkvm network manager\n# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN\n")
|
|
|
|
if err := os.WriteFile(resolvConfPath, content, resolvConfFileMode); err != nil {
|
|
return fmt.Errorf("failed to clear resolv.conf: %w", err)
|
|
}
|
|
|
|
rcm.logger.Info().Msg("resolv.conf cleared")
|
|
return nil
|
|
}
|
|
|
|
// GetCurrentContent returns the current content of resolv.conf
|
|
func (rcm *ResolvConfManager) GetCurrentContent() ([]byte, error) {
|
|
return os.ReadFile(resolvConfPath)
|
|
}
|
|
|
|
// Backup creates a backup of the current resolv.conf
|
|
func (rcm *ResolvConfManager) Backup() error {
|
|
content, err := rcm.GetCurrentContent()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read current resolv.conf: %w", err)
|
|
}
|
|
|
|
backupPath := resolvConfPath + ".backup"
|
|
if err := os.WriteFile(backupPath, content, resolvConfFileMode); err != nil {
|
|
return fmt.Errorf("failed to create backup: %w", err)
|
|
}
|
|
|
|
rcm.logger.Info().Str("backup", backupPath).Msg("resolv.conf backed up")
|
|
return nil
|
|
}
|
|
|
|
// Restore restores resolv.conf from backup
|
|
func (rcm *ResolvConfManager) Restore() error {
|
|
backupPath := resolvConfPath + ".backup"
|
|
content, err := os.ReadFile(backupPath)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read backup: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(resolvConfPath, content, resolvConfFileMode); err != nil {
|
|
return fmt.Errorf("failed to restore resolv.conf: %w", err)
|
|
}
|
|
|
|
rcm.logger.Info().Str("backup", backupPath).Msg("resolv.conf restored from backup")
|
|
return nil
|
|
}
|