From 5521229f825b9324f0ffd5be2e60f6180da36754 Mon Sep 17 00:00:00 2001 From: Yasen Pramatarov Date: Tue, 3 Sep 2024 13:57:17 +0300 Subject: [PATCH] Adds JWT token checks --- CHANGELOG.md | 2 +- go/go.mod | 5 ++- go/go.sum | 2 ++ go/jilo-agent.conf | 3 ++ go/main.go | 76 +++++++++++++++++++++++++++++++++++++++------- 5 files changed, 75 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5d239..9c09e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,6 @@ All notable changes to this project will be documented in this file. ### Added - Initial version in PHP - New version in folder "go", written in Go -- Added endpoints for /nginx, /prosody, /jicofo, /jvb +- Added endpoints for /nginx, /prosody, /jicofo, /jvb, /jibri - Added a config file and default values - Initial vesion of a build script diff --git a/go/go.mod b/go/go.mod index 14bff77..aa9280a 100644 --- a/go/go.mod +++ b/go/go.mod @@ -2,4 +2,7 @@ module jilo-agent go 1.22.6 -require gopkg.in/yaml.v2 v2.4.0 // indirect +require ( + github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/go/go.sum b/go/go.sum index 7534661..2e8eddb 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1,3 +1,5 @@ +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/go/jilo-agent.conf b/go/jilo-agent.conf index 3f69caf..cf38812 100644 --- a/go/jilo-agent.conf +++ b/go/jilo-agent.conf @@ -5,6 +5,9 @@ # the port on which the agent will listen agent_port: 8081 +# secret for checking JWT (same as in Jilo Web agent config) +secret_key: "mysecretkey" + # the port we check for nginx nginx_port: 80 diff --git a/go/main.go b/go/main.go index 522ef08..54fe4ee 100644 --- a/go/main.go +++ b/go/main.go @@ -12,9 +12,10 @@ Version: 0.1 package main import ( - "encoding/json" "fmt" + "encoding/json" "gopkg.in/yaml.v2" + "github.com/dgrijalva/jwt-go" "io/ioutil" "log" "net/http" @@ -27,6 +28,7 @@ import ( // Config holds the structure of the configuration file type Config struct { AgentPort int `yaml:"agent_port"` + SecretKey string `yaml:"secret_key"` NginxPort int `yaml:"nginx_port"` ProsodyPort int `yaml:"prosody_port"` JicofoStatsURL string `yaml:"jicofo_stats_url"` @@ -34,6 +36,13 @@ type Config struct { JibriHealthURL string `yaml:"jibri_health_url"` } +// Claims holds JWT access right +type Claims struct { + Username string `json:"sub"` + Role string `json:"role"` + jwt.StandardClaims +} + // NginxData holds the nginx data structure for the API response to /nginx type NginxData struct { NginxState string `json:"nginx_state"` @@ -64,6 +73,8 @@ type JibriData struct { JibriHealthData map[string]interface{} `json:"jibri_health_data"` } +var secretKey []byte + // getServiceState checks the status of the speciied service func getServiceState(service string) string { output, err := exec.Command("systemctl", "is-active", service).Output() @@ -145,6 +156,48 @@ func loadConfig(filename string) (Config) { return config } +// authenticationJWT handles the JWT auth +func authenticationJWT(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + tokenString := r.Header.Get("Authorization") + + // empty auth header + if tokenString == "" { + http.Error(w, "Auth header not received", http.StatusUnauthorized) + return + } + + // remove "Bearer " + if len(tokenString) > 7 && tokenString[:7] == "Bearer " { + tokenString = tokenString[7:] + } + + claims := &Claims{} + + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + return secretKey, nil + }) + + // JWT errors + if err != nil { + if err == jwt.ErrSignatureInvalid { + http.Error(w, "Invalid JWT signature", http.StatusUnauthorized) + return + } + http.Error(w, "Error parsing JWT", http.StatusUnauthorized) + return + } + + // JWT invalid + if !token.Valid { + http.Error(w, "Invali JWT token", http.StatusUnauthorized) + return + } + + next.ServeHTTP(w, r) + }) +} + // nginxHandler handles the /nginx endpoint func nginxHandler(config Config, w http.ResponseWriter, r *http.Request) { data := NginxData { @@ -201,23 +254,24 @@ func main() { // load the configuration config := loadConfig("jilo-agent.conf") + secretKey = []byte(config.SecretKey) // endpoints - http.HandleFunc("/nginx", func(w http.ResponseWriter, r *http.Request) { + http.Handle("/nginx", authenticationJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nginxHandler(config, w, r) - }) - http.HandleFunc("/prosody", func(w http.ResponseWriter, r *http.Request) { + }))) + http.Handle("/prosody", authenticationJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { prosodyHandler(config, w, r) - }) - http.HandleFunc("/jicofo", func(w http.ResponseWriter, r *http.Request) { + }))) + http.Handle("/jicofo", authenticationJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { jicofoHandler(config, w, r) - }) - http.HandleFunc("/jvb", func(w http.ResponseWriter, r *http.Request) { + }))) + http.Handle("/jvb", authenticationJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { jvbHandler(config, w, r) - }) - http.HandleFunc("/jibri", func(w http.ResponseWriter, r *http.Request) { + }))) + http.Handle("/jibri", authenticationJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { jibriHandler(config, w, r) - }) + }))) // start the http server agentPortStr := fmt.Sprintf(":%d", config.AgentPort)