Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Metrics fix, SAML better authentication and small changes #10

Merged
merged 2 commits into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion cmd/admin/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,55 @@ func handlerAuthCheck(h http.Handler) http.Handler {
// Access granted
h.ServeHTTP(w, r.WithContext(ctx))
case settings.AuthSAML:
samlMiddleware.RequireAccount(h).ServeHTTP(w, r)
if samlMiddleware.IsAuthorized(r) {
cookiev, err := r.Cookie(samlConfig.TokenName)
if err != nil {
log.Printf("error extracting JWT data: %v", err)
http.Redirect(w, r, forbiddenPath, http.StatusFound)
return
}
jwtdata, err := parseJWTFromCookie(samlData.KeyPair, cookiev.Value)
if err != nil {
log.Printf("error parsing JWT: %v", err)
http.Redirect(w, r, forbiddenPath, http.StatusFound)
return
}
// Check if user is already authenticated
authenticated, session := sessionsmgr.CheckAuth(r)
if !authenticated {
// Create user if it does not exist
if !adminUsers.Exists(jwtdata.Username) {
log.Printf("user not found: %s", jwtdata.Username)
http.Redirect(w, r, forbiddenPath, http.StatusFound)
return
}
u, err := adminUsers.Get(jwtdata.Username)
if err != nil {
log.Printf("error getting user %s: %v", jwtdata.Username, err)
http.Redirect(w, r, forbiddenPath, http.StatusFound)
return
}
// Create new session
session, err = sessionsmgr.Save(r, w, u)
if err != nil {
log.Printf("session error: %v", err)
}
}
// Update metadata for the user
err = adminUsers.UpdateMetadata(session.IPAddress, session.UserAgent, session.Username)
if err != nil {
log.Printf("error updating metadata for user %s: %v", session.Username, err)
}
// Set middleware values
s := make(contextValue)
s["user"] = session.Username
s["csrftoken"] = session.Values["csrftoken"].(string)
ctx := context.WithValue(r.Context(), contextKey("session"), s)
// Access granted
samlMiddleware.RequireAccount(h).ServeHTTP(w, r.WithContext(ctx))
} else {
samlMiddleware.RequireAccount(h).ServeHTTP(w, r)
}
case settings.AuthHeaders:
// Access always granted
h.ServeHTTP(w, r)
Expand Down
3 changes: 2 additions & 1 deletion cmd/admin/handlers-post.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func loginPOSTHandler(w http.ResponseWriter, r *http.Request) {
} else {
// Check credentials
if access, user := adminUsers.CheckLoginCredentials(l.Username, l.Password); access {
err = sessionsmgr.Save(r, w, user)
_, err = sessionsmgr.Save(r, w, user)
if err != nil {
responseMessage = "session error"
responseCode = http.StatusForbidden
Expand Down Expand Up @@ -238,6 +238,7 @@ func queryRunPOSTHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("%s %v", responseMessage, err)
goto response
}
expected = append(expected, h)
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions cmd/admin/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ func errorHTTPHandler(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("oh no..."))
}

// Handle forbidden error requests
func forbiddenHTTPHandler(w http.ResponseWriter, r *http.Request) {
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), true)
// Send response
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte("❌"))
}

// Handler for the favicon
func faviconHandler(w http.ResponseWriter, r *http.Request) {
utils.DebugHTTPDump(r, settingsmgr.DebugHTTP(settings.ServiceAdmin), false)
Expand Down
19 changes: 19 additions & 0 deletions cmd/admin/json-carves.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ type CarveJSON struct {
Created CreationTimes `json:"created"`
Status string `json:"status"`
Progress CarveProgress `json:"progress"`
Targets []CarveTarget `json:"targets"`
}

// CarveTarget to be returned with the JSON data for a carve
type CarveTarget struct {
Type string `json:"type"`
Value string `json:"value"`
}

// Handler for JSON carves by target
Expand Down Expand Up @@ -89,6 +96,17 @@ func jsonCarvesHandler(w http.ResponseWriter, r *http.Request) {
data := make(CarveData)
data["path"] = q.Path
data["name"] = q.Name
// Preparing query targets
ts, _ := queriesmgr.GetTargets(q.Name)
_ts := []CarveTarget{}
for _, t := range ts {
_t := CarveTarget{
Type: t.Type,
Value: t.Value,
}
_ts = append(_ts, _t)
}
// Preparing JSON
_c := CarveJSON{
Name: q.Name,
Creator: q.Creator,
Expand All @@ -99,6 +117,7 @@ func jsonCarvesHandler(w http.ResponseWriter, r *http.Request) {
},
Status: status,
Progress: progress,
Targets: _ts,
}
cJSON = append(cJSON, _c)
}
Expand Down
20 changes: 20 additions & 0 deletions cmd/admin/json-queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ type QueryJSON struct {
Created CreationTimes `json:"created"`
Status string `json:"status"`
Progress QueryProgress `json:"progress"`
Targets []QueryTarget `json:"targets"`
}

// QueryTarget to be returned with the JSON data for a query
type QueryTarget struct {
Type string `json:"type"`
Value string `json:"value"`
}

// Handler for JSON queries by target
Expand Down Expand Up @@ -70,13 +77,25 @@ func jsonQueryHandler(w http.ResponseWriter, r *http.Request) {
if q.Completed {
status = queries.StatusComplete
}
// Prepare progress data
progress := make(QueryProgress)
progress["expected"] = q.Expected
progress["executions"] = q.Executions
progress["errors"] = q.Errors
data := make(QueryData)
data["query"] = q.Query
data["name"] = q.Name
// Preparing query targets
ts, _ := queriesmgr.GetTargets(q.Name)
_ts := []QueryTarget{}
for _, t := range ts {
_t := QueryTarget{
Type: t.Type,
Value: t.Value,
}
_ts = append(_ts, _t)
}
// Preparing JSON
_q := QueryJSON{
Name: q.Name,
Creator: q.Creator,
Expand All @@ -87,6 +106,7 @@ func jsonQueryHandler(w http.ResponseWriter, r *http.Request) {
},
Status: status,
Progress: progress,
Targets: _ts,
}
qJSON = append(qJSON, _q)
}
Expand Down
77 changes: 36 additions & 41 deletions cmd/admin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ package main

import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"flag"
"fmt"
"log"
"net/http"
"net/url"
"time"

"github.com/jmpsec/osctrl/pkg/carves"
Expand Down Expand Up @@ -41,12 +38,18 @@ const (
appDescription string = serviceDescription + ", a fast and efficient osquery management"
// Default endpoint to handle HTTP health
healthPath string = "/health"
// Default endpoint to handle HTTP errors
// Default endpoint to handle Login
loginPath string = "/login"
// Default endpoint to handle HTTP(500) errors
errorPath string = "/error"
// Default endpoint to handle Forbidden(403) errors
forbiddenPath string = "/forbidden"
// Default service configuration file
configurationFile string = "config/" + settings.ServiceAdmin + ".json"
// Default DB configuration file
dbConfigurationFile string = "config/db.json"
// Default SAML configuration file
samlConfigurationFile string = "config/saml.json"
// osquery version to display tables
osqueryTablesVersion string = "3.3.2"
// JSON file with osquery tables data
Expand All @@ -61,11 +64,9 @@ const (
defaultInactive int = -72
)

// Global variables
// Global general variables
var (
adminConfig types.JSONConfigurationService
samlMiddleware *samlsp.Middleware
samlConfig JSONConfigurationSAML
db *gorm.DB
settingsmgr *settings.Settings
nodesmgr *nodes.NodeManager
Expand All @@ -85,6 +86,14 @@ var (
versionFlag *bool
configFlag *string
dbFlag *string
samlFlag *string
)

// SAML variables
var (
samlMiddleware *samlsp.Middleware
samlConfig JSONConfigurationSAML
samlData samlThings
)

// Valid values for auth and logging in configuration
Expand Down Expand Up @@ -121,16 +130,7 @@ func loadConfiguration(file, service string) (types.JSONConfigurationService, er
if !validLogging[cfg.Logging] {
return cfg, fmt.Errorf("Invalid logging method")
}
// Load configuration for the auth method
/*
if adminConfig.Auth == settings.AuthSAML {
samlRaw := viper.Sub(settings.AuthSAML)
err = samlRaw.Unmarshal(&samlConfig)
if err != nil {
return cfg, err
}
}
*/

// No errors!
return cfg, nil
}
Expand All @@ -144,6 +144,7 @@ func init() {
versionFlag = flag.Bool("v", false, "Displays the binary version.")
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.")
// Parse all flags
flag.Parse()
if *versionFlag {
Expand All @@ -156,6 +157,13 @@ func init() {
if err != nil {
log.Fatalf("Error loading %s - %s", *configFlag, 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)
}
}
// Load osquery tables JSON
osqueryTables, err = loadOsqueryTables(osqueryTablesFile)
if err != nil {
Expand Down Expand Up @@ -205,32 +213,17 @@ func main() {
if settingsmgr.DebugService(settings.ServiceAdmin) {
log.Println("DebugService: SAML keypair")
}
// Load Keypair to Sign SAML Request.
// Initialize SAML keypair to sign SAML Request.
var err error
var rootURL *url.URL
var idpMetadataURL *url.URL
var keyPair tls.Certificate
keyPair, err = tls.LoadX509KeyPair(samlConfig.CertPath, samlConfig.KeyPath)
if err != nil {
log.Fatal(err)
}
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
if err != nil {
log.Fatal(err)
}
idpMetadataURL, err = url.Parse(samlConfig.MetaDataURL)
if err != nil {
log.Fatal(err)
}
rootURL, err = url.Parse(samlConfig.RootURL)
samlData, err = keypairSAML(samlConfig)
if err != nil {
log.Fatal(err)
log.Fatalf("Can not initialize SAML keypair %s", err)
}
samlMiddleware, err = samlsp.New(samlsp.Options{
URL: *rootURL,
Key: keyPair.PrivateKey.(*rsa.PrivateKey),
Certificate: keyPair.Leaf,
IDPMetadataURL: idpMetadataURL,
URL: *samlData.RootURL,
Key: samlData.KeyPair.PrivateKey.(*rsa.PrivateKey),
Certificate: samlData.KeyPair.Leaf,
IDPMetadataURL: samlData.IdpMetadataURL,
AllowIDPInitiated: true,
})
if err != nil {
Expand All @@ -253,13 +246,15 @@ func main() {
// Admin: login only if local auth is enabled
if adminConfig.Auth != settings.AuthNone {
// login
routerAdmin.HandleFunc("/login", loginGETHandler).Methods("GET")
routerAdmin.HandleFunc("/login", loginPOSTHandler).Methods("POST")
routerAdmin.HandleFunc(loginPath, loginGETHandler).Methods("GET")
routerAdmin.HandleFunc(loginPath, loginPOSTHandler).Methods("POST")
}
// Admin: health of service
routerAdmin.HandleFunc(healthPath, healthHTTPHandler).Methods("GET")
// Admin: error
routerAdmin.HandleFunc(errorPath, errorHTTPHandler).Methods("GET")
// Admin: forbidden
routerAdmin.HandleFunc(forbiddenPath, forbiddenHTTPHandler).Methods("GET")
// Admin: favicon
routerAdmin.HandleFunc("/favicon.ico", faviconHandler)
// Admin: static
Expand Down
Loading