go-version #1
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@
|
||||
go.work
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/go
|
||||
build/
|
||||
|
BIN
build/lg-agent
BIN
build/lg-agent
Binary file not shown.
@@ -1,11 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"git.mziesel.nl/mans/lg/internal/socket"
|
||||
)
|
||||
@@ -63,7 +67,7 @@ func isAuthenticated(r *http.Request) bool {
|
||||
func ensureAuthenticatedMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAuthenticated(r) {
|
||||
http.Error(w, `{"error": "Unauthorized access"}`, http.StatusUnauthorized)
|
||||
http.Error(w, "Unauthorized access", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
@@ -77,14 +81,7 @@ func plainTextMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
func jsonMiddleware(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func apiMiddlewareChain(h http.HandlerFunc) http.HandlerFunc {
|
||||
func middlewareChain(h http.HandlerFunc) http.HandlerFunc {
|
||||
return plainTextMiddleware(ensureAuthenticatedMiddleware(h))
|
||||
}
|
||||
|
||||
@@ -134,6 +131,87 @@ func birdCommandHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func createCommand(tool string, target string) (*exec.Cmd, error) {
|
||||
commands := map[string][]string{
|
||||
"ping": {"ping", "-4", "-c", "4", "--", target},
|
||||
"ping6": {"ping", "-6", "-c", "4", "--", target},
|
||||
"mtr": {"mtr", "--show-ips", "--aslookup", "--report-wide", "-c", "10", "-4", target},
|
||||
"mtr6": {"mtr", "--show-ips", "--aslookup", "--report-wide", "-c", "10", "-6", target},
|
||||
"traceroute": {"traceroute", "-n", "-4", "--", target},
|
||||
"traceroute6": {"traceroute", "-n", "-6", "--", target},
|
||||
}
|
||||
if cmdArgs, exists := commands[tool]; exists {
|
||||
path, err := exec.LookPath(cmdArgs[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := exec.Command(path, cmdArgs[1:]...)
|
||||
return cmd, nil
|
||||
}
|
||||
return nil, errors.New("Command is not allowed")
|
||||
}
|
||||
|
||||
func isValidTarget(target string) error {
|
||||
addr, err := net.ResolveIPAddr("ip", target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addr.IP.IsPrivate() {
|
||||
return errors.New("RFC 1918 and RFC 4193 address space is not allowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func executeStreaming(w http.ResponseWriter, r *http.Request) {
|
||||
tool := r.URL.Query().Get("tool")
|
||||
target := r.URL.Query().Get("target")
|
||||
|
||||
err := isValidTarget(target)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Bad Target: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
w.Header().Set("Cache-Control", "no-cache")
|
||||
w.Header().Set("Connection", "keep-alive")
|
||||
|
||||
cmd, err := createCommand(tool, target)
|
||||
if err != nil {
|
||||
http.Error(w, "Internal Server Error: failed to create command", http.StatusInternalServerError)
|
||||
log.Printf("ERROR: Failed to create command: %s\n", err)
|
||||
return
|
||||
}
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
http.Error(w, "Internal Server Error: failed to open pipe to CMD output", http.StatusInternalServerError)
|
||||
log.Printf("ERROR: Failed to open pipe to CMD output: %s\n", err)
|
||||
return
|
||||
}
|
||||
cmd.Stderr = cmd.Stdout
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
http.Error(w, "Internal Server Error: failed to start command", http.StatusInternalServerError)
|
||||
log.Printf("ERROR: Failed to start command: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
|
||||
_, pw, _ := os.Pipe()
|
||||
cmd.Stdout = pw
|
||||
cmd.Stdout = pw
|
||||
|
||||
for scanner.Scan() {
|
||||
fmt.Fprintf(w, "%s\n", scanner.Bytes())
|
||||
if f, ok := w.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
config := loadConfig()
|
||||
formattedConfig, err := json.MarshalIndent(config, "", " ")
|
||||
@@ -144,9 +222,10 @@ func main() {
|
||||
|
||||
mux := http.NewServeMux()
|
||||
|
||||
mux.HandleFunc("/health", apiMiddlewareChain(healthHandler))
|
||||
mux.HandleFunc("/bird/status", apiMiddlewareChain(birdStatusHandler))
|
||||
mux.HandleFunc("/bird/command", apiMiddlewareChain(birdCommandHandler))
|
||||
mux.HandleFunc("/health", middlewareChain(healthHandler))
|
||||
mux.HandleFunc("/bird/status", middlewareChain(birdStatusHandler))
|
||||
mux.HandleFunc("/bird/command", middlewareChain(birdCommandHandler))
|
||||
mux.HandleFunc("/exec", middlewareChain(executeStreaming))
|
||||
|
||||
log.Printf("starting listner on %s:%d\n", config.Host, config.Port)
|
||||
|
||||
|
Reference in New Issue
Block a user