2026-04-29
This commit is contained in:
56
internal/pkg/config/api.go
Normal file
56
internal/pkg/config/api.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/creasty/defaults"
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/util"
|
||||
)
|
||||
|
||||
type IPNet net.IPNet
|
||||
|
||||
func (n IPNet) MarshalText() ([]byte, error) {
|
||||
ipnet := n.IPNet()
|
||||
return []byte((&ipnet).String()), nil
|
||||
}
|
||||
|
||||
func (n *IPNet) UnmarshalText(text []byte) error {
|
||||
_, network, err := net.ParseCIDR(string(text))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*n = IPNet(*network)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n IPNet) IPNet() net.IPNet {
|
||||
return net.IPNet(n)
|
||||
}
|
||||
|
||||
type APIConf struct {
|
||||
EnabledP *bool `yaml:"enabled,omitempty" default:"false"`
|
||||
TLSEnabledP *bool `yaml:"tls,omitempty"`
|
||||
AllowedNets []IPNet `yaml:"allowed subnets,omitempty" default:"[\"127.0.0.0/8\", \"::1/128\"]"`
|
||||
}
|
||||
|
||||
func (conf *APIConf) AllowedIPNets() (allowedIPNets []net.IPNet) {
|
||||
for _, allowedIPNet := range conf.AllowedNets {
|
||||
allowedIPNets = append(allowedIPNets, allowedIPNet.IPNet())
|
||||
}
|
||||
return allowedIPNets
|
||||
}
|
||||
|
||||
func (conf *APIConf) Unmarshal(unmarshal func(interface{}) error) error {
|
||||
if err := defaults.Set(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (conf APIConf) Enabled() bool {
|
||||
return util.BoolP(conf.EnabledP)
|
||||
}
|
||||
|
||||
func (conf APIConf) TLSEnabled() bool {
|
||||
return util.BoolP(conf.TLSEnabledP)
|
||||
}
|
||||
97
internal/pkg/config/buildconfig.go
Normal file
97
internal/pkg/config/buildconfig.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path"
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/util"
|
||||
)
|
||||
|
||||
var ConfigFile = "/etc/sunhpc/sunhpc.conf"
|
||||
|
||||
type BuildConfig struct {
|
||||
Bindir string `yaml:"bindir,omitempty" default:"@BINDIR@"`
|
||||
Sysconfdir string `yaml:"sysconfdir,omitempty" default:"@SYSCONFDIR@"`
|
||||
Localstatedir string `yaml:"localstatedir,omitempty" default:"@LOCALSTATEDIR@"`
|
||||
Cachedir string `yaml:"cachedir,omitempty" default:"@CACHEDIR@"`
|
||||
Ipxesource string `yaml:"ipxesource,omitempty" default:"@IPXESOURCE@"`
|
||||
Srvdir string `yaml:"srvdir,omitempty" default:"@SRVDIR@"`
|
||||
Firewallddir string `yaml:"firewallddir,omitempty" default:"@FIREWALLDDIR@"`
|
||||
Systemddir string `yaml:"systemddir,omitempty" default:"@SYSTEMDDIR@"`
|
||||
Datadir string `yaml:"datadir,omitempty" default:"@DATADIR@"`
|
||||
WWOverlaydir string `yaml:"wwoverlaydir,omitempty" default:"@WWOVERLAYDIR@"`
|
||||
WWChrootdir string `yaml:"wwchrootdir,omitempty" default:"@WWCHROOTDIR@"`
|
||||
WWProvisiondir string `yaml:"wwprovisiondir,omitempty" default:"@WWPROVISIONDIR@"`
|
||||
WWClientdir string `yaml:"wwclientdir,omitempty" default:"@WWCLIENTDIR@"`
|
||||
}
|
||||
|
||||
const Version = "1.0.0"
|
||||
const Release = "1"
|
||||
|
||||
type TFTPConf struct {
|
||||
EnabledP *bool `yaml:"enabled" default:"true"`
|
||||
TftpRoot string `yaml:"tftproot,omitempty" default:"@TFTPDIR@"`
|
||||
SystemdName string `yaml:"systemd name,omitempty" default:"tftp"`
|
||||
|
||||
IpxeBinaries map[string]string `yaml:"ipxe,omitempty" default:"{\"00:09\": \"ipxe-snponly-x86_64.efi\",\"00:00\": \"undionly.kpxe\",\"00:0B\": \"arm64-efi/snponly.efi\",\"00:07\": \"ipxe-snponly-x86_64.efi\"}"`
|
||||
}
|
||||
|
||||
func (conf TFTPConf) Enabled() bool {
|
||||
return util.BoolP(conf.EnabledP)
|
||||
}
|
||||
|
||||
// SunhpcConf adds additional Sunhpc-specific configuration to
|
||||
// BaseConf.
|
||||
type SunhpcConf struct {
|
||||
Port int `yaml:"port,omitempty" default:"9873"`
|
||||
TLSPort int `yaml:"tls port,omitempty" default:"9874"`
|
||||
SecureP *bool `yaml:"secure,omitempty" default:"true"`
|
||||
TLSEnabledP *bool `yaml:"tls,omitempty"`
|
||||
UpdateInterval int `yaml:"update interval,omitempty" default:"60"`
|
||||
AutobuildOverlaysP *bool `yaml:"autobuild overlays,omitempty" default:"true"`
|
||||
EnableHostOverlayP *bool `yaml:"host overlay,omitempty" default:"true"`
|
||||
GrubBootP *bool `yaml:"grubboot,omitempty" default:"false"`
|
||||
SystemdName string `yaml:"systemd name,omitempty"`
|
||||
}
|
||||
|
||||
func (conf SunhpcConf) Secure() bool {
|
||||
return util.BoolP(conf.SecureP)
|
||||
}
|
||||
|
||||
func (conf SunhpcConf) TLSEnabled() bool {
|
||||
return util.BoolP(conf.TLSEnabledP)
|
||||
}
|
||||
|
||||
func (conf SunhpcConf) AutobuildOverlays() bool {
|
||||
return util.BoolP(conf.AutobuildOverlaysP)
|
||||
}
|
||||
|
||||
func (conf SunhpcConf) EnableHostOverlay() bool {
|
||||
return util.BoolP(conf.EnableHostOverlayP)
|
||||
}
|
||||
|
||||
func (conf SunhpcConf) GrubBoot() bool {
|
||||
return util.BoolP(conf.GrubBootP)
|
||||
}
|
||||
|
||||
func (paths BuildConfig) NodesConf() string {
|
||||
return path.Join(paths.Sysconfdir, "sunhpc", "nodes.conf")
|
||||
}
|
||||
|
||||
func (paths BuildConfig) AuthenticationConf() string {
|
||||
return path.Join(paths.Sysconfdir, "sunhpc", "auth.conf")
|
||||
}
|
||||
|
||||
func (paths BuildConfig) OciBlobCachedir() string {
|
||||
return path.Join(paths.Cachedir, "sunhpc")
|
||||
}
|
||||
|
||||
func (paths BuildConfig) SiteOverlaydir() string {
|
||||
return paths.WWOverlaydir
|
||||
}
|
||||
|
||||
func (paths BuildConfig) DistributionOverlaydir() string {
|
||||
return path.Join(paths.Datadir, "sunhpc", "overlays")
|
||||
}
|
||||
|
||||
func (paths BuildConfig) OverlayProvisiondir() string {
|
||||
return path.Join(paths.WWProvisiondir, "overlays")
|
||||
}
|
||||
5
internal/pkg/config/client.go
Normal file
5
internal/pkg/config/client.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
type SPClientConf struct {
|
||||
Port uint16 `yaml:"port,omitempty" default:"0"`
|
||||
}
|
||||
21
internal/pkg/config/dhcp.go
Normal file
21
internal/pkg/config/dhcp.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/util"
|
||||
)
|
||||
|
||||
// DHCPConf represents the configuration for the DHCP service that
|
||||
// Sunhpc will configure.
|
||||
type DHCPConf struct {
|
||||
EnabledP *bool `yaml:"enabled,omitempty" default:"true"`
|
||||
Template string `yaml:"template,omitempty" default:"default"`
|
||||
RangeStart string `yaml:"range start,omitempty"`
|
||||
RangeEnd string `yaml:"range end,omitempty"`
|
||||
Range6Start string `yaml:"range6 start,omitempty"`
|
||||
Range6End string `yaml:"range6 end,omitempty"`
|
||||
SystemdName string `yaml:"systemd name,omitempty" default:"dhcpd"`
|
||||
}
|
||||
|
||||
func (conf DHCPConf) Enabled() bool {
|
||||
return util.BoolP(conf.EnabledP)
|
||||
}
|
||||
21
internal/pkg/config/dnsmasq.go
Normal file
21
internal/pkg/config/dnsmasq.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/util"
|
||||
)
|
||||
|
||||
// DNSmasqConf represents the configuration for the DNSmasq service that
|
||||
// Sunhpc will configure.
|
||||
type DNSMASQConf struct {
|
||||
EnabledP *bool `yaml:"enabled,omitempty" default:"true"`
|
||||
Template string `yaml:"template,omitempty" default:"default"`
|
||||
RangeStart string `yaml:"range start,omitempty"`
|
||||
RangeEnd string `yaml:"range end,omitempty"`
|
||||
Range6Start string `yaml:"range6 start,omitempty"`
|
||||
Range6End string `yaml:"range6 end,omitempty"`
|
||||
SystemdName string `yaml:"systemd name,omitempty" default:"dhcpd"`
|
||||
}
|
||||
|
||||
func (conf DNSMASQConf) Enabled() bool {
|
||||
return util.BoolP(conf.EnabledP)
|
||||
}
|
||||
23
internal/pkg/config/mounts.go
Normal file
23
internal/pkg/config/mounts.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/util"
|
||||
)
|
||||
|
||||
// A MountEntry represents a bind mount that is applied to an image
|
||||
// during exec and shell.
|
||||
type MountEntry struct {
|
||||
Source string `yaml:"source"`
|
||||
Dest string `yaml:"dest,omitempty"`
|
||||
ReadOnlyP *bool `yaml:"readonly,omitempty"`
|
||||
Options string `yaml:"options,omitempty"` // ignored at the moment
|
||||
CopyP *bool `yaml:"copy,omitempty"` // temporarily copy the file into the image
|
||||
}
|
||||
|
||||
func (mount MountEntry) ReadOnly() bool {
|
||||
return util.BoolP(mount.ReadOnlyP)
|
||||
}
|
||||
|
||||
func (mount MountEntry) Copy() bool {
|
||||
return util.BoolP(mount.CopyP)
|
||||
}
|
||||
34
internal/pkg/config/nfs.go
Normal file
34
internal/pkg/config/nfs.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/creasty/defaults"
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/util"
|
||||
)
|
||||
|
||||
// NFSConf represents the NFS configuration that will be used by
|
||||
// Sunhpc to generate exports on the server and mounts on compute
|
||||
// nodes.
|
||||
type NFSConf struct {
|
||||
EnabledP *bool `yaml:"enabled,omitempty" default:"true"`
|
||||
ExportsExtended []*NFSExportConf `yaml:"export paths,omitempty" default:"[]"`
|
||||
SystemdName string `yaml:"systemd name,omitempty" default:"nfsd"`
|
||||
}
|
||||
|
||||
func (conf NFSConf) Enabled() bool {
|
||||
return util.BoolP(conf.EnabledP)
|
||||
}
|
||||
|
||||
// An NFSExportConf reprents a single NFS export / mount.
|
||||
type NFSExportConf struct {
|
||||
Path string `yaml:"path" default:"/dev/null"`
|
||||
ExportOptions string `yaml:"export options,omitempty" default:"rw,sync,no_subtree_check"`
|
||||
}
|
||||
|
||||
// Implements the Unmarshal interface for NFSConf to set default
|
||||
// values.
|
||||
func (conf *NFSConf) Unmarshal(unmarshal func(interface{}) error) error {
|
||||
if err := defaults.Set(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
262
internal/pkg/config/root.go
Normal file
262
internal/pkg/config/root.go
Normal file
@@ -0,0 +1,262 @@
|
||||
// Package config reads, parses, and represents the sunhpc.conf
|
||||
// config file.
|
||||
//
|
||||
// sunhpc.conf is a yaml-formatted configuration file that includes
|
||||
// configuration for the sunhpc daemon and commands, as well as the
|
||||
// DHCP, TFTP and NFS services that sunhpc manages.
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/creasty/defaults"
|
||||
"gitea.sunhpc.com/kelvin/sunhpc/internal/pkg/splog"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var cachedConf SunhpcYaml
|
||||
|
||||
// SunhpcYaml is the main Sunhpc configuration structure. It stores
|
||||
// some information about the Sunhpc server locally, and has
|
||||
// [SunhpcConf], [DHCPConf], [TFTPConf], and [NFSConf] sub-sections.
|
||||
type SunhpcYaml struct {
|
||||
Comment string `yaml:"comment,omitempty"`
|
||||
Ipaddr string `yaml:"ipaddr,omitempty"`
|
||||
Netmask string `yaml:"netmask,omitempty"`
|
||||
Network string `yaml:"network,omitempty"`
|
||||
Fqdn string `yaml:"fqdn,omitempty"`
|
||||
Ipaddr6 string `yaml:"ipaddr6,omitempty"`
|
||||
PrefixLen6 string `yaml:"prefixlen6,omitempty"`
|
||||
Sunhpc *SunhpcConf `yaml:"sunhpc,omitempty"`
|
||||
API *APIConf `yaml:"api,omitempty"`
|
||||
DNSMASQ *DNSMASQConf `yaml:"dhcp,omitempty"`
|
||||
DHCP *DHCPConf `yaml:"dhcp,omitempty"`
|
||||
TFTP *TFTPConf `yaml:"tftp,omitempty"`
|
||||
NFS *NFSConf `yaml:"nfs,omitempty"`
|
||||
SSH *SSHConf `yaml:"ssh,omitempty"`
|
||||
MountsImage []*MountEntry `yaml:"image mounts,omitempty" default:"[{\"source\": \"/etc/resolv.conf\", \"dest\": \"/etc/resolv.conf\"}]"`
|
||||
Paths *BuildConfig `yaml:"paths,omitempty"`
|
||||
//Client *SPClientConf `yaml:"client,omitempty"`
|
||||
|
||||
sunhpcconf string
|
||||
autodetected bool
|
||||
}
|
||||
|
||||
// New caches and returns a new [SunhpcYaml] initialized with empty
|
||||
// values, clearing replacing any previously cached value.
|
||||
func New() *SunhpcYaml {
|
||||
cachedConf = SunhpcYaml{}
|
||||
cachedConf.sunhpcconf = ""
|
||||
|
||||
cachedConf.Sunhpc = new(SunhpcConf)
|
||||
cachedConf.DNSMASQ = new(DNSMASQConf)
|
||||
cachedConf.Paths = new(BuildConfig)
|
||||
cachedConf.DHCP = new(DHCPConf)
|
||||
cachedConf.TFTP = new(TFTPConf)
|
||||
cachedConf.NFS = new(NFSConf)
|
||||
cachedConf.SSH = new(SSHConf)
|
||||
cachedConf.API = new(APIConf)
|
||||
if err := defaults.Set(&cachedConf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &cachedConf
|
||||
}
|
||||
|
||||
// Get returns a previously cached [SunhpcYaml] if it exists, or returns
|
||||
// a new SunhpcYaml.
|
||||
func Get() *SunhpcYaml {
|
||||
// NOTE: This function can be called before any log level is set
|
||||
// so using splog.Verbose or splog.Debug won't work
|
||||
if reflect.ValueOf(cachedConf).IsZero() {
|
||||
cachedConf = *New()
|
||||
}
|
||||
return &cachedConf
|
||||
}
|
||||
|
||||
// Read populates [SunhpcYaml] with the values from a configuration
|
||||
// file.
|
||||
func (conf *SunhpcYaml) Read(confFileName string, autodetect bool) error {
|
||||
splog.Debug("Reading sunhpc.conf from: %s", confFileName)
|
||||
conf.sunhpcconf = confFileName
|
||||
if data, err := os.ReadFile(confFileName); err != nil {
|
||||
return err
|
||||
} else if err := conf.Parse(data, autodetect); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Parse populates [SunhpcYaml] with the values from a yaml document.
|
||||
func (conf *SunhpcYaml) Parse(data []byte, autodetect bool) error {
|
||||
// ipxe binaries are merged not overwritten, store defaults separate
|
||||
defIpxe := make(map[string]string)
|
||||
for k, v := range conf.TFTP.IpxeBinaries {
|
||||
defIpxe[k] = v
|
||||
delete(conf.TFTP.IpxeBinaries, k)
|
||||
}
|
||||
if err := yaml.Unmarshal(data, &conf); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(conf.TFTP.IpxeBinaries) == 0 {
|
||||
conf.TFTP.IpxeBinaries = defIpxe
|
||||
}
|
||||
|
||||
if ip, network, err := net.ParseCIDR(conf.Ipaddr); err == nil {
|
||||
conf.Ipaddr = ip.String()
|
||||
if conf.Network == "" {
|
||||
conf.Network = network.IP.String()
|
||||
}
|
||||
if conf.Netmask == "" {
|
||||
conf.Netmask = net.IP(network.Mask).String()
|
||||
}
|
||||
}
|
||||
|
||||
if autodetect {
|
||||
if conf.Ipaddr == "" {
|
||||
if ip := GetOutboundIP(); ip != nil {
|
||||
conf.Ipaddr = ip.String()
|
||||
conf.autodetected = true
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Netmask == "" {
|
||||
if ip := net.ParseIP(conf.Ipaddr); ip != nil {
|
||||
if network, err := GetIPNetForIP(ip); err == nil {
|
||||
conf.Netmask = net.IP(network.Mask).String()
|
||||
conf.autodetected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Network == "" {
|
||||
if ip := net.ParseIP(conf.Ipaddr); ip != nil {
|
||||
if mask := net.IPMask(net.ParseIP(conf.Netmask)); mask != nil {
|
||||
conf.Network = ip.Mask(mask).String()
|
||||
conf.autodetected = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(conf.Ipaddr6); ip != nil {
|
||||
if prefix, err := strconv.Atoi(conf.PrefixLen6); err == nil {
|
||||
conf.Ipaddr6 = ip.String()
|
||||
conf.PrefixLen6 = strconv.Itoa(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
if ip, net, err := net.ParseCIDR(conf.Ipaddr6); err == nil {
|
||||
conf.Ipaddr6 = ip.String()
|
||||
prefixLen6, _ := net.Mask.Size()
|
||||
conf.PrefixLen6 = strconv.Itoa(prefixLen6)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) Network6() string {
|
||||
cidr := fmt.Sprintf("%s/%s", config.Ipaddr6, config.PrefixLen6)
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return ipnet.IP.String()
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) NetworkCIDR() string {
|
||||
if config.Network == "" || config.Netmask == "" {
|
||||
return ""
|
||||
}
|
||||
cidr := net.IPNet{
|
||||
IP: net.ParseIP(config.Network),
|
||||
Mask: net.IPMask(net.ParseIP(config.Netmask)),
|
||||
}
|
||||
if cidr.IP == nil || cidr.Mask == nil {
|
||||
return ""
|
||||
}
|
||||
return cidr.String()
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) NetworkCIDR6() string {
|
||||
cidr := fmt.Sprintf("%s/%s", config.Ipaddr6, config.PrefixLen6)
|
||||
_, ipnet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return ipnet.String()
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) IpCIDR() string {
|
||||
if config.Ipaddr == "" || config.Netmask == "" {
|
||||
return ""
|
||||
}
|
||||
cidr := net.IPNet{
|
||||
IP: net.ParseIP(config.Ipaddr),
|
||||
Mask: net.IPMask(net.ParseIP(config.Netmask)),
|
||||
}
|
||||
if cidr.IP == nil || cidr.Mask == nil {
|
||||
return ""
|
||||
}
|
||||
return cidr.String()
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) IpCIDR6() string {
|
||||
cidr := fmt.Sprintf("%s/%s", config.Ipaddr6, config.PrefixLen6)
|
||||
ip, _, err := net.ParseCIDR(cidr)
|
||||
if err != nil || ip == nil || ip.To4() != nil {
|
||||
return ""
|
||||
}
|
||||
return cidr
|
||||
}
|
||||
|
||||
// InitializedFromFile returns true if [SunhpcYaml] memory was read from
|
||||
// a file, or false otherwise.
|
||||
func (conf *SunhpcYaml) InitializedFromFile() bool {
|
||||
return conf.sunhpcconf != ""
|
||||
}
|
||||
|
||||
func (conf *SunhpcYaml) GetSunhpcConf() string {
|
||||
return conf.sunhpcconf
|
||||
}
|
||||
|
||||
func (conf *SunhpcYaml) Autodetected() bool {
|
||||
return conf.autodetected
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) Dump() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
yamlEncoder := yaml.NewEncoder(&buf)
|
||||
yamlEncoder.SetIndent(2)
|
||||
err := yamlEncoder.Encode(config)
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
|
||||
func (config *SunhpcYaml) PersistToFile(configFile string) (err error) {
|
||||
out, dumpErr := config.Dump()
|
||||
if dumpErr != nil {
|
||||
splog.Error("%s", dumpErr)
|
||||
return dumpErr
|
||||
}
|
||||
file, err := os.OpenFile(configFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o644)
|
||||
if err != nil {
|
||||
splog.Error("%s", err)
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if cerr := file.Close(); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
}()
|
||||
_, err = file.WriteString(string(out))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
splog.Debug("persisted: %s", configFile)
|
||||
return nil
|
||||
}
|
||||
5
internal/pkg/config/ssh.go
Normal file
5
internal/pkg/config/ssh.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package config
|
||||
|
||||
type SSHConf struct {
|
||||
KeyTypes []string `yaml:"key types,omitempty" default:"[\"ed25519\",\"ecdsa\",\"rsa\",\"dsa\"]"`
|
||||
}
|
||||
58
internal/pkg/config/util.go
Normal file
58
internal/pkg/config/util.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
func GetOutboundIP() net.IP {
|
||||
conn, err := net.Dial("udp", "192.0.2.1:80")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer func() { _ = conn.Close() }()
|
||||
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
||||
return localAddr.IP
|
||||
}
|
||||
|
||||
func GetIPNetForIP(ip net.IP) (*net.IPNet, error) {
|
||||
interfaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get network interfaces: %v", err)
|
||||
}
|
||||
|
||||
for _, iface := range interfaces {
|
||||
// skip interfaces that are down or not relevant
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
addrs, err := iface.Addrs()
|
||||
if err != nil {
|
||||
continue // try next interface
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
// We expect addr to be of type *net.IPNet.
|
||||
var ipNet *net.IPNet
|
||||
switch v := addr.(type) {
|
||||
case *net.IPNet:
|
||||
ipNet = v
|
||||
case *net.IPAddr:
|
||||
// Wrap net.IPAddr in an IPNet with its default mask.
|
||||
ipNet = &net.IPNet{IP: v.IP, Mask: v.IP.DefaultMask()}
|
||||
}
|
||||
|
||||
if ipNet == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if the IP matches (for IPv4, Equal works well)
|
||||
if ipNet.IP.Equal(ip) {
|
||||
return ipNet, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not find IPNet for IP %v", ip)
|
||||
}
|
||||
Reference in New Issue
Block a user