Author: Paolo Lulli <paolo@lulli.net>
Enable TLS without balancer
server/config/config.go | 4 + server/rest/mtls.go | 22 +++++++ server/rest/rest-event.go | 101 +++++++++++++++++++----------------- server/rest/rest-metric.go | 110 +++++++++++++++++++++------------------ server/service-rest.go | 25 +++++---
diff --git a/server/config/config.go b/server/config/config.go index 2864005276045c5cbed4eceb7bd29f990621cbdb..7e276b83e953a62216c91c25dc00e9f343111cfb 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -48,6 +48,10 @@ ArchiveFrequency string `json:"archiveFrequency"` ArchiveDirectory string `json:"archiveDirectory"` IsArchiveNode string `json:"archiveNode"` + + TlsActive string `json:"tlsActive"` + TlsKeyFile string `json:"tlsKeyFile"` + TlsCertificate string `json:"tlsCertificate"` } func GetConfig(fileName string) Configuration { diff --git a/server/rest/mtls.go b/server/rest/mtls.go index bc68bb775d3d5954b4d960b3c41436338a0a917e..49d2a01f4bd6f1c51a14b78830863d9f9779aaab 100644 --- a/server/rest/mtls.go +++ b/server/rest/mtls.go @@ -10,8 +10,26 @@ */ package rest -import "github.com/gin-gonic/gin" +import ( + "github.com/gin-gonic/gin" + "yats-server/config" +) -func GetClientCN(c *gin.Context) string { +func GetClientCN(c *gin.Context, cfg config.Configuration) string { + if cfg.TlsActive == "true" { + s, done := extractCommonName(c) + if done { + return s + } + panic("Could not extract common name") + } return c.Request.Header.Get("X-SSL-Client-CN") } + +func extractCommonName(c *gin.Context) (string, bool) { + certificates := c.Request.TLS.PeerCertificates + if len(certificates) > 0 { + return certificates[0].Subject.CommonName, true + } + panic("Could not extract common name") +} diff --git a/server/rest/rest-event.go b/server/rest/rest-event.go index 46d947ff442c27a21507f4b813c8fbb5c3739d5a..7cac661564009aefb5ec3aaf9bdec3380e5f54cb 100644 --- a/server/rest/rest-event.go +++ b/server/rest/rest-event.go @@ -16,6 +16,7 @@ "github.com/gin-gonic/gin" "net/http" "strconv" "time" + "yats-server/config" "yats-server/db" "yats-server/model" ) @@ -31,23 +32,25 @@ // @Param event1 body model.EventModel true "Event body data" // @Produce json // @Success 202 {string} WriteEvent // @Router /event [post] -func WriteEvent(c *gin.Context) { - var event model.EventModel +func WriteEvent(cfg config.Configuration) gin.HandlerFunc { + return func(c *gin.Context) { + var event model.EventModel - if err := c.BindJSON(&event); err != nil { - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) - return - } + if err := c.BindJSON(&event); err != nil { + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) + return + } - clientCN := GetClientCN(c) - fmt.Printf("%s / %s ", clientCN, event.Name) - if event.Etime == 0 { - db.SaveEvent(clientCN, event.Name) - } else { - unixTimeUTC := time.Unix(event.Etime, 0) - db.SaveEventAt(clientCN, unixTimeUTC, event.Name) + clientCN := GetClientCN(c, cfg) + fmt.Printf("%s / %s ", clientCN, event.Name) + if event.Etime == 0 { + db.SaveEvent(clientCN, event.Name) + } else { + unixTimeUTC := time.Unix(event.Etime, 0) + db.SaveEventAt(clientCN, unixTimeUTC, event.Name) + } + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK"}) } - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK"}) } // SearchEvents godoc @@ -61,30 +64,32 @@ // @Param event2 body model.EventSearchRequest true "Event query filters" // @Produce json // @Success 200 {string} SearchEvents // @Router /event/search [post] -func SearchEvents(c *gin.Context) { - var event model.EventSearchRequest +func SearchEvents(cfg config.Configuration) gin.HandlerFunc { + return func(c *gin.Context) { + var event model.EventSearchRequest - if err := c.BindJSON(&event); err != nil { - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) - return - } + if err := c.BindJSON(&event); err != nil { + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) + return + } - clientCN := GetClientCN(c) - fmt.Printf("Client ID: %s\n ", clientCN) + clientCN := GetClientCN(c, cfg) + fmt.Printf("Client ID: %s\n ", clientCN) - var eventsPack []model.EventModel - if event.To == 0 { - unixTimeUTC := time.Unix(event.From, 0) - timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() - eventsPack = db.EventsFrom(db.Session, clientCN, string(timeAsBytes), 100) - } else if event.From != 0 { - fromUnixTimeUTC := time.Unix(event.From, 0) - toUnixTimeUTC := time.Unix(event.To, 0) - fromInBytes, _ := fromUnixTimeUTC.UTC().MarshalText() - toInBytes, _ := toUnixTimeUTC.UTC().MarshalText() - eventsPack = db.EventsBetween(db.Session, clientCN, string(fromInBytes), string(toInBytes), 100) + var eventsPack []model.EventModel + if event.To == 0 { + unixTimeUTC := time.Unix(event.From, 0) + timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() + eventsPack = db.EventsFrom(db.Session, clientCN, string(timeAsBytes), 100) + } else if event.From != 0 { + fromUnixTimeUTC := time.Unix(event.From, 0) + toUnixTimeUTC := time.Unix(event.To, 0) + fromInBytes, _ := fromUnixTimeUTC.UTC().MarshalText() + toInBytes, _ := toUnixTimeUTC.UTC().MarshalText() + eventsPack = db.EventsBetween(db.Session, clientCN, string(fromInBytes), string(toInBytes), 100) + } + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": eventsPack}) } - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": eventsPack}) } // GetEventsFrom godoc @@ -99,22 +104,24 @@ // @Produce json // @Success 200 {string} SearchEvents // @Router /event/{from} [get] // @Param from path string true "Starting from timestamp :from" -func GetEventsFrom(c *gin.Context) { +func GetEventsFrom(cfg config.Configuration) gin.HandlerFunc { + return func(c *gin.Context) { - fromParam := c.Param("from") - fromParamInt64, err := strconv.ParseInt(fromParam, 10, 64) - if err != nil { - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) - } + fromParam := c.Param("from") + fromParamInt64, err := strconv.ParseInt(fromParam, 10, 64) + if err != nil { + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) + } - clientCN := GetClientCN(c) - fmt.Printf("Client ID: %s\n ", clientCN) + clientCN := GetClientCN(c, cfg) + fmt.Printf("Client ID: %s\n ", clientCN) - var eventsPack []model.EventModel + var eventsPack []model.EventModel - unixTimeUTC := time.Unix(fromParamInt64, 0) - timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() - eventsPack = db.EventsFrom(db.Session, clientCN, string(timeAsBytes), 100) + unixTimeUTC := time.Unix(fromParamInt64, 0) + timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() + eventsPack = db.EventsFrom(db.Session, clientCN, string(timeAsBytes), 100) - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": eventsPack}) + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": eventsPack}) + } } diff --git a/server/rest/rest-metric.go b/server/rest/rest-metric.go index 260775b52f8fcb5985460f3b64281dc414711ee9..1d971744337b0abb0f5f16e0ef8e1864e471c1c2 100644 --- a/server/rest/rest-metric.go +++ b/server/rest/rest-metric.go @@ -16,6 +16,7 @@ "github.com/gin-gonic/gin" "net/http" "strconv" "time" + "yats-server/config" "yats-server/db" "yats-server/model" ) @@ -33,25 +34,27 @@ // @Param metric2 body model.MetricModel true "Metric request body" // @Produce json // @Success 202 {string} WriteMetric // @Router /metric [post] -func WriteMetric(c *gin.Context) { - var metric model.MetricModel +func WriteMetric(cfg config.Configuration) gin.HandlerFunc { + return func(c *gin.Context) { + var metric model.MetricModel - if err := c.BindJSON(&metric); err != nil { - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) - return - } + if err := c.BindJSON(&metric); err != nil { + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) + return + } - clientCN := GetClientCN(c) - fmt.Printf("%s / %s / %s", clientCN, metric.Name, metric.Value) + clientCN := GetClientCN(c, cfg) + fmt.Printf("%s / %s / %s", clientCN, metric.Name, metric.Value) - if metric.Mtime != 0 { - unixTimeUTC := time.Unix(metric.Mtime, 0) - db.SaveMetricAt(clientCN, unixTimeUTC, metric.Name, metric.Value) - } else { - db.SaveMetric(clientCN, metric.Name, metric.Value) + if metric.Mtime != 0 { + unixTimeUTC := time.Unix(metric.Mtime, 0) + db.SaveMetricAt(clientCN, unixTimeUTC, metric.Name, metric.Value) + } else { + db.SaveMetric(clientCN, metric.Name, metric.Value) + } + + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK"}) } - - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK"}) } // SearchMetrics godoc @@ -65,36 +68,38 @@ // @Param metric1 body model.MetricSearchRequest true "Metric query filters" // @Produce json // @Success 200 {string} WriteMetric // @Router /metric/search [post] -func SearchMetrics(c *gin.Context) { - var metric model.MetricSearchRequest +func SearchMetrics(cfg config.Configuration) gin.HandlerFunc { + return func(c *gin.Context) { + var metric model.MetricSearchRequest - if err := c.BindJSON(&metric); err != nil { - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) - return - } + if err := c.BindJSON(&metric); err != nil { + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) + return + } - clientCN := GetClientCN(c) - fmt.Printf("%s / %s ", clientCN, metric.Name) + clientCN := GetClientCN(c, cfg) + fmt.Printf("%s / %s ", clientCN, metric.Name) - var metricsPack []model.MetricModel + var metricsPack []model.MetricModel - if metric.To == 0 { - unixTimeUTC := time.Unix(metric.From, 0) - timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() - metricsPack = db.MetricsFrom(db.Session, clientCN, metric.Name, string(timeAsBytes), 100) - } else if metric.From != 0 { - fromUnixTimeUTC := time.Unix(metric.From, 0) - toUnixTimeUTC := time.Unix(metric.To, 0) - fromInBytes, _ := fromUnixTimeUTC.UTC().MarshalText() - toInBytes, _ := toUnixTimeUTC.UTC().MarshalText() - metricsPack = db.MetricsBetween(db.Session, clientCN, metric.Name, string(fromInBytes), string(toInBytes), 100) + if metric.To == 0 { + unixTimeUTC := time.Unix(metric.From, 0) + timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() + metricsPack = db.MetricsFrom(db.Session, clientCN, metric.Name, string(timeAsBytes), 100) + } else if metric.From != 0 { + fromUnixTimeUTC := time.Unix(metric.From, 0) + toUnixTimeUTC := time.Unix(metric.To, 0) + fromInBytes, _ := fromUnixTimeUTC.UTC().MarshalText() + toInBytes, _ := toUnixTimeUTC.UTC().MarshalText() + metricsPack = db.MetricsBetween(db.Session, clientCN, metric.Name, string(fromInBytes), string(toInBytes), 100) + } + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": metricsPack}) } - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": metricsPack}) } // SearchMetricsFrom SearchMetrics godoc // @Param X-SSL-Client-CN header string true "clientCN" -// @Summary Get the spcific metric {name} starting from the epoch {from} +// @Summary Get the specific metric {name} starting from the epoch {from} // @Schemes // @Description search metrics after the specified timestamp // @Tags Metrics @@ -103,24 +108,27 @@ // @Success 200 {string} []model.MetricModel // @Router /metric/{name}/{from} [get] // @Param name path string true "Metric Name :name" // @Param from path string true "Starting from epoch :from" -func SearchMetricsFrom(c *gin.Context) { - var metric model.MetricSearchRequest +func SearchMetricsFrom(cfg config.Configuration) gin.HandlerFunc { + return func(c *gin.Context) { + + var metric model.MetricSearchRequest - nameParam := c.Param("name") - fromParam := c.Param("from") - fromParamInt64, err := strconv.ParseInt(fromParam, 10, 64) - if err != nil { - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) - } + nameParam := c.Param("name") + fromParam := c.Param("from") + fromParamInt64, err := strconv.ParseInt(fromParam, 10, 64) + if err != nil { + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "-1"}) + } - clientCN := GetClientCN(c) - fmt.Printf("%s / %s ", clientCN, metric.Name) + clientCN := GetClientCN(c, cfg) + fmt.Printf("%s / %s ", clientCN, metric.Name) - var metricsPack []model.MetricModel + var metricsPack []model.MetricModel - unixTimeUTC := time.Unix(fromParamInt64, 0) - timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() - metricsPack = db.MetricsFrom(db.Session, clientCN, nameParam, string(timeAsBytes), 100) + unixTimeUTC := time.Unix(fromParamInt64, 0) + timeAsBytes, _ := unixTimeUTC.UTC().MarshalText() + metricsPack = db.MetricsFrom(db.Session, clientCN, nameParam, string(timeAsBytes), 100) - c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": metricsPack}) + c.IndentedJSON(http.StatusAccepted, gin.H{"ret": "OK", "content": metricsPack}) + } } diff --git a/server/service-rest.go b/server/service-rest.go index 5baef4d6c5c455e5b50dfcc4713cefa87fc91aa9..e5dd75ce3689905199846e853b234ac5ba04071a 100644 --- a/server/service-rest.go +++ b/server/service-rest.go @@ -17,10 +17,10 @@ "github.com/gin-gonic/gin" ) -func RestService(c config.Configuration) { +func RestService(cfg config.Configuration) { // session = DB - address := c.RestAddress + address := cfg.RestAddress router := gin.Default() @@ -29,16 +29,21 @@ router.SetTrustedProxies([]string{"127.0.0.1"}) gin.SetMode(gin.ReleaseMode) - router.POST("/metric", rest.WriteMetric) - router.POST("/metric/search", rest.SearchMetrics) - router.GET("/metric/:name/:from", rest.SearchMetricsFrom) + router.POST("/metric", rest.WriteMetric(cfg)) + router.POST("/metric/search", rest.SearchMetrics(cfg)) + router.GET("/metric/:name/:from", rest.SearchMetricsFrom(cfg)) + + router.POST("/event", rest.WriteEvent(cfg)) + router.POST("/event/search", rest.SearchEvents(cfg)) + router.GET("/event/:from", rest.GetEventsFrom(cfg)) - router.POST("/event", rest.WriteEvent) - router.POST("/event/search", rest.SearchEvents) - router.GET("/event/:from", rest.GetEventsFrom) + router.POST("/position", rest.WritePosition(cfg)) enableSwaggerEndpoint(router) - router.Run(address) - //router.RunTLS(address, c.CertFile, c.KeyFile) + if cfg.TlsActive == "true" { + router.RunTLS(address, cfg.TlsCertificate, cfg.TlsKeyFile) + } else { + router.Run(address) + } }