You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
81 lines
2.4 KiB
81 lines
2.4 KiB
|
2 months ago
|
// Package managementaccess owns the narrow runtime boundary used to make
|
||
|
|
// staged management-service settings real on the target.
|
||
|
|
package managementaccess
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"tera-clone/services/tera-api/internal/domain"
|
||
|
|
)
|
||
|
|
|
||
|
|
const DefaultSocketPath = "/run/tera-accessd/control.sock"
|
||
|
|
|
||
|
|
type Controller interface {
|
||
|
|
Apply(context.Context, domain.ManagementAccessSettings) error
|
||
|
|
}
|
||
|
|
|
||
|
|
type NoopController struct{}
|
||
|
|
|
||
|
|
func (NoopController) Apply(context.Context, domain.ManagementAccessSettings) error { return nil }
|
||
|
|
|
||
|
|
type UnavailableError struct{ Reason string }
|
||
|
|
|
||
|
|
func (e *UnavailableError) Error() string {
|
||
|
|
reason := strings.TrimSpace(e.Reason)
|
||
|
|
if reason == "" {
|
||
|
|
reason = "tera-accessd is unavailable"
|
||
|
|
}
|
||
|
|
return reason
|
||
|
|
}
|
||
|
|
|
||
|
|
func IsUnavailable(err error) bool {
|
||
|
|
var target *UnavailableError
|
||
|
|
return errors.As(err, &target)
|
||
|
|
}
|
||
|
|
|
||
|
|
func Validate(settings domain.ManagementAccessSettings) error {
|
||
|
|
if settings.IdleTimeoutMinutes < 1 || settings.IdleTimeoutMinutes > 120 {
|
||
|
|
return errors.New("idle_timeout_minutes must be between 1 and 120")
|
||
|
|
}
|
||
|
|
if settings.SSHEnabled || settings.TelnetEnabled {
|
||
|
|
return errors.New("management access SSH and Telnet are excluded; use per-channel SSH or Telnet access")
|
||
|
|
}
|
||
|
|
if settings.LoginDetectionMinutes < 0 || settings.LoginDetectionMinutes > 1440 {
|
||
|
|
return errors.New("login_detection_minutes must be between 0 and 1440")
|
||
|
|
}
|
||
|
|
if settings.LoginSecurityHitCount < 0 || settings.LoginSecurityHitCount > 100 {
|
||
|
|
return errors.New("login_security_hit_count must be between 0 and 100")
|
||
|
|
}
|
||
|
|
if !settings.HTTPSEnabled && !settings.SerialConsoleEnabled {
|
||
|
|
return errors.New("management access must keep HTTPS or serial console enabled")
|
||
|
|
}
|
||
|
|
ports := []struct {
|
||
|
|
name string
|
||
|
|
port int
|
||
|
|
enabled bool
|
||
|
|
}{
|
||
|
|
{"http", settings.HTTPPort, settings.HTTPEnabled},
|
||
|
|
{"https", settings.HTTPSPort, settings.HTTPSEnabled},
|
||
|
|
}
|
||
|
|
active := make(map[int]string)
|
||
|
|
for _, item := range ports {
|
||
|
|
if item.port < 1 || item.port > 65535 {
|
||
|
|
return fmt.Errorf("management access %s port must be between 1 and 65535", item.name)
|
||
|
|
}
|
||
|
|
if !item.enabled {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if item.port == 8080 {
|
||
|
|
return fmt.Errorf("management access %s port 8080 is reserved for the recovery-safe web endpoint", item.name)
|
||
|
|
}
|
||
|
|
if previous, exists := active[item.port]; exists {
|
||
|
|
return fmt.Errorf("management access services %s and %s cannot share port %d", previous, item.name, item.port)
|
||
|
|
}
|
||
|
|
active[item.port] = item.name
|
||
|
|
}
|
||
|
|
return nil
|
||
|
|
}
|