mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-29 18:33:05 +00:00
Get rid of dynamic errors
This commit is contained in:
parent
25b790d025
commit
d6739386a0
10 changed files with 53 additions and 22 deletions
|
@ -30,7 +30,6 @@ linters:
|
||||||
|
|
||||||
# We should strive to enable these:
|
# We should strive to enable these:
|
||||||
- wrapcheck
|
- wrapcheck
|
||||||
- goerr113
|
|
||||||
- dupl
|
- dupl
|
||||||
- makezero
|
- makezero
|
||||||
|
|
||||||
|
|
9
app.go
9
app.go
|
@ -52,6 +52,11 @@ const (
|
||||||
Sqlite = "sqlite3"
|
Sqlite = "sqlite3"
|
||||||
updateInterval = 5000
|
updateInterval = 5000
|
||||||
HTTPReadTimeout = 30 * time.Second
|
HTTPReadTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
errUnsupportedDatabase = Error("unsupported DB")
|
||||||
|
errUnsupportedLetsEncryptChallengeType = Error(
|
||||||
|
"unknown value for Lets Encrypt challenge type",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config contains the initial Headscale configuration.
|
// Config contains the initial Headscale configuration.
|
||||||
|
@ -166,7 +171,7 @@ func NewHeadscale(cfg Config) (*Headscale, error) {
|
||||||
case Sqlite:
|
case Sqlite:
|
||||||
dbString = cfg.DBpath
|
dbString = cfg.DBpath
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unsupported DB")
|
return nil, errUnsupportedDatabase
|
||||||
}
|
}
|
||||||
|
|
||||||
app := Headscale{
|
app := Headscale{
|
||||||
|
@ -626,7 +631,7 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
|
||||||
return certManager.TLSConfig(), nil
|
return certManager.TLSConfig(), nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unknown value for TLSLetsEncryptChallengeType")
|
return nil, errUnsupportedLetsEncryptChallengeType
|
||||||
}
|
}
|
||||||
} else if h.cfg.TLSCertPath == "" {
|
} else if h.cfg.TLSCertPath == "" {
|
||||||
if !strings.HasPrefix(h.cfg.ServerURL, "http://") {
|
if !strings.HasPrefix(h.cfg.ServerURL, "http://") {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
survey "github.com/AlecAivazis/survey/v2"
|
survey "github.com/AlecAivazis/survey/v2"
|
||||||
|
"github.com/juanfont/headscale"
|
||||||
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
@ -19,6 +20,10 @@ func init() {
|
||||||
namespaceCmd.AddCommand(renameNamespaceCmd)
|
namespaceCmd.AddCommand(renameNamespaceCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
errMissingParameter = headscale.Error("missing parameters")
|
||||||
|
)
|
||||||
|
|
||||||
var namespaceCmd = &cobra.Command{
|
var namespaceCmd = &cobra.Command{
|
||||||
Use: "namespaces",
|
Use: "namespaces",
|
||||||
Short: "Manage the namespaces of Headscale",
|
Short: "Manage the namespaces of Headscale",
|
||||||
|
@ -29,7 +34,7 @@ var createNamespaceCmd = &cobra.Command{
|
||||||
Short: "Creates a new namespace",
|
Short: "Creates a new namespace",
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("missing parameters")
|
return errMissingParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -71,7 +76,7 @@ var destroyNamespaceCmd = &cobra.Command{
|
||||||
Short: "Destroys a namespace",
|
Short: "Destroys a namespace",
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("missing parameters")
|
return errMissingParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -197,7 +202,7 @@ var renameNamespaceCmd = &cobra.Command{
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
expectedArguments := 2
|
expectedArguments := 2
|
||||||
if len(args) < expectedArguments {
|
if len(args) < expectedArguments {
|
||||||
return fmt.Errorf("missing parameters")
|
return errMissingParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -178,7 +178,7 @@ var expirePreAuthKeyCmd = &cobra.Command{
|
||||||
Short: "Expire a preauthkey",
|
Short: "Expire a preauthkey",
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
Args: func(cmd *cobra.Command, args []string) error {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return fmt.Errorf("missing parameters")
|
return errMissingParameter
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -81,6 +81,7 @@ func LoadConfig(path string) error {
|
||||||
errorText += "Fatal config error: server_url must start with https:// or http://\n"
|
errorText += "Fatal config error: server_url must start with https:// or http://\n"
|
||||||
}
|
}
|
||||||
if errorText != "" {
|
if errorText != "" {
|
||||||
|
//nolint
|
||||||
return errors.New(strings.TrimSuffix(errorText, "\n"))
|
return errors.New(strings.TrimSuffix(errorText, "\n"))
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -305,6 +306,8 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
|
||||||
// to avoid races
|
// to avoid races
|
||||||
minInactivityTimeout, _ := time.ParseDuration("65s")
|
minInactivityTimeout, _ := time.ParseDuration("65s")
|
||||||
if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
|
if viper.GetDuration("ephemeral_node_inactivity_timeout") <= minInactivityTimeout {
|
||||||
|
// TODO: Find a better way to return this text
|
||||||
|
//nolint
|
||||||
err := fmt.Errorf(
|
err := fmt.Errorf(
|
||||||
"ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s",
|
"ephemeral_node_inactivity_timeout (%s) is set too low, must be more than %s",
|
||||||
viper.GetString("ephemeral_node_inactivity_timeout"),
|
viper.GetString("ephemeral_node_inactivity_timeout"),
|
||||||
|
|
7
db.go
7
db.go
|
@ -9,7 +9,10 @@ import (
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
const dbVersion = "1"
|
const (
|
||||||
|
dbVersion = "1"
|
||||||
|
errValueNotFound = Error("not found")
|
||||||
|
)
|
||||||
|
|
||||||
// KV is a key-value store in a psql table. For future use...
|
// KV is a key-value store in a psql table. For future use...
|
||||||
type KV struct {
|
type KV struct {
|
||||||
|
@ -92,7 +95,7 @@ func (h *Headscale) getValue(key string) (string, error) {
|
||||||
result.Error,
|
result.Error,
|
||||||
gorm.ErrRecordNotFound,
|
gorm.ErrRecordNotFound,
|
||||||
) {
|
) {
|
||||||
return "", errors.New("not found")
|
return "", errValueNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return row.Value, nil
|
return row.Value, nil
|
||||||
|
|
16
machine.go
16
machine.go
|
@ -20,6 +20,12 @@ import (
|
||||||
"tailscale.com/types/wgkey"
|
"tailscale.com/types/wgkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errMachineNotFound = Error("machine not found")
|
||||||
|
errMachineAlreadyRegistered = Error("machine already registered")
|
||||||
|
errMachineRouteIsNotAvailable = Error("route is not available on machine")
|
||||||
|
)
|
||||||
|
|
||||||
// Machine is a Headscale client.
|
// Machine is a Headscale client.
|
||||||
type Machine struct {
|
type Machine struct {
|
||||||
ID uint64 `gorm:"primary_key"`
|
ID uint64 `gorm:"primary_key"`
|
||||||
|
@ -248,7 +254,7 @@ func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("machine not found")
|
return nil, errMachineNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMachineByID finds a Machine by ID and returns the Machine struct.
|
// GetMachineByID finds a Machine by ID and returns the Machine struct.
|
||||||
|
@ -615,7 +621,7 @@ func (h *Headscale) RegisterMachine(
|
||||||
result.Error,
|
result.Error,
|
||||||
gorm.ErrRecordNotFound,
|
gorm.ErrRecordNotFound,
|
||||||
) {
|
) {
|
||||||
return nil, errors.New("Machine not found")
|
return nil, errMachineNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Trace().
|
log.Trace().
|
||||||
|
@ -624,7 +630,7 @@ func (h *Headscale) RegisterMachine(
|
||||||
Msg("Attempting to register machine")
|
Msg("Attempting to register machine")
|
||||||
|
|
||||||
if machine.isAlreadyRegistered() {
|
if machine.isAlreadyRegistered() {
|
||||||
err := errors.New("Machine already registered")
|
err := errMachineAlreadyRegistered
|
||||||
log.Error().
|
log.Error().
|
||||||
Caller().
|
Caller().
|
||||||
Err(err).
|
Err(err).
|
||||||
|
@ -740,9 +746,9 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
|
||||||
for _, newRoute := range newRoutes {
|
for _, newRoute := range newRoutes {
|
||||||
if !containsIPPrefix(availableRoutes, newRoute) {
|
if !containsIPPrefix(availableRoutes, newRoute) {
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"route (%s) is not available on node %s",
|
"route (%s) is not available on node %s: %w",
|
||||||
machine.Name,
|
machine.Name,
|
||||||
newRoute,
|
newRoute, errMachineRouteIsNotAvailable,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ const (
|
||||||
errPreAuthKeyNotFound = Error("AuthKey not found")
|
errPreAuthKeyNotFound = Error("AuthKey not found")
|
||||||
errPreAuthKeyExpired = Error("AuthKey expired")
|
errPreAuthKeyExpired = Error("AuthKey expired")
|
||||||
errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used")
|
errSingleUseAuthKeyHasBeenUsed = Error("AuthKey has already been used")
|
||||||
|
errNamespaceMismatch = Error("namespace mismatch")
|
||||||
)
|
)
|
||||||
|
|
||||||
// PreAuthKey describes a pre-authorization key usable in a particular namespace.
|
// PreAuthKey describes a pre-authorization key usable in a particular namespace.
|
||||||
|
@ -87,7 +88,7 @@ func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, er
|
||||||
}
|
}
|
||||||
|
|
||||||
if pak.Namespace.Name != namespace {
|
if pak.Namespace.Name != namespace {
|
||||||
return nil, errors.New("Namespace mismatch")
|
return nil, errNamespaceMismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
return pak, nil
|
return pak, nil
|
||||||
|
|
|
@ -2,12 +2,15 @@ package headscale
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errRouteIsNotAvailable = Error("route is not available")
|
||||||
|
)
|
||||||
|
|
||||||
// Deprecated: use machine function instead
|
// Deprecated: use machine function instead
|
||||||
// GetAdvertisedNodeRoutes returns the subnet routes advertised by a node (identified by
|
// GetAdvertisedNodeRoutes returns the subnet routes advertised by a node (identified by
|
||||||
// namespace and node name).
|
// namespace and node name).
|
||||||
|
@ -129,7 +132,7 @@ func (h *Headscale) EnableNodeRoute(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !available {
|
if !available {
|
||||||
return fmt.Errorf("route (%s) is not available on node %s", nodeName, routeStr)
|
return errRouteIsNotAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
routes, err := json.Marshal(enabledRoutes)
|
routes, err := json.Marshal(enabledRoutes)
|
||||||
|
|
16
utils.go
16
utils.go
|
@ -20,6 +20,12 @@ import (
|
||||||
"tailscale.com/types/wgkey"
|
"tailscale.com/types/wgkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errCannotDecryptReponse = Error("cannot decrypt response")
|
||||||
|
errResponseMissingNonce = Error("response missing nonce")
|
||||||
|
errCouldNotAllocateIP = Error("could not find any suitable IP")
|
||||||
|
)
|
||||||
|
|
||||||
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
|
// Error is used to compare errors as per https://dave.cheney.net/2016/04/07/constant-errors
|
||||||
type Error string
|
type Error string
|
||||||
|
|
||||||
|
@ -46,7 +52,7 @@ func decodeMsg(
|
||||||
}
|
}
|
||||||
// fmt.Println(string(decrypted))
|
// fmt.Println(string(decrypted))
|
||||||
if err := json.Unmarshal(decrypted, output); err != nil {
|
if err := json.Unmarshal(decrypted, output); err != nil {
|
||||||
return fmt.Errorf("response: %w", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -55,7 +61,7 @@ func decodeMsg(
|
||||||
func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) {
|
func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte, error) {
|
||||||
var nonce [24]byte
|
var nonce [24]byte
|
||||||
if len(msg) < len(nonce)+1 {
|
if len(msg) < len(nonce)+1 {
|
||||||
return nil, fmt.Errorf("response missing nonce, len=%d", len(msg))
|
return nil, errResponseMissingNonce
|
||||||
}
|
}
|
||||||
copy(nonce[:], msg)
|
copy(nonce[:], msg)
|
||||||
msg = msg[len(nonce):]
|
msg = msg[len(nonce):]
|
||||||
|
@ -63,7 +69,7 @@ func decryptMsg(msg []byte, pubKey *wgkey.Key, privKey *wgkey.Private) ([]byte,
|
||||||
pub, pri := (*[32]byte)(pubKey), (*[32]byte)(privKey)
|
pub, pri := (*[32]byte)(pubKey), (*[32]byte)(privKey)
|
||||||
decrypted, ok := box.Open(nil, msg, &nonce, pub, pri)
|
decrypted, ok := box.Open(nil, msg, &nonce, pub, pri)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("cannot decrypt response")
|
return nil, errCannotDecryptReponse
|
||||||
}
|
}
|
||||||
|
|
||||||
return decrypted, nil
|
return decrypted, nil
|
||||||
|
@ -106,7 +112,7 @@ func (h *Headscale) getAvailableIP() (*netaddr.IP, error) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !ipPrefix.Contains(ip) {
|
if !ipPrefix.Contains(ip) {
|
||||||
return nil, fmt.Errorf("could not find any suitable IP in %s", ipPrefix)
|
return nil, errCouldNotAllocateIP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some OS (including Linux) does not like when IPs ends with 0 or 255, which
|
// Some OS (including Linux) does not like when IPs ends with 0 or 255, which
|
||||||
|
@ -143,7 +149,7 @@ func (h *Headscale) getUsedIPs() ([]netaddr.IP, error) {
|
||||||
if addr != "" {
|
if addr != "" {
|
||||||
ip, err := netaddr.ParseIP(addr)
|
ip, err := netaddr.ParseIP(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse ip from database, %w", err)
|
return nil, fmt.Errorf("failed to parse ip from database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ips[index] = ip
|
ips[index] = ip
|
||||||
|
|
Loading…
Reference in a new issue