diff --git a/cmd/admin/auth.go b/cmd/admin/auth.go index 792dec02..361bb4df 100644 --- a/cmd/admin/auth.go +++ b/cmd/admin/auth.go @@ -4,6 +4,7 @@ import ( "context" "log" "net/http" + "strings" "github.com/jmpsec/osctrl/pkg/settings" ) @@ -102,8 +103,45 @@ func handlerAuthCheck(h http.Handler) http.Handler { samlMiddleware.RequireAccount(h).ServeHTTP(w, r) } case settings.AuthHeaders: - // Access always granted - h.ServeHTTP(w, r) + username := r.Header.Get(headersConfig.TrustedPrefix + headersConfig.UserName) + groups := strings.Split(r.Header.Get(headersConfig.TrustedPrefix + headersConfig.Groups), ",") + + if username == "" { + log.Printf("A username is required to use this system.") + w.WriteHeader(http.StatusBadRequest) + return + } + + s := make(contextValue) + s["user"] = username + + for _, group := range groups { + if (group == headersConfig.AdminGroup) { + s["level"] = adminLevel + // We can break because there is no greater permission level + break + } else if (group == headersConfig.UserGroup) { + s["level"] = userLevel + // We can't break because we might still find a higher permission level + } + } + + // This user didn't present a group that has permission to use the service + if _, ok := s["level"]; !ok { + w.WriteHeader(http.StatusForbidden) + return + } + + err := adminUsers.UpdateMetadata(r.RemoteAddr, r.Header.Get("User-Agent"), username) + if err != nil { + log.Printf("error updating metadata for user %s: %v", username, err) + } + + //s["csrftoken"] = session.Values["csrftoken"].(string) + ctx := context.WithValue(r.Context(), contextKey("session"), s) + + // Access granted + h.ServeHTTP(w, r.WithContext(ctx)) } }) } diff --git a/cmd/admin/headers.go b/cmd/admin/headers.go new file mode 100644 index 00000000..4ef7f0ad --- /dev/null +++ b/cmd/admin/headers.go @@ -0,0 +1,30 @@ +package main + +import ( + "log" + + "github.com/jmpsec/osctrl/pkg/settings" + "github.com/jmpsec/osctrl/pkg/types" + "github.com/spf13/viper" +) + +// Function to load the configuration file +func loadHeaders(file string) (types.JSONConfigurationHeaders, error) { + var cfg types.JSONConfigurationHeaders + log.Printf("Loading %s", file) + // Load file and read config + viper.SetConfigFile(file) + err := viper.ReadInConfig() + if err != nil { + return cfg, err + } + // Header values + headersRaw := viper.Sub(settings.AuthHeaders) + err = headersRaw.Unmarshal(&cfg) + if err != nil { + return cfg, err + } + + // No errors! + return cfg, nil +} \ No newline at end of file diff --git a/cmd/admin/main.go b/cmd/admin/main.go index c4079a7f..6263fa06 100644 --- a/cmd/admin/main.go +++ b/cmd/admin/main.go @@ -50,6 +50,8 @@ const ( dbConfigurationFile string = "config/db.json" // Default SAML configuration file samlConfigurationFile string = "config/saml.json" + // Default Headers configuration file + headersConfigurationFile string = "config/headers.json" // osquery version to display tables osqueryTablesVersion string = "4.0.1" // JSON file with osquery tables data @@ -87,6 +89,7 @@ var ( configFlag *string dbFlag *string samlFlag *string + headersFlag *string ) // SAML variables @@ -96,13 +99,20 @@ var ( samlData samlThings ) -// Valid values for auth and logging in configuration +// Headers variables +var ( + headersConfig types.JSONConfigurationHeaders +) + +// Valid values for auth in configuration var validAuth = map[string]bool{ settings.AuthDB: true, settings.AuthSAML: true, settings.AuthHeaders: true, settings.AuthJSON: true, } + +// Valid values for logging in configuration var validLogging = map[string]bool{ settings.LoggingDB: true, settings.LoggingSplunk: true, @@ -146,6 +156,7 @@ func init() { configFlag = flag.String("c", configurationFile, "Service configuration JSON file to use.") dbFlag = flag.String("D", dbConfigurationFile, "DB configuration JSON file to use.") samlFlag = flag.String("S", samlConfigurationFile, "SAML configuration JSON file to use.") + headersFlag = flag.String("H", headersConfigurationFile, "Headers configuration JSON file to use.") // Parse all flags flag.Parse() if *versionFlag { @@ -158,17 +169,29 @@ func init() { if err != nil { log.Fatalf("Error loading %s - %s", *configFlag, err) } + + // Load osquery tables JSON + osqueryTables, err = loadOsqueryTables(osqueryTablesFile) + if err != nil { + log.Fatalf("Error loading osquery tables %s", err) + } + // Load configuration for SAML if enabled if adminConfig.Auth == settings.AuthSAML { samlConfig, err = loadSAML(*samlFlag) if err != nil { log.Fatalf("Error loading %s - %s", *samlFlag, err) } + return } - // Load osquery tables JSON - osqueryTables, err = loadOsqueryTables(osqueryTablesFile) - if err != nil { - log.Fatalf("Error loading osquery tables %s", err) + + // Load configuration for Headers if enabled + if adminConfig.Auth == settings.AuthHeaders { + headersConfig, err = loadHeaders(*headersFlag) + if err != nil { + log.Fatalf("Error loading %s - %s", *headersFlag, err) + } + return } } diff --git a/pkg/types/types.go b/pkg/types/types.go index e42f0e84..33136cbf 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -20,3 +20,17 @@ type JSONConfigurationService struct { Auth string `json:"auth"` Logging string `json:"logging"` } + +// JSONConfigurationHeaders to keep all SAML details for auth +type JSONConfigurationHeaders struct { + TrustedPrefix string `json:"trustedPrefix"` + AdminGroup string `json:"adminGroup"` + UserGroup string `json:"userGroup"` + Email string `json:"email"` + UserName string `json:"userName"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + DisplayName string `json:"displayName"` + DistinguishedName string `json:"distinguishedName"` + Groups string `json:"groups"` +}