common

package
v2.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ShellTimeout      = 30 * time.Minute
	UpgradeTimeout    = 30 * time.Minute
	SystemCmdTimeout  = 60 * time.Second
	FileTimeout       = 10 * time.Minute
	FirewallTimeout   = 2 * time.Minute
	UserTimeout       = 2 * time.Minute
	UserDeleteTimeout = 5 * time.Minute
	GroupTimeout      = 30 * time.Second
	InfoTimeout       = 30 * time.Second
)

Default timeouts for each handler domain. Each handler applies its own timeout via context.WithTimeout, rather than relying on a global pool timeout.

View Source
const (
	// System commands
	Upgrade  CommandType = "upgrade"
	Restart  CommandType = "restart"
	Quit     CommandType = "quit"
	Reboot   CommandType = "reboot"
	Shutdown CommandType = "shutdown"
	Update   CommandType = "update"
	ByeBye   CommandType = "byebye"

	// Group commands
	AddGroup CommandType = "addgroup"
	DelGroup CommandType = "delgroup"

	// Info commands
	Ping   CommandType = "ping"
	Help   CommandType = "help"
	Commit CommandType = "commit"
	Sync   CommandType = "sync"

	// Shell commands
	ShellCmd CommandType = "shell"
	Exec     CommandType = "exec"

	// User commands
	AddUser CommandType = "adduser"
	DelUser CommandType = "deluser"
	ModUser CommandType = "moduser"

	// Firewall commands
	FirewallCmd           CommandType = "firewall"
	FirewallRollback      CommandType = "firewall-rollback"
	FirewallReorderChains CommandType = "firewall-reorder-chains"
	FirewallReorderRules  CommandType = "firewall-reorder-rules"

	// Firewall sub-operations (used within firewall command)
	FirewallOpBatch  string = "batch"
	FirewallOpFlush  string = "flush"
	FirewallOpDelete string = "delete"
	FirewallOpAdd    string = "add"
	FirewallOpUpdate string = "update"

	// File commands
	Upload   CommandType = "upload"
	Download CommandType = "download"

	// Terminal commands
	OpenPty    CommandType = "openpty"
	OpenFtp    CommandType = "openftp"
	ResizePty  CommandType = "resizepty"
	RefreshPty CommandType = "refreshpty"

	// Tunnel commands
	OpenTunnel  CommandType = "opentunnel"
	CloseTunnel CommandType = "closetunnel"
)

Command type constants

View Source
const (
	ShellAndOperator = "&&" // Execute next command only if previous succeeds
	ShellOrOperator  = "||" // Execute next command only if previous fails
	ShellSemicolon   = ";"  // Execute next command regardless of previous result
)

Shell operators for command parsing

View Source
const TimeoutExitCode = 124

TimeoutExitCode is returned when a handler-level timeout is exceeded.

Variables

This section is empty.

Functions

func AbsentGroupLookup added in v2.3.2

func AbsentGroupLookup(string) (*user.Group, error)

AbsentGroupLookup reports the group absent (UnknownGroupError).

func AbsentUserLookup added in v2.3.2

func AbsentUserLookup(string) (*user.User, error)

AbsentUserLookup reports the user absent (UnknownUserError).

func ClassifyGroupForCreate added in v2.3.2

func ClassifyGroupForCreate(lookup GroupLookupFunc, groupname string, wantGID uint64) (needCreate bool, code int, out string, err error)

ClassifyGroupForCreate is the shared idempotency gate for group creation, used by both standalone addgroup (group handler) and the RHEL primary-group ensure inside handleAddUser, so the two paths stay consistent (A-3). It decides, purely from a lookup, whether the caller should create the group:

  • needCreate=false, code==0 : group present with the expected gid; skip create.
  • needCreate=true, code==0 : group absent; caller should create it (then pass the result through ReconcileGroupCreate as the secondary net).
  • needCreate=false, code!=0 : gid conflict, unparseable gid, or a lookup that itself failed; caller returns (code, out, err) as-is.

func CommandsToStrings

func CommandsToStrings(commands []CommandType) []string

CommandsToStrings converts a slice of CommandType to a slice of strings

func DefaultGroupLookup added in v2.3.2

func DefaultGroupLookup(name string) (*user.Group, error)

DefaultGroupLookup is the production group lookup backed by os/user. The same CGO_ENABLED=0 caveat as DefaultUserLookup applies (reads /etc/group only).

func DefaultUserLookup added in v2.3.2

func DefaultUserLookup(name string) (*user.User, error)

DefaultUserLookup is the production user lookup backed by os/user.

NOTE: the release binary is built with CGO_ENABLED=0 (see .goreleaser.yaml), so this uses the pure-Go resolver that reads only /etc/passwd—it does NOT consult NSS (nsswitch.conf). Local users (the provisioning target) resolve correctly, but names that exist only in LDAP/SSSD/AD are INVISIBLE here. Callers must not assume this lookup is authoritative for directory-backed names; ReconcileUserCreate adds a best-effort create-time net (a re-lookup for local TOCTOU races, plus an "already exists" output fallback for the NSS-backed case the resolver cannot see).

func GetBoolArg

func GetBoolArg(args map[string]interface{}, key string, defaultValue bool) bool

GetBoolArg retrieves a boolean argument from the args map

func GetIntArg

func GetIntArg(args map[string]interface{}, key string, defaultValue int) int

GetIntArg retrieves an integer argument from the args map

func GetStringArg

func GetStringArg(args map[string]interface{}, key string, defaultValue string) string

GetStringArg retrieves a string argument from the args map

func GetStringSliceArg

func GetStringSliceArg(args map[string]interface{}, key string) []string

GetStringSliceArg retrieves a string slice argument from the args map

func GroupExists added in v2.3.2

func GroupExists(lookup GroupLookupFunc, name string) (g *user.Group, found bool, err error)

GroupExists mirrors UserExists for groups (UnknownGroupError means absent).

func GroupGIDConflictMessage added in v2.3.2

func GroupGIDConflictMessage(groupname string, existingGID, requestedGID uint64) string

GroupGIDConflictMessage mirrors UserUIDConflictMessage for groups.

func IsTimeout

func IsTimeout(ctx context.Context) bool

IsTimeout returns true if the context error indicates a deadline exceeded.

func ReconcileGroupCreate added in v2.3.2

func ReconcileGroupCreate(
	lookup GroupLookupFunc, groupname string, wantGID uint64,
	code int, out string, cmdErr error,
) (int, string, error)

ReconcileGroupCreate is the create-time secondary net for addgroup/groupadd, mirroring ReconcileUserCreate. A group has no "omitted gid" case in the provisioning payloads (GID is validated required,min=1), so the gid is always compared when the group is visible. The "already exists" tertiary net tolerates only a same-name collision (NSS-backed group); a gid-in-use collision by a differently-named group is surfaced, since the requested name stays uncreated.

func ReconcileUserCreate added in v2.3.2

func ReconcileUserCreate(
	lookup UserLookupFunc, username string, wantUID uint64, uidRequested bool,
	code int, out string, cmdErr error,
) (int, string, error)

ReconcileUserCreate is the create-time secondary net for adduser/useradd.

The primary idempotency decision is made by an up-front UserExists gate. This net covers the two cases the gate cannot: a concurrent provisioner that created the user between the gate and the create call (TOCTOU), and—because the CGO_ENABLED=0 resolver cannot see NSS/LDAP/SSSD names—a directory-backed account the gate reported absent. On a non-zero create result:

  • re-lookup finds the user with a matching (or omitted, uidRequested=false) uid -> idempotent success (0, "", nil).
  • re-lookup finds the user with a DIFFERENT uid -> surface a conflict.
  • re-lookup still cannot see the user BUT the create tool (which is NSS-aware) reported "already exists" -> tolerate as idempotent success. Identity cannot be verified here, so this is a best-effort last resort.
  • otherwise -> return the original create failure unchanged.

When code == 0 it is a no-op that returns the inputs untouched, so callers can wrap every create unconditionally without affecting the success path.

func TimeoutError

func TimeoutError(timeout time.Duration) (int, string, error)

TimeoutError returns a standard timeout response (exit 124 + message).

func UserExists added in v2.3.2

func UserExists(lookup UserLookupFunc, name string) (u *user.User, found bool, err error)

UserExists classifies a user lookup result:

  • found=true : the user is present (u is non-nil).
  • found=false, err==nil : the user is definitively absent (UnknownUserError).
  • found=false, err!=nil : the lookup itself failed (e.g. unreadable /etc/passwd); the caller must fail loud rather than blind-create.

func UserUIDConflictMessage added in v2.3.2

func UserUIDConflictMessage(username string, existingUID, requestedUID uint64) string

UserUIDConflictMessage is the single wording used whenever an existing user's uid differs from the requested one, so the up-front gate and the create-time secondary net surface identical, unambiguous drift messages.

func WithHandlerTimeout

func WithHandlerTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc)

WithHandlerTimeout wraps ctx with the given timeout and returns a context and cancel func. Use IsTimeout to check if the context deadline was exceeded, and TimeoutError to produce a standard timeout response (exit 124).

Types

type APISession

type APISession interface {
	MultipartRequest(url string, body io.Reader, contentType string, contentLength int64, timeout time.Duration) ([]byte, int, error)
	Delete(url string, rawBody interface{}, timeout time.Duration) ([]byte, int, error)
}

APISession interface for API operations (file upload, server unregister)

type BaseHandler

type BaseHandler struct {
	Executor CommandExecutor // Made public
	// contains filtered or unexported fields
}

BaseHandler provides common functionality for all handlers

func NewBaseHandler

func NewBaseHandler(name HandlerType, commands []CommandType, executor CommandExecutor) *BaseHandler

NewBaseHandler creates a new base handler

func (*BaseHandler) Commands

func (h *BaseHandler) Commands() []string

Commands returns the list of supported commands

func (*BaseHandler) Name

func (h *BaseHandler) Name() string

Name returns the handler name

func (*BaseHandler) ValidateStruct

func (h *BaseHandler) ValidateStruct(s interface{}) error

ValidateStruct validates a struct using struct tags

type CommandArgs

type CommandArgs struct {
	// Common fields
	SessionID string
	// CommandID is the UUID of the deploy shell Command that originated
	// this execution (propagated from protocol.Command.ID). Empty for
	// non-Command-driven work such as internal collectors.
	CommandID string
	URL       string
	Command   string
	Timeout   time.Duration
	AllowSh   bool

	// User management
	Username                string
	Groupname               string
	Groupnames              []string
	HomeDirectory           string
	HomeDirectoryPermission string
	PurgeHomeDirectory      bool
	UID                     uint64
	GID                     uint64
	Comment                 string
	Shell                   string
	Groups                  []uint64
	IsServiceAccount        bool

	// File operations
	Type           string
	Content        string
	Path           string
	Paths          []string
	Files          []File
	AllowOverwrite bool
	AllowUnzip     bool
	UseBlob        bool

	// Terminal operations
	Rows  uint16
	Cols  uint16
	Input string

	// Tunnel operations
	TargetPort int
	ClientType string // cli, web, editor

	// Environment
	Env map[string]string

	// ChunkCallback, if non-nil, receives streamed stdout/stderr chunks.
	// Sequencing is the caller's responsibility.
	ChunkCallback func(content string)

	// Firewall operations
	Keys         []string
	ChainName    string
	Method       string
	Chain        string
	Protocol     string
	PortStart    int
	PortEnd      int
	DPorts       []int
	ICMPType     string
	Source       string
	Destination  string
	Target       string
	Description  string
	Priority     int
	RuleType     string
	Rules        []FirewallRule
	Operation    string
	RuleID       string
	OldRuleID    string
	AssignmentID string
	ServerID     string
	ChainNames   []string

	// Backend information
	Backend string // Backend type: iptables, nftables, firewalld, ufw
	Table   string // iptables/nftables table: filter, nat, mangle, raw, security
	Family  string // IP family: ip (IPv4), ip6 (IPv6), inet, arp, bridge, netdev

	// Firewalld specific
	Zone              string // Firewalld zone (default, public, etc.)
	Service           string // Firewalld service name
	FirewalldRuleType string // Firewalld rule type: service, port, rich

	// UFW specific
	Direction string // UFW direction: in, out
	Interface string // UFW interface name
}

CommandArgs is a strongly-typed struct for all command arguments

type CommandExecutor

type CommandExecutor interface {
	// Run executes a command with the given arguments
	Run(ctx context.Context, name string, args ...string) (int, string, error)

	// RunAsUser executes a command as a specific user
	RunAsUser(ctx context.Context, username string, name string, args ...string) (int, string, error)

	// RunWithInput executes a command with stdin input
	RunWithInput(ctx context.Context, input string, name string, args ...string) (int, string, error)

	// RunWithTimeout executes a command with a timeout
	RunWithTimeout(ctx context.Context, timeout time.Duration, name string, args ...string) (int, string, error)

	// Exec executes a command with all options (user, group, env, timeout)
	Exec(ctx context.Context, args []string, username, groupname string, env map[string]string, timeout time.Duration) (int, string, error)

	// ExecWithHook is like Exec but invokes pidHook with the child's pid
	// immediately after the process is started (before it is waited on).
	// Callers use it to register the root pid with the PAM tracker so
	// sudo calls issued by the child can be attributed to the originating
	// deploy shell Command. pidHook may be nil, in which case this
	// behaves identically to Exec.
	ExecWithHook(ctx context.Context, args []string, username, groupname string, env map[string]string, timeout time.Duration, pidHook func(pid int)) (int, string, error)

	// ExecWithStreamingHook is like ExecWithHook with a chunkCallback that
	// receives streamed stdout/stderr chunks. Sequencing is the caller's
	// responsibility.
	ExecWithStreamingHook(ctx context.Context, args []string, username, groupname string, env map[string]string, timeout time.Duration, pidHook func(pid int), chunkCallback func(content string)) (int, string, error)
}

CommandExecutor defines the interface for executing system commands. This interface is defined within the handlers package to avoid circular dependencies. The concrete implementation is in the executor package.

type CommandResult

type CommandResult struct {
	ExitCode int
	Output   string
	Err      error
}

CommandResult represents the result of a mocked command execution.

type CommandType

type CommandType string

CommandType represents the type of a command

func (CommandType) String

func (c CommandType) String() string

String returns the string representation of CommandType

type ExecutedCommand

type ExecutedCommand struct {
	Name    string
	Args    []string
	User    string
	Timeout time.Duration
}

ExecutedCommand represents a command that was executed by the mock.

type File

type File struct {
	Username       string `json:"username"`
	Groupname      string `json:"groupname"`
	Type           string `json:"type"`
	Content        string `json:"content"`
	Path           string `json:"path"`
	AllowOverwrite bool   `json:"allow_overwrite"`
	AllowUnzip     bool   `json:"allow_unzip"`
	URL            string `json:"url"`
}

File represents a file transfer operation

type FirewallRule

type FirewallRule struct {
	ChainName   string `json:"chain_name"`
	Method      string `json:"method"`
	Chain       string `json:"chain"`
	Protocol    string `json:"protocol"`
	PortStart   int    `json:"port_start"`
	PortEnd     int    `json:"port_end"`
	DPorts      []int  `json:"dports"`
	ICMPType    string `json:"icmp_type"`
	Source      string `json:"source"`
	Destination string `json:"destination"`
	Target      string `json:"target"`
	Description string `json:"description"`
	Priority    int    `json:"priority"`
	RuleType    string `json:"rule_type"`
	RuleID      string `json:"rule_id"`
	OldRuleID   string `json:"old_rule_id"`
	Operation   string `json:"operation"`
}

FirewallRule represents a single firewall rule

type GroupLookupFunc added in v2.3.2

type GroupLookupFunc func(name string) (*user.Group, error)

GroupLookupFunc resolves a group by name, mirroring os/user.LookupGroup.

func ExistingGroupLookup added in v2.3.2

func ExistingGroupLookup(gid string) GroupLookupFunc

ExistingGroupLookup returns a fake reporting a group present with the given gid.

type Handler

type Handler interface {
	// Name returns the handler name (e.g., "system", "user", "firewall")
	Name() string

	// Commands returns the list of commands this handler supports
	Commands() []string

	// Execute runs the specified command with the given arguments.
	// Returns exit code, output string, and error if command fails.
	Execute(ctx context.Context, cmd string, args *CommandArgs) (exitCode int, output string, err error)

	// Validate checks if the provided arguments are valid for the command.
	// This allows pre-execution validation without running the command.
	Validate(cmd string, args *CommandArgs) error
}

Handler defines the interface for command handlers. Each handler is responsible for executing a specific set of commands.

type HandlerType

type HandlerType string

HandlerType represents the type of a handler

const (
	System       HandlerType = "system"
	Group        HandlerType = "group"
	Info         HandlerType = "info"
	Shell        HandlerType = "shell"
	User         HandlerType = "user"
	Firewall     HandlerType = "firewall"
	FileTransfer HandlerType = "file"
	Terminal     HandlerType = "terminal"
	Tunnel       HandlerType = "tunnel"
)

Handler type constants

func (HandlerType) String

func (h HandlerType) String() string

String returns the string representation of HandlerType

type MockCommandExecutor

type MockCommandExecutor struct {
	// contains filtered or unexported fields
}

MockCommandExecutor is a mock implementation of CommandExecutor for testing. It is the single source of truth for mocking in this package.

func NewMockCommandExecutor

func NewMockCommandExecutor(t *testing.T) *MockCommandExecutor

func (*MockCommandExecutor) Exec

func (m *MockCommandExecutor) Exec(ctx context.Context, args []string, username, groupname string, env map[string]string, timeout time.Duration) (int, string, error)

func (*MockCommandExecutor) ExecWithHook

func (m *MockCommandExecutor) ExecWithHook(ctx context.Context, args []string, username, groupname string, env map[string]string, timeout time.Duration, pidHook func(pid int)) (int, string, error)

ExecWithHook mirrors Exec for mock purposes and invokes pidHook (when non-nil) with a distinct, positive synthetic pid so handlers that rely on the hook contract can be exercised without spawning real processes. Using a positive pid ensures the registration code paths in the PID tracker (which ignores pid<=0) are actually exercised.

func (*MockCommandExecutor) ExecWithStreamingHook added in v2.3.1

func (m *MockCommandExecutor) ExecWithStreamingHook(ctx context.Context, args []string, username, groupname string, env map[string]string, timeout time.Duration, pidHook func(pid int), chunkCallback func(content string)) (int, string, error)

ExecWithStreamingHook mirrors ExecWithHook and emits the full output as one chunk.

func (*MockCommandExecutor) GetExecutedCommands

func (m *MockCommandExecutor) GetExecutedCommands() []ExecutedCommand

func (*MockCommandExecutor) Invoked added in v2.3.2

func (m *MockCommandExecutor) Invoked(program string) bool

Invoked reports whether a command whose program name equals program was run.

func (*MockCommandExecutor) Run

func (m *MockCommandExecutor) Run(ctx context.Context, name string, args ...string) (int, string, error)

func (*MockCommandExecutor) RunAsUser

func (m *MockCommandExecutor) RunAsUser(ctx context.Context, username string, name string, args ...string) (int, string, error)

func (*MockCommandExecutor) RunWithInput

func (m *MockCommandExecutor) RunWithInput(ctx context.Context, input string, name string, args ...string) (int, string, error)

func (*MockCommandExecutor) RunWithTimeout

func (m *MockCommandExecutor) RunWithTimeout(ctx context.Context, timeout time.Duration, name string, args ...string) (int, string, error)

func (*MockCommandExecutor) SetResult

func (m *MockCommandExecutor) SetResult(command string, exitCode int, output string, err error)

type SystemInfoManager

type SystemInfoManager interface {
	CommitSystemInfo()
	SyncSystemInfo(keys []string)
}

SystemInfoManager interface for system info operations

type UserLookupFunc added in v2.3.2

type UserLookupFunc func(name string) (*user.User, error)

UserLookupFunc resolves a user by name. It mirrors os/user.Lookup so the production default can be assigned directly, while tests inject a fake.

func ExistingUserLookup added in v2.3.2

func ExistingUserLookup(uid string) UserLookupFunc

ExistingUserLookup returns a fake reporting a user present with the given uid.

type VersionResolver

type VersionResolver interface {
	GetLatestVersion() string
	GetPamVersion() string
	InvalidatePamCache()
}

VersionResolver provides version information for upgrade decisions.

type WSClient

type WSClient interface {
	Restart()
	ShutDown()
	RestartCollector()
}

WSClient interface for WebSocket client operations

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL