// Package proto describes the wire messages shared by the server and the agent. // // All agent<->server traffic is plain JSON over HTTP(S) long-polling so it // survives NAT and restrictive egress firewalls: the agent only ever makes // outbound requests. package proto import "encoding/json" // Control poll message types (server -> agent, returned from /api/agent/poll). const ( // CtrlIdle is returned when the long-poll times out with nothing to do. CtrlIdle = "idle" // CtrlOpenTerminal tells the agent to spawn a PTY for the session id. CtrlOpenTerminal = "open_terminal" // CtrlOpenForward tells the agent to dial 127.0.0.1:Port and bridge it to // the session id (reverse TCP port-forward). CtrlOpenForward = "open_forward" // CtrlTask tells the agent to fetch and run the one-shot task TaskID. CtrlTask = "task" ) // EnrollRequest is sent once by a freshly installed agent. type EnrollRequest struct { Name string `json:"name"` Hostname string `json:"hostname"` OS string `json:"os"` Arch string `json:"arch"` Version string `json:"version"` User string `json:"user,omitempty"` // OS user the agent runs as } // EnrollResponse carries the identity the agent persists and uses from then on. type EnrollResponse struct { AgentID string `json:"agent_id"` Token string `json:"token"` } // ControlMessage is the body returned by the control long-poll. type ControlMessage struct { Type string `json:"type"` SessionID string `json:"session_id,omitempty"` Port int `json:"port,omitempty"` // CtrlOpenForward: client-side port to dial TaskID string `json:"task_id,omitempty"` // CtrlTask User string `json:"user,omitempty"` // CtrlOpenTerminal: OS user for the shell ("" = the agent's own) } // Terminal / stream frame types on the session "in" channel (server -> agent). // Reused for both PTY terminals and raw TCP port-forward streams. const ( FrameStdin = "stdin" // base64 bytes for the PTY / socket FrameResize = "resize" // PTY window size (terminal only) FrameClose = "close" // tear the stream down ) // InFrame is one element of the stream downstream batch (server -> agent). type InFrame struct { Type string `json:"type"` Data string `json:"data,omitempty"` // base64 bytes Cols uint16 `json:"cols,omitempty"` Rows uint16 `json:"rows,omitempty"` } // InBatch is the JSON body returned by GET /api/agent/term/{sid}/in. type InBatch struct { Frames []InFrame `json:"frames"` } // OutMessage is POSTed by the agent to /api/agent/term/{sid}/out. type OutMessage struct { Data string `json:"data,omitempty"` // base64 bytes Exit bool `json:"exit,omitempty"` } // --- one-shot tasks (file manager, native in the agent) --- const ( TaskFSList = "fs.list" // list a directory TaskFSRead = "fs.read" // read file content (for view/edit) TaskFSWrite = "fs.write" // write/overwrite file (edit save, create, upload) TaskFSMkdir = "fs.mkdir" // create directory TaskFSDelete = "fs.delete" // delete a single file or empty dir TaskFSDelTree = "fs.deltree" // recursively delete a directory TaskFSUnzip = "fs.unzip" // unzip an archive (needs `unzip` on the client) TaskFSZip = "fs.zip" // zip selected paths (native archive/zip) TaskFSMove = "fs.move" // move/rename TaskFSDownload = "fs.download" // read a whole file (larger cap) for browser download TaskSysUpdate = "sys.update" // download the latest agent binary and restart TaskSysUsers = "sys.users" // report the agent's user and the login-capable users TaskSysUserAdd = "sys.useradd" // create a new OS user TaskSysStat = "sys.stat" // CPU load, RAM and disk usage right now TaskSysSpeed = "sys.speed" // measure the link to the server (ping, down, up) ) // FSReadCap bounds how many bytes TaskFSRead returns (5 MiB). The panel lets a // file be edited only when it fits, so that a save never cuts it short. const FSReadCap = 5 * 1024 * 1024 // TaskEnvelope is what the agent fetches for a task id. type TaskEnvelope struct { Type string `json:"type"` Args json.RawMessage `json:"args"` } // TaskResult is POSTed back by the agent. type TaskResult struct { OK bool `json:"ok"` Error string `json:"error,omitempty"` Data json.RawMessage `json:"data,omitempty"` } // fs task argument shapes. type FSPathArgs struct { Path string `json:"path"` } type FSWriteArgs struct { Path string `json:"path"` Data string `json:"data"` // base64 content } type FSUnzipArgs struct { Path string `json:"path"` Dest string `json:"dest"` } type FSZipArgs struct { Paths []string `json:"paths"` Dest string `json:"dest"` } type FSMoveArgs struct { Src string `json:"src"` Dst string `json:"dst"` } // FSEntry is one directory entry. type FSEntry struct { Name string `json:"name"` IsDir bool `json:"is_dir"` Size int64 `json:"size"` Mode string `json:"mode"` MTime int64 `json:"mtime"` } // FSListResult is returned for TaskFSList. type FSListResult struct { Path string `json:"path"` Entries []FSEntry `json:"entries"` } // FSReadResult is returned for TaskFSRead. type FSReadResult struct { Path string `json:"path"` Size int64 `json:"size"` Content string `json:"content"` // base64 Truncated bool `json:"truncated"` Binary bool `json:"binary"` } // SysUsersResult is returned for TaskSysUsers. type SysUsersResult struct { Current string `json:"current"` // user the agent runs as CanSwitch bool `json:"can_switch"` // agent is root, so it can open shells as (and create) other users Users []string `json:"users"` } // SysUserAddArgs are the arguments of TaskSysUserAdd. type SysUserAddArgs struct { Name string `json:"name"` Password string `json:"password,omitempty"` // empty leaves the account without a password Sudo bool `json:"sudo"` // add to the sudo/wheel group } // SysStatResult is returned for TaskSysStat. Sizes are in bytes. type SysStatResult struct { CPU float64 `json:"cpu"` // load across all cores, 0..100 NCPU int `json:"ncpu"` MemTotal uint64 `json:"mem_total"` MemUsed uint64 `json:"mem_used"` DiskTotal uint64 `json:"disk_total"` // filesystem holding "/" DiskFree uint64 `json:"disk_free"` } // SysSpeedArgs are the arguments of TaskSysSpeed. type SysSpeedArgs struct { // Relay makes the agent use the relayed streams, which the server pipes // to and from the browser. Relay bool `json:"relay,omitempty"` } // SysSpeedResult is returned for TaskSysSpeed: the agent's HTTP link to the // server as the agent measured it. Down is server -> agent. type SysSpeedResult struct { PingMs float64 `json:"ping_ms"` DownBps float64 `json:"down_bps"` // bits per second UpBps float64 `json:"up_bps"` Relay bool `json:"relay,omitempty"` // the streams went through the relay }