Cron-Jobs Collector: System-Crontab + OPNsense config.xml Jobs
- Neuer Collector: agent/collector/cron.go - Zwei Quellen: crontab -l (System) + config.xml OPNsense-Jobs - Daten im Heartbeat unter 'cron_jobs' - Pro Job: source, schedule, enabled, command, description, uuid, origin - Backend Model CronJob in models/system.go - Getestet: 18 Jobs auf Produktion (8 System + 10 OPNsense) - README + Dateistruktur aktualisiert - Agent auf allen 3 Firewalls deployed
This commit is contained in:
parent
c72b791796
commit
3f3d555658
@ -49,6 +49,7 @@ Client ──► Backend:ProxyPort ──► WebSocket (Binary) ──► Agent
|
|||||||
| Zertifikate | Alle Certs + CAs, Ablaufdatum, Aussteller | `config.xml` + PEM-Parsing |
|
| Zertifikate | Alle Certs + CAs, Ablaufdatum, Aussteller | `config.xml` + PEM-Parsing |
|
||||||
| Plugins | Installierte OPNsense-Plugins | `pkg info` |
|
| Plugins | Installierte OPNsense-Plugins | `pkg info` |
|
||||||
| Updates | Pending Core + Package Updates | `opnsense-update -c`, `pkg upgrade -n` |
|
| Updates | Pending Core + Package Updates | `opnsense-update -c`, `pkg upgrade -n` |
|
||||||
|
| Cron | System-Crontab + OPNsense config.xml Jobs | `crontab -l`, `config.xml` |
|
||||||
|
|
||||||
### Remote-Commands (via WebSocket, on-demand)
|
### Remote-Commands (via WebSocket, on-demand)
|
||||||
|
|
||||||
@ -539,7 +540,8 @@ rmm/
|
|||||||
│ │ ├── gateways.go # Gateway-Status (configctl)
|
│ │ ├── gateways.go # Gateway-Status (configctl)
|
||||||
│ │ ├── certificates.go # Zertifikate + CAs (config.xml + PEM)
|
│ │ ├── certificates.go # Zertifikate + CAs (config.xml + PEM)
|
||||||
│ │ ├── plugins.go # Installierte Plugins (pkg info)
|
│ │ ├── plugins.go # Installierte Plugins (pkg info)
|
||||||
│ │ └── updates.go # Pending Updates (core + packages)
|
│ │ ├── updates.go # Pending Updates (core + packages)
|
||||||
|
│ │ └── cron.go # Cron-Jobs (System-Crontab + OPNsense config.xml)
|
||||||
│ └── ws/
|
│ └── ws/
|
||||||
│ ├── client.go # WebSocket Client, Reconnect, Ping/Pong
|
│ ├── client.go # WebSocket Client, Reconnect, Ping/Pong
|
||||||
│ ├── handler.go # Command Handler (exec, backup, tunnel_connect/disconnect)
|
│ ├── handler.go # Command Handler (exec, backup, tunnel_connect/disconnect)
|
||||||
|
|||||||
145
agent/collector/cron.go
Normal file
145
agent/collector/cron.go
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CronJob struct {
|
||||||
|
Source string `json:"source"` // "system" oder "opnsense"
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Minutes string `json:"minutes"`
|
||||||
|
Hours string `json:"hours"`
|
||||||
|
Days string `json:"days"`
|
||||||
|
Months string `json:"months"`
|
||||||
|
Weekdays string `json:"weekdays"`
|
||||||
|
Command string `json:"command"`
|
||||||
|
Parameters string `json:"parameters,omitempty"`
|
||||||
|
Who string `json:"who,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
UUID string `json:"uuid,omitempty"`
|
||||||
|
Origin string `json:"origin,omitempty"`
|
||||||
|
Schedule string `json:"schedule"` // Menschenlesbar: "*/4 * * * *"
|
||||||
|
}
|
||||||
|
|
||||||
|
func CollectCron() []CronJob {
|
||||||
|
var jobs []CronJob
|
||||||
|
|
||||||
|
// 1. System-Crontab (crontab -l)
|
||||||
|
sysJobs := collectSystemCrontab()
|
||||||
|
jobs = append(jobs, sysJobs...)
|
||||||
|
|
||||||
|
// 2. OPNsense config.xml Cron-Jobs
|
||||||
|
opnJobs := collectOPNsenseCron()
|
||||||
|
jobs = append(jobs, opnJobs...)
|
||||||
|
|
||||||
|
log.Printf("Cron: %d Jobs gesammelt (%d System, %d OPNsense)",
|
||||||
|
len(jobs), len(sysJobs), len(opnJobs))
|
||||||
|
|
||||||
|
return jobs
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectSystemCrontab() []CronJob {
|
||||||
|
out, err := exec.Command("/bin/sh", "-c", "/usr/bin/crontab -l").Output()
|
||||||
|
output := string(out)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var jobs []CronJob
|
||||||
|
for _, line := range strings.Split(output, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
// Kommentare, leere Zeilen, Variablen ueberspringen
|
||||||
|
if line == "" || strings.HasPrefix(line, "#") || strings.Contains(line, "=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crontab-Format: min hour day month weekday command
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
command := strings.Join(fields[5:], " ")
|
||||||
|
// Klammern und Redirects bereinigen
|
||||||
|
command = strings.TrimPrefix(command, "(")
|
||||||
|
command = strings.TrimSuffix(command, ") > /dev/null")
|
||||||
|
|
||||||
|
jobs = append(jobs, CronJob{
|
||||||
|
Source: "system",
|
||||||
|
Enabled: true,
|
||||||
|
Minutes: fields[0],
|
||||||
|
Hours: fields[1],
|
||||||
|
Days: fields[2],
|
||||||
|
Months: fields[3],
|
||||||
|
Weekdays: fields[4],
|
||||||
|
Command: command,
|
||||||
|
Schedule: strings.Join(fields[:5], " "),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return jobs
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectOPNsenseCron() []CronJob {
|
||||||
|
data, err := os.ReadFile("/conf/config.xml")
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cronJob struct {
|
||||||
|
UUID string `xml:"uuid,attr"`
|
||||||
|
Origin string `xml:"origin"`
|
||||||
|
Enabled string `xml:"enabled"`
|
||||||
|
Minutes string `xml:"minutes"`
|
||||||
|
Hours string `xml:"hours"`
|
||||||
|
Days string `xml:"days"`
|
||||||
|
Months string `xml:"months"`
|
||||||
|
Weekdays string `xml:"weekdays"`
|
||||||
|
Who string `xml:"who"`
|
||||||
|
Command string `xml:"command"`
|
||||||
|
Parameters string `xml:"parameters"`
|
||||||
|
Description string `xml:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type opnsenseCron struct {
|
||||||
|
Jobs []cronJob `xml:"OPNsense>cron>jobs>job"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg opnsenseCron
|
||||||
|
if err := xml.Unmarshal(data, &cfg); err != nil {
|
||||||
|
log.Printf("Cron XML parsen fehlgeschlagen: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var jobs []CronJob
|
||||||
|
for _, j := range cfg.Jobs {
|
||||||
|
schedule := strings.Join([]string{j.Minutes, j.Hours, j.Days, j.Months, j.Weekdays}, " ")
|
||||||
|
|
||||||
|
cmd := j.Command
|
||||||
|
if j.Parameters != "" {
|
||||||
|
cmd += " " + j.Parameters
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs = append(jobs, CronJob{
|
||||||
|
Source: "opnsense",
|
||||||
|
Enabled: j.Enabled == "1",
|
||||||
|
Minutes: j.Minutes,
|
||||||
|
Hours: j.Hours,
|
||||||
|
Days: j.Days,
|
||||||
|
Months: j.Months,
|
||||||
|
Weekdays: j.Weekdays,
|
||||||
|
Command: cmd,
|
||||||
|
Who: j.Who,
|
||||||
|
Description: strings.TrimSpace(j.Description),
|
||||||
|
UUID: j.UUID,
|
||||||
|
Origin: j.Origin,
|
||||||
|
Schedule: schedule,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return jobs
|
||||||
|
}
|
||||||
@ -121,6 +121,7 @@ func sendHeartbeat(c *client.Client, agentID string) error {
|
|||||||
certs := collector.CollectCertificates()
|
certs := collector.CollectCertificates()
|
||||||
plugins := collector.CollectPlugins()
|
plugins := collector.CollectPlugins()
|
||||||
updates := collector.CollectUpdates()
|
updates := collector.CollectUpdates()
|
||||||
|
cronJobs := collector.CollectCron()
|
||||||
|
|
||||||
req := map[string]interface{}{
|
req := map[string]interface{}{
|
||||||
"agent_id": agentID,
|
"agent_id": agentID,
|
||||||
@ -158,6 +159,7 @@ func sendHeartbeat(c *client.Client, agentID string) error {
|
|||||||
"certificates": certs,
|
"certificates": certs,
|
||||||
"plugins": plugins,
|
"plugins": plugins,
|
||||||
"updates": updates,
|
"updates": updates,
|
||||||
|
"cron_jobs": cronJobs,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ type SystemData struct {
|
|||||||
Certificates []CertificateInfo `json:"certificates,omitempty"`
|
Certificates []CertificateInfo `json:"certificates,omitempty"`
|
||||||
Plugins []PluginInfo `json:"plugins,omitempty"`
|
Plugins []PluginInfo `json:"plugins,omitempty"`
|
||||||
Updates *UpdateInfo `json:"updates,omitempty"`
|
Updates *UpdateInfo `json:"updates,omitempty"`
|
||||||
|
CronJobs []CronJob `json:"cron_jobs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WireGuardTunnel struct {
|
type WireGuardTunnel struct {
|
||||||
@ -152,6 +153,23 @@ type PendingUpdateInfo struct {
|
|||||||
NewVer string `json:"new_version"`
|
NewVer string `json:"new_version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CronJob struct {
|
||||||
|
Source string `json:"source"`
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Minutes string `json:"minutes"`
|
||||||
|
Hours string `json:"hours"`
|
||||||
|
Days string `json:"days"`
|
||||||
|
Months string `json:"months"`
|
||||||
|
Weekdays string `json:"weekdays"`
|
||||||
|
Command string `json:"command"`
|
||||||
|
Parameters string `json:"parameters,omitempty"`
|
||||||
|
Who string `json:"who,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
UUID string `json:"uuid,omitempty"`
|
||||||
|
Origin string `json:"origin,omitempty"`
|
||||||
|
Schedule string `json:"schedule"`
|
||||||
|
}
|
||||||
|
|
||||||
// HeartbeatRequest
|
// HeartbeatRequest
|
||||||
type HeartbeatRequest struct {
|
type HeartbeatRequest struct {
|
||||||
AgentID string `json:"agent_id"`
|
AgentID string `json:"agent_id"`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user