diff --git a/README.md b/README.md index 57e0a3e..5263b78 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A distributed management daemon designed to remotely configure system components - Go - NetworkManager - systemd -- Linux operating system +- Linux ## Installation diff --git a/cmd/rcond/main.go b/cmd/rcond/main.go index d6c562b..80d9de5 100644 --- a/cmd/rcond/main.go +++ b/cmd/rcond/main.go @@ -11,6 +11,9 @@ import ( "github.com/0x1d/rcond/pkg/cluster" "github.com/0x1d/rcond/pkg/config" http "github.com/0x1d/rcond/pkg/http" + "github.com/0x1d/rcond/pkg/network" + "github.com/0x1d/rcond/pkg/system" + "github.com/godbus/dbus/v5" ) func usage() { @@ -26,11 +29,13 @@ func main() { os.Exit(1) } + configureSystem(appConfig) clusterAgent := startClusterAgent(appConfig) startApiServer(appConfig, clusterAgent) select {} } + func loadConfig() (*config.Config, error) { configPath := "/etc/rcond/config.yaml" appConfig := &config.Config{} @@ -83,7 +88,7 @@ func startApiServer(appConfig *config.Config, clusterAgent *cluster.Agent) *http srv.WithClusterAgent(clusterAgent) srv.RegisterRoutes() - log.Printf("Starting API server on %s", appConfig.Rcond.Addr) + log.Printf("[INFO] Starting API server on %s", appConfig.Rcond.Addr) if err := srv.Start(); err != nil { log.Fatal(err) } @@ -93,7 +98,7 @@ func startApiServer(appConfig *config.Config, clusterAgent *cluster.Agent) *http func startClusterAgent(appConfig *config.Config) *cluster.Agent { clusterConfig := &appConfig.Cluster if clusterConfig.Enabled { - log.Printf("Starting cluster agent on %s:%d", clusterConfig.BindAddr, clusterConfig.BindPort) + log.Printf("[INFO] Starting cluster agent on %s:%d", clusterConfig.BindAddr, clusterConfig.BindPort) clusterAgent, err := cluster.NewAgent(clusterConfig, cluster.ClusterEventsMap()) if err != nil { log.Fatal(err) @@ -107,6 +112,43 @@ func startClusterAgent(appConfig *config.Config) *cluster.Agent { return nil } +func configureSystem(appConfig *config.Config) error { + log.Print("[INFO] Configure system") + // configure hostname + if err := network.SetHostname(appConfig.Hostname); err != nil { + log.Printf("[ERROR] %s", err) + } + // configure network connections + for _, connection := range appConfig.Network.Connections { + err := system.WithDbus(func(conn *dbus.Conn) error { + _, err := network.AddConnectionWithConfig(conn, &network.ConnectionConfig{ + Type: connection.Type, + UUID: connection.UUID, + ID: connection.ID, + AutoConnect: connection.AutoConnect, + SSID: connection.SSID, + Mode: connection.Mode, + Band: connection.Band, + Channel: connection.Channel, + KeyMgmt: connection.KeyMgmt, + PSK: connection.PSK, + IPv4Method: connection.IPv4Method, + IPv6Method: connection.IPv6Method, + }) + if err != nil { + return err + } + return nil + }) + if err != nil { + log.Printf("[ERROR] %s", err) + } + + } + log.Print("[INFO] System configured") + return nil +} + func overrideConfigValuesFromEnv(envMap map[string]*string) { for varName, configValue := range envMap { if envValue, ok := os.LookupEnv(varName); ok { diff --git a/config/rcond-rpi-ap.yaml b/config/rcond-rpi-ap.yaml new file mode 100644 index 0000000..7aa7bf4 --- /dev/null +++ b/config/rcond-rpi-ap.yaml @@ -0,0 +1,33 @@ +hostname: rpi-test + +rcond: + addr: 0.0.0.0:8081 + api_token: 1234567890 + +network: + connections: + # create access point and share network connection on wlan0 + - name: MyHomeWiFi + type: 802-11-wireless + interface: wlan0 + ssid: MyHomeWiFi + mode: ap + band: bg + channel: 1 + keymgmt: wpa-psk + psk: SuperSecure + ipv4method: shared + ipv6method: ignore + autoconnect: true + +cluster: + enabled: true + log_level: INFO + node_name: rcond-agent + secret_key: DMXnaJUUbIBMj1Df0dPsQY+Sks1VxWTa + advertise_addr: 0.0.0.0 + advertise_port: 7946 + bind_addr: 0.0.0.0 + bind_port: 7946 + #join: + # - 127.0.0.1:7946 \ No newline at end of file diff --git a/config/rcond-rpi-sta.yaml b/config/rcond-rpi-sta.yaml new file mode 100644 index 0000000..dc17202 --- /dev/null +++ b/config/rcond-rpi-sta.yaml @@ -0,0 +1,38 @@ +hostname: rpi-test + +rcond: + addr: 0.0.0.0:8081 + api_token: 1234567890 + +network: + connections: + # connect to MyHomeWifi on wlan0 + - name: MyHomeWiFi + type: 802-11-wireless + interface: wlan0 + ssid: MyHomeWiFi + mode: infrastructure + keymgmt: wpa-psk + psk: SuperSecure + ipv4method: auto + ipv6method: ignore + autoconnect: true + # TODO + # provide DHCP on eth0 + #- name: MyThingsNetwork + # type: 802-3-ethernet + # interface: eth0 + # method: shared + # autoconnect: true + +cluster: + enabled: true + log_level: INFO + node_name: rcond-agent + secret_key: DMXnaJUUbIBMj1Df0dPsQY+Sks1VxWTa + advertise_addr: 0.0.0.0 + advertise_port: 7946 + bind_addr: 0.0.0.0 + bind_port: 7946 + #join: + # - 127.0.0.1:7946 \ No newline at end of file diff --git a/config/rcond.yaml b/config/rcond.yaml index 9d048d4..1af6200 100644 --- a/config/rcond.yaml +++ b/config/rcond.yaml @@ -8,7 +8,7 @@ cluster: # Enable the cluster agent enabled: true # Log level - log_level: INFO + log_level: DEBUG # Name of the node in the cluster node_name: rcond # Secret key for the cluster agent used for message encryption. diff --git a/go.mod b/go.mod index 98528f2..71a534d 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ replace github.com/0x1d/rcond/cmd => ./cmd replace github.com/0x1d/rcond/pkg => ./pkg require ( + github.com/godbus/dbus v4.1.0+incompatible github.com/godbus/dbus/v5 v5.1.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 diff --git a/go.sum b/go.sum index da7c50e..98275cc 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= +github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= diff --git a/pkg/cluster/agent.go b/pkg/cluster/agent.go index 5826a3a..d2caef9 100644 --- a/pkg/cluster/agent.go +++ b/pkg/cluster/agent.go @@ -69,19 +69,19 @@ func (a *Agent) Event(event ClusterEvent) error { // Members returns a list of members in the Serf cluster. func (a *Agent) Members() ([]serf.Member, error) { - log.Printf("Getting members of the cluster") + log.Printf("[INFO] Getting members of the cluster") return a.Serf.Members(), nil } // Join attempts to join the Serf cluster with the given addresses, optionally ignoring old nodes. func (a *Agent) Join(addrs []string, ignoreOld bool) (int, error) { - log.Printf("Joining nodes in the cluster: %v", addrs) + log.Printf("[INFO] Joining nodes in the cluster: %v", addrs) n, err := a.Serf.Join(addrs, ignoreOld) if err != nil { - log.Printf("Failed to join nodes in the cluster: %v", err) + log.Printf("[ERROR] Failed to join nodes in the cluster: %v", err) return 0, err } - log.Printf("Joined %d nodes in the cluster", n) + log.Printf("[INFO] Joined %d nodes in the cluster", n) return n, nil } @@ -92,24 +92,24 @@ func (a *Agent) Leave() error { // Shutdown shuts down the Serf cluster agent. func (a *Agent) Shutdown() error { - log.Printf("Shutting down cluster agent") + log.Printf("[INFO] Shutting down cluster agent") return a.Serf.Shutdown() } // handleEvents handles Serf events received on the event channel. func handleEvents(eventCh chan serf.Event, clusterEvents map[string]func([]byte)) { + eventHandlers := clusterEvents for event := range eventCh { switch event.EventType() { case serf.EventUser: userEvent := event.(serf.UserEvent) - eventHandlers := clusterEvents if handler, ok := eventHandlers[userEvent.Name]; ok { handler(userEvent.Payload) } else { - log.Printf("No event handler found for event: %s", userEvent.Name) + log.Printf("[INFO] No event handler found for event: %s", userEvent.Name) } default: - log.Printf("Received event: %s\n", event.EventType()) + log.Printf("[INFO] Received event: %s\n", event.EventType()) } } } diff --git a/pkg/cluster/events.go b/pkg/cluster/events.go index c9e9077..7c5b803 100644 --- a/pkg/cluster/events.go +++ b/pkg/cluster/events.go @@ -17,18 +17,18 @@ func ClusterEventsMap() map[string]func([]byte) { func restart(payload []byte) { if err := system.Restart(); err != nil { - log.Printf("(ClusterEvent:restart) failed: %s", err) + log.Printf("[ERROR] (ClusterEvent:restart) failed: %s", err) } } func shutdown(payload []byte) { if err := system.Shutdown(); err != nil { - log.Printf("(ClusterEvent:shutdown) failed: %s", err) + log.Printf("[ERROR] (ClusterEvent:shutdown) failed: %s", err) } } // just a sample function to test event functionality func printHostname(payload []byte) { hostname, _ := network.GetHostname() - log.Printf("(ClusterEvent:printHostname): %s", hostname) + log.Printf("[INFO] (ClusterEvent:printHostname): %s", hostname) } diff --git a/pkg/config/config.go b/pkg/config/config.go index 9036bd7..c15787e 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,8 +7,10 @@ import ( ) type Config struct { - Rcond RcondConfig `yaml:"rcond"` - Cluster ClusterConfig `yaml:"cluster"` + Hostname string `yaml:"hostname"` + Rcond RcondConfig `yaml:"rcond"` + Network NetworkConfig `yaml:"network"` + Cluster ClusterConfig `yaml:"cluster"` } type RcondConfig struct { @@ -16,6 +18,25 @@ type RcondConfig struct { ApiToken string `yaml:"api_token"` } +type NetworkConfig struct { + Connections []ConnectionConfig `yaml:"connections"` +} + +type ConnectionConfig struct { + Type string `yaml:"type,omitempty"` + UUID string `yaml:"uuid,omitempty"` + ID string `yaml:"id,omitempty"` + AutoConnect bool `yaml:"autoconnect,omitempty"` + SSID string `yaml:"ssid,omitempty"` + Mode string `yaml:"mode,omitempty"` + Band string `yaml:"band,omitempty"` + Channel uint32 `yaml:"channel,omitempty"` + KeyMgmt string `yaml:"keymgmt,omitempty"` + PSK string `yaml:"psk,omitempty"` + IPv4Method string `yaml:"ipv4method,omitempty"` + IPv6Method string `yaml:"ipv6method,omitempty"` +} + type ClusterConfig struct { Enabled bool `yaml:"enabled"` NodeName string `yaml:"node_name"` diff --git a/pkg/system/dbus.go b/pkg/system/dbus.go index 801c881..60f59e0 100644 --- a/pkg/system/dbus.go +++ b/pkg/system/dbus.go @@ -11,11 +11,11 @@ import ( func WithDbus(fn func(*dbus.Conn) error) error { conn, err := dbus.SystemBus() if err != nil { - log.Printf("Failed to connect to system bus: %v", err) + log.Printf("[ERROR] Failed to connect to system bus: %v", err) return err } if err := fn(conn); err != nil { - log.Print(err) + log.Printf("[ERROR] Failed to execute D-Bus function: %s", err) return err } conn.Close()