From e0d477c10de67b1b73234b328be0b8f5a0e7d8f4 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Tue, 7 Feb 2023 17:01:17 +0000 Subject: [PATCH 1/7] zhars/actualize_prometheus_metrics_collecting Actualize prometheus metrics --- cmd/acra-server/common/prometheus.go | 5 +- cmd/acra-translator/common/prometheus.go | 91 ++++++++++++++++++++---- cmd/acra-translator/common/service.go | 15 ++-- cmd/acra-translator/grpc_api/service.go | 8 +-- cmd/acra-translator/http_api/service.go | 41 ++++------- crypto/acrastruct.go | 7 +- crypto/decryptor.go | 6 +- crypto/prometheus.go | 44 ++++++++++++ crypto/registry.go | 9 ++- decryptor/base/prometheus.go | 83 +++++++++++++++++---- pseudonymization/data_encoder.go | 11 ++- pseudonymization/queryDataEncryptor.go | 12 +++- tests/test.py | 2 + 13 files changed, 261 insertions(+), 73 deletions(-) create mode 100644 crypto/prometheus.go diff --git a/cmd/acra-server/common/prometheus.go b/cmd/acra-server/common/prometheus.go index b45304716..347766c87 100644 --- a/cmd/acra-server/common/prometheus.go +++ b/cmd/acra-server/common/prometheus.go @@ -19,11 +19,12 @@ package common import ( "sync" + "github.com/prometheus/client_golang/prometheus" + "github.com/cossacklabs/acra/cmd" "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/network" "github.com/cossacklabs/acra/utils" - "github.com/prometheus/client_golang/prometheus" ) const ( @@ -54,6 +55,8 @@ func RegisterMetrics(serviceName string, version *utils.Version, edition utils.P prometheus.MustRegister(connectionCounter) prometheus.MustRegister(connectionProcessingTimeHistogram) base.RegisterAcraStructProcessingMetrics() + base.RegisterEncryptionDecryptionProcessingMetrics() + base.RegisterTokenizationProcessingMetrics() base.RegisterDbProcessingMetrics() cmd.RegisterVersionMetrics(serviceName, version) cmd.RegisterBuildInfoMetrics(serviceName, edition) diff --git a/cmd/acra-translator/common/prometheus.go b/cmd/acra-translator/common/prometheus.go index 89e674201..1efd49d8a 100644 --- a/cmd/acra-translator/common/prometheus.go +++ b/cmd/acra-translator/common/prometheus.go @@ -19,12 +19,14 @@ package common import ( "context" "errors" + "sync" + + "github.com/prometheus/client_golang/prometheus" + "github.com/cossacklabs/acra/cmd" "github.com/cossacklabs/acra/decryptor/base" tokenCommon "github.com/cossacklabs/acra/pseudonymization/common" "github.com/cossacklabs/acra/utils" - "github.com/prometheus/client_golang/prometheus" - "sync" ) const ( @@ -92,6 +94,8 @@ func RegisterMetrics(serviceName string) { prometheus.MustRegister(connectionProcessingTimeHistogram) prometheus.MustRegister(RequestProcessingTimeHistogram) base.RegisterAcraStructProcessingMetrics() + base.RegisterEncryptionDecryptionProcessingMetrics() + base.RegisterTokenizationProcessingMetrics() version, err := utils.GetParsedVersion() if err != nil { panic(err) @@ -122,28 +126,52 @@ func NewPrometheusServiceWrapper(service ITranslatorService, metricType string) func (wrapper *prometheusWrapper) Decrypt(ctx context.Context, acraStruct, clientID, additionalContext []byte) ([]byte, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, decryptOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.Decrypt(ctx, acraStruct, clientID, additionalContext) + decrypted, err := wrapper.ITranslatorService.Decrypt(ctx, acraStruct, clientID, additionalContext) + if err != nil { + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraStruct).Inc() + return nil, err + } + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraStruct).Inc() + return decrypted, nil } // Encrypt AcraStruct using ClientID func (wrapper *prometheusWrapper) Encrypt(ctx context.Context, data, clientID, additionalContext []byte) ([]byte, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, encryptOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.Encrypt(ctx, data, clientID, additionalContext) + encrypted, err := wrapper.ITranslatorService.Encrypt(ctx, data, clientID, additionalContext) + if err != nil { + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraStruct).Inc() + return nil, err + } + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraStruct).Inc() + return encrypted, nil } // EncryptSearchable generate AcraStruct using ClientID and searchable hash func (wrapper *prometheusWrapper) EncryptSearchable(ctx context.Context, data, clientID, additionalContext []byte) (SearchableResponse, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, encryptSearchableOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.EncryptSearchable(ctx, data, clientID, additionalContext) + encrypted, err := wrapper.ITranslatorService.EncryptSearchable(ctx, data, clientID, additionalContext) + if err != nil { + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraStructSearch).Inc() + return SearchableResponse{}, err + } + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraStructSearch).Inc() + return encrypted, nil } // DecryptSearchable decrypt AcraStruct using ClientID and then verify hash func (wrapper *prometheusWrapper) DecryptSearchable(ctx context.Context, data, hash, clientID, additionalContext []byte) ([]byte, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, decryptSearchableOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.DecryptSearchable(ctx, data, hash, clientID, additionalContext) + decrypted, err := wrapper.ITranslatorService.DecryptSearchable(ctx, data, hash, clientID, additionalContext) + if err != nil { + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraStructSearch).Inc() + return nil, err + } + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraStructSearch).Inc() + return decrypted, nil } // GenerateQueryHash generates searchable hash for data @@ -157,40 +185,79 @@ func (wrapper *prometheusWrapper) GenerateQueryHash(ctx context.Context, data, c func (wrapper *prometheusWrapper) Tokenize(ctx context.Context, data interface{}, dataType tokenCommon.TokenType, clientID, additionalContext []byte) (interface{}, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, tokenizeOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.Tokenize(ctx, data, dataType, clientID, additionalContext) + tokenized, err := wrapper.ITranslatorService.Tokenize(ctx, data, dataType, clientID, additionalContext) + if err != nil { + base.AcraTokenizationCounter.WithLabelValues(base.LabelStatusFail, dataType.String()).Inc() + return nil, err + } + + base.AcraTokenizationCounter.WithLabelValues(base.LabelStatusSuccess, dataType.String()).Inc() + return tokenized, nil } // Detokenize data from request according to TokenType using ClientID func (wrapper *prometheusWrapper) Detokenize(ctx context.Context, data interface{}, dataType tokenCommon.TokenType, clientID, additionalContext []byte) (interface{}, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, detokenizeOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.Detokenize(ctx, data, dataType, clientID, additionalContext) + detokenized, err := wrapper.ITranslatorService.Detokenize(ctx, data, dataType, clientID, additionalContext) + if err != nil { + base.AcraDetokenizationCounter.WithLabelValues(base.LabelStatusFail, dataType.String()).Inc() + return nil, err + } + + base.AcraDetokenizationCounter.WithLabelValues(base.LabelStatusSuccess, dataType.String()).Inc() + return detokenized, nil } // EncryptSymSearchable encrypts data with AcraBlock using ClientID and searchable hash func (wrapper *prometheusWrapper) EncryptSymSearchable(ctx context.Context, data, clientID, additionalContext []byte) (SearchableResponse, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, encryptSymSearchableOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.EncryptSymSearchable(ctx, data, clientID, additionalContext) + encrypted, err := wrapper.ITranslatorService.EncryptSymSearchable(ctx, data, clientID, additionalContext) + if err != nil { + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraBlockSearch).Inc() + return SearchableResponse{}, err + } + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraBlockSearch).Inc() + return encrypted, nil } // DecryptSymSearchable decrypt AcraBlock using ClientID and verify hash func (wrapper *prometheusWrapper) DecryptSymSearchable(ctx context.Context, data, hash, clientID, additionalContext []byte) ([]byte, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, decryptSymSearchableOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.DecryptSymSearchable(ctx, data, hash, clientID, additionalContext) + decrypted, err := wrapper.ITranslatorService.DecryptSymSearchable(ctx, data, hash, clientID, additionalContext) + if err != nil { + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraBlockSearch).Inc() + return nil, err + } + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraBlockSearch).Inc() + return decrypted, nil } // EncryptSym encrypts data with AcraBlock using ClientID func (wrapper *prometheusWrapper) EncryptSym(ctx context.Context, data, clientID, additionalContext []byte) ([]byte, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, encryptSymOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.EncryptSym(ctx, data, clientID, additionalContext) + encrypted, err := wrapper.ITranslatorService.EncryptSym(ctx, data, clientID, additionalContext) + if err != nil { + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraBlock).Inc() + return nil, err + } + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraBlock).Inc() + return encrypted, nil } // DecryptSym decrypts AcraBlock using ClientID func (wrapper *prometheusWrapper) DecryptSym(ctx context.Context, acraBlock, clientID, additionalContext []byte) ([]byte, error) { timer := prometheus.NewTimer(prometheus.ObserverFunc(RequestProcessingTimeHistogram.WithLabelValues(wrapper.metricType, decryptSymOperation).Observe)) defer timer.ObserveDuration() - return wrapper.ITranslatorService.DecryptSym(ctx, acraBlock, clientID, additionalContext) + decrypted, err := wrapper.ITranslatorService.DecryptSym(ctx, acraBlock, clientID, additionalContext) + if err != nil { + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusFail, base.LabelTypeAcraBlock).Inc() + return nil, err + } + + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusSuccess, base.LabelTypeAcraBlock).Inc() + return decrypted, nil } diff --git a/cmd/acra-translator/common/service.go b/cmd/acra-translator/common/service.go index 1fa796a4e..91c8510de 100644 --- a/cmd/acra-translator/common/service.go +++ b/cmd/acra-translator/common/service.go @@ -3,12 +3,14 @@ package common import ( "context" "errors" + + "github.com/sirupsen/logrus" + "github.com/cossacklabs/acra/crypto" "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/hmac" "github.com/cossacklabs/acra/logging" tokenCommon "github.com/cossacklabs/acra/pseudonymization/common" - "github.com/sirupsen/logrus" ) // ITranslatorService interface introduce all supported methods by Acra-Translator @@ -81,7 +83,7 @@ func (service *TranslatorService) Decrypt(ctx context.Context, acraStruct, clien data, decryptErr := service.handler.DecryptWithHandler(handler, acraStruct, dataContext) if decryptErr != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).WithError(decryptErr).Errorln("Can't decrypt AcraStruct") _, _, err = service.poisonDetector.OnColumn(dataCtx, acraStruct) if err != nil { @@ -91,7 +93,7 @@ func (service *TranslatorService) Decrypt(ctx context.Context, acraStruct, clien // don't show users that we found poison record return nil, ErrCantDecrypt } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() return data, nil } @@ -113,7 +115,6 @@ func (service *TranslatorService) Encrypt(ctx context.Context, data, clientID, a id := clientID handler, err := crypto.GetHandlerByEnvelopeID(crypto.AcraStructEnvelopeID) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() logger. WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct). WithError(err). @@ -125,12 +126,10 @@ func (service *TranslatorService) Encrypt(ctx context.Context, data, clientID, a data, encryptErr := service.handler.EncryptWithHandler(handler, id, data) if encryptErr != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).WithError(encryptErr).Errorln("Can't encrypt data") // don't show users that we found poison record return nil, ErrCantEncrypt } - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc() return data, nil } @@ -462,7 +461,7 @@ func (service *TranslatorService) DecryptSym(ctx context.Context, acraBlock, cli decrypted, err := service.handler.DecryptWithHandler(handler, acraBlock, dataContext) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraBlock).WithError(err).Errorln("Can't decrypt AcraBlock") _, _, poisonErr := service.poisonDetector.OnColumn(dataCtx, acraBlock) if poisonErr != nil { @@ -471,5 +470,7 @@ func (service *TranslatorService) DecryptSym(ctx context.Context, acraBlock, cli } return acraBlock, ErrCantDecrypt } + + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() return decrypted, nil } diff --git a/cmd/acra-translator/grpc_api/service.go b/cmd/acra-translator/grpc_api/service.go index 1b7a029c3..439b25882 100644 --- a/cmd/acra-translator/grpc_api/service.go +++ b/cmd/acra-translator/grpc_api/service.go @@ -18,14 +18,15 @@ package grpc_api import ( "errors" + + "github.com/sirupsen/logrus" + "golang.org/x/net/context" + "github.com/cossacklabs/acra/acrablock" "github.com/cossacklabs/acra/cmd/acra-translator/common" - "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/hmac" "github.com/cossacklabs/acra/logging" tokenCommon "github.com/cossacklabs/acra/pseudonymization/common" - "github.com/sirupsen/logrus" - "golang.org/x/net/context" ) // ErrEmptyClientID error used if ClientID required in request but not provided @@ -71,7 +72,6 @@ func (service *TranslatorService) Encrypt(ctx context.Context, request *EncryptR response, err := service.service.Encrypt(ctx, request.Data, request.ClientId, nil) if err != nil { - base.APIEncryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() msg := "Unexpected error with AcraStruct generation" logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantEncryptData).Warningln(msg) return nil, err diff --git a/cmd/acra-translator/http_api/service.go b/cmd/acra-translator/http_api/service.go index 20d6a4cb3..076d4bfcd 100644 --- a/cmd/acra-translator/http_api/service.go +++ b/cmd/acra-translator/http_api/service.go @@ -12,18 +12,19 @@ import ( "reflect" "time" - "github.com/cossacklabs/acra/cmd/acra-translator/common" - "github.com/cossacklabs/acra/decryptor/base" - "github.com/cossacklabs/acra/hmac" - "github.com/cossacklabs/acra/logging" - "github.com/cossacklabs/acra/network" - pseudonymizationCommon "github.com/cossacklabs/acra/pseudonymization/common" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/render" log "github.com/sirupsen/logrus" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" + + "github.com/cossacklabs/acra/cmd/acra-translator/common" + "github.com/cossacklabs/acra/decryptor/base" + "github.com/cossacklabs/acra/hmac" + "github.com/cossacklabs/acra/logging" + "github.com/cossacklabs/acra/network" + pseudonymizationCommon "github.com/cossacklabs/acra/pseudonymization/common" ) // HTTPError store HTTP response status and message @@ -310,13 +311,13 @@ func (service *HTTPService) decryptOld(ctx *gin.Context) { } decryptedStruct, err := service.service.Decrypt(service.ctx, acraStruct, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt AcraStruct") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) ctx.String(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Decrypted AcraStruct") ctx.Render(http.StatusOK, render.Data{Data: decryptedStruct, ContentType: "application/octet-stream"}) return @@ -345,13 +346,11 @@ func (service *HTTPService) encryptOld(ctx *gin.Context) { } decryptedStruct, err := service.service.Encrypt(service.ctx, plaintext, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() msg := fmt.Sprintf("Can't encrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) ctx.String(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc() logger.Infoln("Encrypted data") ctx.Render(http.StatusOK, render.Data{Data: decryptedStruct, ContentType: "application/octet-stream"}) return @@ -409,13 +408,11 @@ func (service *HTTPService) _encrypt(ctx *gin.Context, data []byte) (response en encryptedData, err := service.service.Encrypt(service.ctx, request.Data, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() msg := fmt.Sprintf("Can't encrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantEncryptData).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc() logger.Infoln("Encrypted data") response = encryptionHTTPResponse{Data: encryptedData} return @@ -451,13 +448,13 @@ func (service *HTTPService) _decrypt(ctx *gin.Context, data []byte) (response en decryptedData, err := service.service.Decrypt(service.ctx, request.Data, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Encrypted data") response = encryptionHTTPResponse{Data: decryptedData} return @@ -498,13 +495,11 @@ func (service *HTTPService) _encryptSearchable(ctx *gin.Context, data []byte) (r encryptedData, err := service.service.EncryptSearchable(service.ctx, request.Data, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() msg := fmt.Sprintf("Can't encrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc() logger.Infoln("Encrypted data") response = encryptionHTTPResponse{Data: append(encryptedData.Hash, encryptedData.EncryptedData...)} return @@ -543,13 +538,13 @@ func (service *HTTPService) _decryptSearchable(ctx *gin.Context, data []byte) (r acraStruct := request.Data[len(hashData):] decryptedData, err := service.service.DecryptSearchable(service.ctx, acraStruct, hashData, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Decrypted data") response = encryptionHTTPResponse{Data: decryptedData} return @@ -639,13 +634,11 @@ func (service *HTTPService) _encryptSymSearchable(ctx *gin.Context, data []byte) encryptedData, err := service.service.EncryptSymSearchable(service.ctx, request.Data, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() msg := fmt.Sprintf("Can't encrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantHandleHTTPRequest).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc() logger.Infoln("Encrypted data") response = encryptionHTTPResponse{Data: append(encryptedData.Hash, encryptedData.EncryptedData...)} return @@ -689,13 +682,11 @@ func (service *HTTPService) _decryptSymSearchable(ctx *gin.Context, data []byte) acraStruct := request.Data[len(hashData):] decryptedData, err := service.service.DecryptSymSearchable(service.ctx, acraStruct, hashData, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantHandleHTTPRequest).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() logger.Infoln("Decrypted data") response = encryptionHTTPResponse{Data: decryptedData} return @@ -736,13 +727,11 @@ func (service *HTTPService) _encryptSym(ctx *gin.Context, data []byte) (response encryptedData, err := service.service.EncryptSym(service.ctx, request.Data, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeFail).Inc() msg := fmt.Sprintf("Can't encrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantHandleHTTPRequest).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.EncryptionTypeSuccess).Inc() logger.Infoln("Encrypted data") response = encryptionHTTPResponse{Data: encryptedData} return @@ -778,13 +767,13 @@ func (service *HTTPService) _decryptSym(ctx *gin.Context, data []byte) (response decryptedData, err := service.service.DecryptSym(service.ctx, request.Data, connectionClientID, nil) if err != nil { - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantHandleHTTPRequest).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Decrypted data") response = encryptionHTTPResponse{Data: decryptedData} return diff --git a/crypto/acrastruct.go b/crypto/acrastruct.go index 295cb4e0c..e220dfe92 100644 --- a/crypto/acrastruct.go +++ b/crypto/acrastruct.go @@ -2,13 +2,15 @@ package crypto import ( "fmt" + + "github.com/sirupsen/logrus" + "github.com/cossacklabs/acra/acrastruct" "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/encryptor" "github.com/cossacklabs/acra/encryptor/config" "github.com/cossacklabs/acra/logging" "github.com/cossacklabs/acra/utils" - "github.com/sirupsen/logrus" ) // AcraStructEnvelopeID represent AcraBlock EnvelopeID will be serialized inside CryptoContainer @@ -50,6 +52,7 @@ func (handler AcraStructHandler) Decrypt(data []byte, context *base.DataProcesso privateKeys, err := context.Keystore.GetServerDecryptionPrivateKeys(accessContext.GetClientID()) defer utils.ZeroizePrivateKeys(privateKeys) if err != nil { + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() logger.WithError(err).WithFields( logrus.Fields{ logging.FieldKeyEventCode: logging.EventCodeErrorCantReadKeys, @@ -58,6 +61,8 @@ func (handler AcraStructHandler) Decrypt(data []byte, context *base.DataProcesso Debugln("Probably error occurred because: 1. used not appropriate TLS certificate or acra-server configured with inappropriate --client_id=; 2. forgot to generate keys for your TLS certificate (or with specified client_id); 3. incorrectly configured keystore: incorrect path to folder or Redis database's number") return []byte{}, fmt.Errorf("can't read private key for matched client_id to decrypt AcraStruct: %w", err) } + + base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() return acrastruct.DecryptRotatedAcrastruct(data, privateKeys, nil) } diff --git a/crypto/decryptor.go b/crypto/decryptor.go index 291297d5a..7a1dafc1b 100644 --- a/crypto/decryptor.go +++ b/crypto/decryptor.go @@ -2,10 +2,12 @@ package crypto import ( "context" + + log "github.com/sirupsen/logrus" + "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/logging" - log "github.com/sirupsen/logrus" ) // DecryptHandler implements EnvelopeCallbackHandler as EnvelopeDetector callback for simple decryption processing @@ -37,10 +39,8 @@ func (d DecryptHandler) OnCryptoEnvelope(ctx context.Context, container []byte) logging.FieldKeyEventCode: logging.EventCodeErrorDecryptorCantDecryptBinary, "client_id": string(accessContext.GetClientID()), }).WithError(err).Warningln("Can't decrypt SerializedContainer") - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeFail).Inc() return container, nil } - base.AcrastructDecryptionCounter.WithLabelValues(base.DecryptionTypeSuccess).Inc() return decrypted, nil } diff --git a/crypto/prometheus.go b/crypto/prometheus.go new file mode 100644 index 000000000..f70cd4ab7 --- /dev/null +++ b/crypto/prometheus.go @@ -0,0 +1,44 @@ +package crypto + +import ( + "github.com/cossacklabs/acra/decryptor/base" + "github.com/cossacklabs/acra/encryptor" +) + +// PrometheusContainerHandlerWrapper wraps ContainerHandler with adding prometheus metrics logic +type PrometheusContainerHandlerWrapper struct { + ContainerHandler + containerHandlerType string +} + +// NewPrometheusContainerHandlerWrapper create new ContainerHandler prometheus wrapper +func NewPrometheusContainerHandlerWrapper(handler ContainerHandler, containerHandlerType string) PrometheusContainerHandlerWrapper { + return PrometheusContainerHandlerWrapper{ + ContainerHandler: handler, + containerHandlerType: containerHandlerType, + } +} + +// Decrypt proxy ContainerHandler.Decrypt with prometheus metrics +func (handler PrometheusContainerHandlerWrapper) Decrypt(data []byte, context *base.DataProcessorContext) ([]byte, error) { + decrypted, err := handler.ContainerHandler.Decrypt(data, context) + if err != nil { + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusFail, handler.containerHandlerType).Inc() + return nil, err + } + + base.AcraDecryptionCounter.WithLabelValues(base.LabelStatusSuccess, handler.containerHandlerType).Inc() + return decrypted, nil +} + +// EncryptWithClientID proxy ContainerHandler.EncryptWithClientID with prometheus metrics +func (handler PrometheusContainerHandlerWrapper) EncryptWithClientID(clientID, data []byte, context *encryptor.DataEncryptorContext) ([]byte, error) { + encrypted, err := handler.ContainerHandler.EncryptWithClientID(clientID, data, context) + if err != nil { + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusFail, handler.containerHandlerType).Inc() + return nil, err + } + + base.AcraEncryptionCounter.WithLabelValues(base.LabelStatusSuccess, handler.containerHandlerType).Inc() + return encrypted, nil +} diff --git a/crypto/registry.go b/crypto/registry.go index a2531f18c..af9f41af6 100644 --- a/crypto/registry.go +++ b/crypto/registry.go @@ -2,6 +2,8 @@ package crypto import ( "errors" + + "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/keystore" ) @@ -38,10 +40,13 @@ func InitRegistry(keyStore keystore.ServerKeyStore) error { envelopes: make(map[string]ContainerHandler), handlerIDMap: make(map[byte]ContainerHandler), } - if err := Register(NewAcraBlockHandler()); err != nil { + acraBlockPrometheusHandler := NewPrometheusContainerHandlerWrapper(NewAcraBlockHandler(), base.LabelTypeAcraBlock) + if err := Register(acraBlockPrometheusHandler); err != nil { return err } - return Register(NewAcraStructHandler()) + + acraStructPrometheusHandler := NewPrometheusContainerHandlerWrapper(NewAcraStructHandler(), base.LabelTypeAcraStruct) + return Register(acraStructPrometheusHandler) } // Register public API allows registering other handlers from other packages diff --git a/decryptor/base/prometheus.go b/decryptor/base/prometheus.go index ad66834f3..6f4b45589 100644 --- a/decryptor/base/prometheus.go +++ b/decryptor/base/prometheus.go @@ -1,22 +1,24 @@ package base import ( - "github.com/prometheus/client_golang/prometheus" "sync" -) -// Labels and values about AcraStruct decryptions status -const ( - DecryptionTypeLabel = "status" - DecryptionTypeSuccess = "success" - DecryptionTypeFail = "fail" + "github.com/prometheus/client_golang/prometheus" ) -// Labels and values about data encryption status +// LabelStatus base constants for prometheus metrics const ( - EncryptionTypeLabel = "status" - EncryptionTypeSuccess = "success" - EncryptionTypeFail = "fail" + LabelStatus = "status" + LabelStatusFail = "fail" + LabelStatusSuccess = "success" + + LabelType = "type" + LabelTypeAcraBlock = "acrablock" + LabelTypeAcraStruct = "acrastruct" + LabelTypeAcraBlockSearch = "acrablock_searchable" + LabelTypeAcraStructSearch = "acrastruct_searchable" + + LabelTokenType = "token_type" ) // Labels and values about db type in processing @@ -26,20 +28,52 @@ const ( DecryptionDBMysql = "mysql" ) +// Deprecated Metrics var ( // AcrastructDecryptionCounter collect decryptions count success/failed AcrastructDecryptionCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "acra_acrastruct_decryptions_total", Help: "number of AcraStruct decryptions", - }, []string{DecryptionTypeLabel}) + }, []string{LabelStatus}) // APIEncryptionCounter collect encryptions count success/failed APIEncryptionCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "acra_api_encryptions_total", Help: "number of encryptions data to AcraStruct", - }, []string{EncryptionTypeLabel}) + }, []string{LabelStatus}) +) + +var ( + + // AcraDecryptionCounter collect decryptions count success/failed for type acrablock/acrastruct + AcraDecryptionCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "acra_decryptions_total", + Help: "number of decryptions AcraStruct/AcraBlock", + }, []string{LabelStatus, LabelType}) + + // AcraEncryptionCounter collect encryptions count success/failed for type acrablock/acrastruct + AcraEncryptionCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "acra_encryptions_total", + Help: "number of encryptions AcraStruct/AcraBlock", + }, []string{LabelStatus, LabelType}) + + // AcraTokenizationCounter collect tokenizations count success/failed for token_type + AcraTokenizationCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "acra_tokenizations_total", + Help: "number of tokenizations for token_type", + }, []string{LabelStatus, LabelTokenType}) + + // AcraDetokenizationCounter collect tokenizations count success/failed for token_type + AcraDetokenizationCounter = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "acra_detokenization_total", + Help: "number of detokenizations for token_type", + }, []string{LabelStatus, LabelTokenType}) // ResponseProcessingTimeHistogram collect metrics about response processing time ResponseProcessingTimeHistogram = prometheus.NewHistogramVec(prometheus.HistogramOpts{ @@ -56,8 +90,12 @@ var ( }, []string{DecryptionDBLabel}) ) -var dbRegisterLock = sync.Once{} -var acraStructRegisterLock = sync.Once{} +var ( + dbRegisterLock = sync.Once{} + acraStructRegisterLock = sync.Once{} + encryptionDecryptionRegisterLock = sync.Once{} + tokenizationRegisterLock = sync.Once{} +) // RegisterDbProcessingMetrics register in default prometheus registry metrics related with processing db requests/responses func RegisterDbProcessingMetrics() { @@ -73,5 +111,20 @@ func RegisterAcraStructProcessingMetrics() { prometheus.MustRegister(AcrastructDecryptionCounter) prometheus.MustRegister(APIEncryptionCounter) }) +} + +// RegisterEncryptionDecryptionProcessingMetrics register in default prometheus registry metrics related with AcraBlock/AcraStruct decryption/encryption +func RegisterEncryptionDecryptionProcessingMetrics() { + encryptionDecryptionRegisterLock.Do(func() { + prometheus.MustRegister(AcraDecryptionCounter) + prometheus.MustRegister(AcraEncryptionCounter) + }) +} +// RegisterTokenizationProcessingMetrics register in default prometheus registry metrics related with tokenization/detokenization +func RegisterTokenizationProcessingMetrics() { + tokenizationRegisterLock.Do(func() { + prometheus.MustRegister(AcraTokenizationCounter) + prometheus.MustRegister(AcraDetokenizationCounter) + }) } diff --git a/pseudonymization/data_encoder.go b/pseudonymization/data_encoder.go index 55778d912..b9c97440e 100644 --- a/pseudonymization/data_encoder.go +++ b/pseudonymization/data_encoder.go @@ -2,6 +2,7 @@ package pseudonymization import ( "context" + "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/encryptor" "github.com/cossacklabs/acra/pseudonymization/common" @@ -29,7 +30,15 @@ func (p *TokenProcessor) OnColumn(ctx context.Context, data []byte) (context.Con if ok && columnSetting.IsTokenized() { tokenContext := common.TokenContext{ClientID: accessContext.GetClientID(), AdditionalContext: accessContext.GetAdditionalContext()} data, err := p.tokenizer.Detokenize(data, tokenContext, columnSetting) - return ctx, data, err + if err != nil { + if err != ErrDataTypeMismatch { + base.AcraDetokenizationCounter.WithLabelValues(base.LabelStatusFail, columnSetting.GetTokenType().String()).Inc() + } + return ctx, data, err + } + + base.AcraDetokenizationCounter.WithLabelValues(base.LabelStatusSuccess, columnSetting.GetTokenType().String()).Inc() + return ctx, data, nil } return ctx, data, nil } diff --git a/pseudonymization/queryDataEncryptor.go b/pseudonymization/queryDataEncryptor.go index 9ac2bf8db..be6f451fb 100644 --- a/pseudonymization/queryDataEncryptor.go +++ b/pseudonymization/queryDataEncryptor.go @@ -17,6 +17,7 @@ limitations under the License. package pseudonymization import ( + "github.com/cossacklabs/acra/decryptor/base" configCE "github.com/cossacklabs/acra/encryptor/config" "github.com/cossacklabs/acra/pseudonymization/common" ) @@ -35,7 +36,16 @@ func NewTokenEncryptor(tokenizer *DataTokenizer) (*TokenEncryptor, error) { func (e *TokenEncryptor) EncryptWithClientID(clientID, data []byte, setting configCE.ColumnEncryptionSetting) ([]byte, error) { if setting.IsTokenized() { tokenContext := common.TokenContext{ClientID: clientID} - return e.tokenizer.Tokenize(data, tokenContext, setting) + tokenized, err := e.tokenizer.Tokenize(data, tokenContext, setting) + if err != nil { + if err != ErrDataTypeMismatch { + base.AcraTokenizationCounter.WithLabelValues(base.LabelStatusFail, setting.GetTokenType().String()).Inc() + } + return nil, err + } + + base.AcraTokenizationCounter.WithLabelValues(base.LabelStatusSuccess, setting.GetTokenType().String()).Inc() + return tokenized, nil } return data, nil } diff --git a/tests/test.py b/tests/test.py index e58661329..db6f3a405 100644 --- a/tests/test.py +++ b/tests/test.py @@ -2176,6 +2176,7 @@ def testAcraServer(self): 'acraserver_request_processing_seconds_bucket': {'min_value': 0}, 'acra_acrastruct_decryptions_total': {'min_value': 1}, + 'acra_decryptions_total': {'min_value': 1}, 'acraserver_version_major': {'min_value': 0}, 'acraserver_version_minor': {'min_value': 0}, @@ -2205,6 +2206,7 @@ def testAcraTranslator(self): 'acratranslator_version_patch': {'min_value': 0}, 'acra_acrastruct_decryptions_total': {'min_value': 1}, + 'acra_decryptions_total': {'min_value': 1}, 'acratranslator_build_info': {'min_value': 1}, } From 7cedac84e7f63c4830320b1e2406d14edc9d0d3c Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Tue, 7 Feb 2023 17:09:56 +0000 Subject: [PATCH 2/7] zhars/actualize_prometheus_metrics_collecting Updated CHANGELOG_DEV.md file --- CHANGELOG_DEV.md | 3 +++ decryptor/base/prometheus.go | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_DEV.md b/CHANGELOG_DEV.md index 4dbb1e973..dfcc61408 100644 --- a/CHANGELOG_DEV.md +++ b/CHANGELOG_DEV.md @@ -1,3 +1,6 @@ +# 0.95.0 - 2023-02-07 +- Added collecting new Prometheus metrics for AcraServer/AcraTranslator; + # 0.95.0 - 2023-02-02 - Improve processing int4 values from PostgreSQL with binary format of values diff --git a/decryptor/base/prometheus.go b/decryptor/base/prometheus.go index 6f4b45589..fbbda827b 100644 --- a/decryptor/base/prometheus.go +++ b/decryptor/base/prometheus.go @@ -71,7 +71,7 @@ var ( // AcraDetokenizationCounter collect tokenizations count success/failed for token_type AcraDetokenizationCounter = prometheus.NewCounterVec( prometheus.CounterOpts{ - Name: "acra_detokenization_total", + Name: "acra_detokenizations_total", Help: "number of detokenizations for token_type", }, []string{LabelStatus, LabelTokenType}) From e82e1b5123700aab36c76c315fd94717287996b7 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 8 Feb 2023 19:04:21 +0000 Subject: [PATCH 3/7] zhars/actualize_prometheus_metrics_collecting Fixed after review --- cmd/acra-translator/common/service.go | 5 +- cmd/acra-translator/grpc_api/service.go | 4 ++ cmd/acra-translator/http_api/service.go | 8 +++ pseudonymization/tokenizeQuery.go | 10 +++- .../ee_encryptor_config_prometheus.yaml | 58 +++++++++++++++++++ tests/test.py | 45 ++++++++++++-- 6 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 tests/encryptor_configs/ee_encryptor_config_prometheus.yaml diff --git a/cmd/acra-translator/common/service.go b/cmd/acra-translator/common/service.go index 91c8510de..ed85cffb5 100644 --- a/cmd/acra-translator/common/service.go +++ b/cmd/acra-translator/common/service.go @@ -83,6 +83,7 @@ func (service *TranslatorService) Decrypt(ctx context.Context, acraStruct, clien data, decryptErr := service.handler.DecryptWithHandler(handler, acraStruct, dataContext) if decryptErr != nil { + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).WithError(decryptErr).Errorln("Can't decrypt AcraStruct") _, _, err = service.poisonDetector.OnColumn(dataCtx, acraStruct) @@ -93,6 +94,7 @@ func (service *TranslatorService) Decrypt(ctx context.Context, acraStruct, clien // don't show users that we found poison record return nil, ErrCantDecrypt } + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() return data, nil } @@ -461,6 +463,7 @@ func (service *TranslatorService) DecryptSym(ctx context.Context, acraBlock, cli decrypted, err := service.handler.DecryptWithHandler(handler, acraBlock, dataContext) if err != nil { + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraBlock).WithError(err).Errorln("Can't decrypt AcraBlock") _, _, poisonErr := service.poisonDetector.OnColumn(dataCtx, acraBlock) @@ -470,7 +473,7 @@ func (service *TranslatorService) DecryptSym(ctx context.Context, acraBlock, cli } return acraBlock, ErrCantDecrypt } - + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() return decrypted, nil } diff --git a/cmd/acra-translator/grpc_api/service.go b/cmd/acra-translator/grpc_api/service.go index 439b25882..df8ec4fd9 100644 --- a/cmd/acra-translator/grpc_api/service.go +++ b/cmd/acra-translator/grpc_api/service.go @@ -24,6 +24,7 @@ import ( "github.com/cossacklabs/acra/acrablock" "github.com/cossacklabs/acra/cmd/acra-translator/common" + "github.com/cossacklabs/acra/decryptor/base" "github.com/cossacklabs/acra/hmac" "github.com/cossacklabs/acra/logging" tokenCommon "github.com/cossacklabs/acra/pseudonymization/common" @@ -72,10 +73,13 @@ func (service *TranslatorService) Encrypt(ctx context.Context, request *EncryptR response, err := service.service.Encrypt(ctx, request.Data, request.ClientId, nil) if err != nil { + base.APIEncryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := "Unexpected error with AcraStruct generation" logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorCantEncryptData).Warningln(msg) return nil, err } + + base.APIEncryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() return &EncryptResponse{Acrastruct: response}, nil } diff --git a/cmd/acra-translator/http_api/service.go b/cmd/acra-translator/http_api/service.go index 076d4bfcd..8735656cd 100644 --- a/cmd/acra-translator/http_api/service.go +++ b/cmd/acra-translator/http_api/service.go @@ -311,12 +311,14 @@ func (service *HTTPService) decryptOld(ctx *gin.Context) { } decryptedStruct, err := service.service.Decrypt(service.ctx, acraStruct, connectionClientID, nil) if err != nil { + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt AcraStruct") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) ctx.String(http.StatusUnprocessableEntity, msg) return } + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Decrypted AcraStruct") ctx.Render(http.StatusOK, render.Data{Data: decryptedStruct, ContentType: "application/octet-stream"}) @@ -448,12 +450,14 @@ func (service *HTTPService) _decrypt(ctx *gin.Context, data []byte) (response en decryptedData, err := service.service.Decrypt(service.ctx, request.Data, connectionClientID, nil) if err != nil { + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Encrypted data") response = encryptionHTTPResponse{Data: decryptedData} @@ -538,12 +542,14 @@ func (service *HTTPService) _decryptSearchable(ctx *gin.Context, data []byte) (r acraStruct := request.Data[len(hashData):] decryptedData, err := service.service.DecryptSearchable(service.ctx, acraStruct, hashData, connectionClientID, nil) if err != nil { + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantDecryptAcraStruct).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Decrypted data") response = encryptionHTTPResponse{Data: decryptedData} @@ -767,12 +773,14 @@ func (service *HTTPService) _decryptSym(ctx *gin.Context, data []byte) (response decryptedData, err := service.service.DecryptSym(service.ctx, request.Data, connectionClientID, nil) if err != nil { + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusFail).Inc() msg := fmt.Sprintf("Can't decrypt data") logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTranslatorCantHandleHTTPRequest).Warningln(msg) httpErr = NewHTTPError(http.StatusUnprocessableEntity, msg) return } + //TODO: remove deprecated metrics in 1-2 versions base.AcrastructDecryptionCounter.WithLabelValues(base.LabelStatusSuccess).Inc() logger.Infoln("Decrypted data") response = encryptionHTTPResponse{Data: decryptedData} diff --git a/pseudonymization/tokenizeQuery.go b/pseudonymization/tokenizeQuery.go index 55bf16c5c..51f00655c 100644 --- a/pseudonymization/tokenizeQuery.go +++ b/pseudonymization/tokenizeQuery.go @@ -3,11 +3,12 @@ package pseudonymization import ( "context" + "github.com/sirupsen/logrus" + "github.com/cossacklabs/acra/decryptor/base" queryEncryptor "github.com/cossacklabs/acra/encryptor" "github.com/cossacklabs/acra/encryptor/config" "github.com/cossacklabs/acra/sqlparser" - "github.com/sirupsen/logrus" ) // TokenizeQuery replace tokenized data inside AcraStruct/AcraBlocks and change WHERE conditions to support searchable tokenization @@ -79,6 +80,13 @@ func (encryptor *TokenizeQuery) OnQuery(ctx context.Context, query base.OnQueryO err = queryEncryptor.UpdateExpressionValue(ctx, item.Expr.Right, encryptor.coder, encryptor.getTokenizerDataWithSetting(item.Setting)) if err != nil { + // in case of several Observers registered (TokenizeQuery, HashQuery) + // we might catch pure searchable queries with TokenizeQuery so that data will be returned unchanged as setting is not matched + // we just ignore error to let process next Observer + if err == queryEncryptor.ErrUpdateLeaveDataUnchanged && !item.Setting.IsTokenized() { + return query, false, nil + } + logrus.WithError(err).Debugln("Failed to update expression") return query, false, err } diff --git a/tests/encryptor_configs/ee_encryptor_config_prometheus.yaml b/tests/encryptor_configs/ee_encryptor_config_prometheus.yaml new file mode 100644 index 000000000..02ac99aad --- /dev/null +++ b/tests/encryptor_configs/ee_encryptor_config_prometheus.yaml @@ -0,0 +1,58 @@ +schemas: + - table: test_tokenization_default_client_id + columns: + - id + - nullable + - empty + - token_i32 + - token_i64 + - token_str + - token_bytes + - token_email + encrypted: + - column: token_i32 + token_type: int32 + tokenized: true + - column: token_i64 + token_type: int64 + tokenized: true + - column: token_str + token_type: str + tokenized: true + - column: token_bytes + token_type: bytes + tokenized: true + - column: token_email + token_type: email + tokenized: true + + - table: test_searchable_transparent_encryption + columns: + - id + - specified_client_id + - default_client_id + - number + - raw_data + - nullable + - searchable + - searchable_acrablock + - empty + - token_i32 + - token_i64 + - token_str + - token_bytes + - token_email + - masking + encrypted: + # as a specified client_id we use generated client_id from client certificates from ./ssl/acra-writer/ and /ssl/acra-writer-2/ + - column: specified_client_id + client_id: 12824c1c541a615f428a740770291374a8504f84a0682ab4015bc8e792b1bc8240022c5c9faa8c2111e0076b3b929148f4b801414413edaa800cb42492c20cf7 + + - column: default_client_id + + - column: searchable + searchable: true + + - column: searchable_acrablock + searchable: true + crypto_envelope: acrablock diff --git a/tests/test.py b/tests/test.py index db6f3a405..1a12e9a74 100644 --- a/tests/test.py +++ b/tests/test.py @@ -662,7 +662,8 @@ def test_clearing(self): ssl_context = ssl.create_default_context(cafile=base.TEST_TLS_CA) ssl_context.load_cert_chain(base.TEST_TLS_CLIENT_CERT, base.TEST_TLS_CLIENT_KEY) ssl_context.check_hostname = True - with urlopen('https://localhost:{}/resetKeyStorage'.format(self.ACRASERVER_PORT + 1), context=ssl_context) as response: + with urlopen('https://localhost:{}/resetKeyStorage'.format(self.ACRASERVER_PORT + 1), + context=ssl_context) as response: self.assertEqual(response.status, 200) @@ -2108,6 +2109,33 @@ class TestPrometheusMetrics(AcraTranslatorMixin, BaseTestCase): LOG_METRICS = True # some small value but greater than 0 to compare with metrics value of time of processing MIN_EXECUTION_TIME = 0.0000001 + ENCRYPTOR_CONFIG = base.get_encryptor_config('tests/encryptor_configs/ee_encryptor_config_prometheus.yaml') + + def setUp(self): + super().setUp() + + # init searchable transparent encryption test + self.searchableTransparentTest = TestSearchableTransparentEncryption() + self.searchableTransparentTest.engine_raw = self.engine_raw + self.searchableTransparentTest.engine1 = self.engine1 + self.searchableTransparentTest.engine2 = self.engine2 + self.searchableTransparentTest.encryptor_table = BaseSearchableTransparentEncryption().get_encryptor_table() + base.metadata.create_all(self.engine_raw, [self.searchableTransparentTest.encryptor_table]) + + # init searchable transparent encryption test + self.tokenizationTest = TestTokenization() + self.tokenizationTest.engine_raw = self.engine_raw + self.tokenizationTest.engine1 = self.engine1 + self.tokenizationTest.engine2 = self.engine2 + + def tearDown(self): + self.searchableTransparentTest.tearDown() + base.metadata.drop_all(self.engine_raw, [self.searchableTransparentTest.encryptor_table]) + self.tokenizationTest.tearDown() + + def fork_acra(self, popen_kwargs: dict = None, **acra_kwargs: dict): + acra_kwargs.update(encryptor_config_file=self.ENCRYPTOR_CONFIG) + return super(TestPrometheusMetrics, self).fork_acra(popen_kwargs, **acra_kwargs) def checkMetrics(self, url, labels=None): """ @@ -2159,6 +2187,9 @@ def skip(need_skip): def testAcraServer(self): # run some queries to set some values for counters HexFormatTest.testClientIDRead(self) + + self.tokenizationTest.testTokenizationDefaultClientID() + self.searchableTransparentTest.testSearch() labels = { # TEST_TLS_CLIENT_CERT + TEST_TLS_CLIENT_2_CERT 'acraserver_connections_total': {'min_value': 2}, @@ -2176,12 +2207,17 @@ def testAcraServer(self): 'acraserver_request_processing_seconds_bucket': {'min_value': 0}, 'acra_acrastruct_decryptions_total': {'min_value': 1}, - 'acra_decryptions_total': {'min_value': 1}, 'acraserver_version_major': {'min_value': 0}, 'acraserver_version_minor': {'min_value': 0}, 'acraserver_version_patch': {'min_value': 0}, + 'acra_tokenizations_total': {'min_value': 1}, + 'acra_detokenizations_total': {'min_value': 1}, + + 'acra_encryptions_total': {'min_value': 1}, + 'acra_decryptions_total': {'min_value': 1}, + 'acraserver_build_info': {'min_value': 1}, } self.checkMetrics('http://localhost:{}/metrics'.format( @@ -4576,7 +4612,8 @@ def test_insert_returning(self): 'token_str': random_str(), 'token_bytes': random_bytes(), 'token_email': random_email()} with self.engine1.begin() as connection: if TEST_POSTGRESQL: - result = connection.execute(sa.insert(self.test_table).values(encrypted_row).returning(self.test_table)).fetchall() + result = connection.execute( + sa.insert(self.test_table).values(encrypted_row).returning(self.test_table)).fetchall() elif TEST_MARIADB: # use raw sql due to only sqlalchemy 2.x supports returning for mariadb # TODO use sqlalchemy core after upgrading from 1.x to 2.x version @@ -4585,7 +4622,7 @@ def test_insert_returning(self): result = connection.execute(sa.text( "insert into {} ({}) values ( :nullable, :empty, :token_i32, :token_i64, :token_str, :token_bytes, :token_email) returning {};".format( self.test_table.name, columns, columns)), encrypted_row - ).fetchall() + ).fetchall() else: self.fail("Invalid environment") self.assertEqual(len(result), 1) From c022021f16419ec2202eff9ead4af738f90d21ca Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 8 Feb 2023 19:11:20 +0000 Subject: [PATCH 4/7] zhars/actualize_prometheus_metrics_collecting Added quard check for searchable tokenization observer --- pseudonymization/tokenizeQuery.go | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/pseudonymization/tokenizeQuery.go b/pseudonymization/tokenizeQuery.go index 51f00655c..8cfcc91f9 100644 --- a/pseudonymization/tokenizeQuery.go +++ b/pseudonymization/tokenizeQuery.go @@ -70,6 +70,10 @@ func (encryptor *TokenizeQuery) OnQuery(ctx context.Context, query base.OnQueryO clientSession := base.ClientSessionFromContext(ctx) bindSettings := queryEncryptor.PlaceholderSettingsFromClientSession(clientSession) for _, item := range items { + if !item.Setting.IsTokenized() { + continue + } + rightVal, ok := item.Expr.Right.(*sqlparser.SQLVal) if !ok { logrus.Debugln("expect SQLVal as Right expression for searchable consistent tokenization") @@ -80,13 +84,6 @@ func (encryptor *TokenizeQuery) OnQuery(ctx context.Context, query base.OnQueryO err = queryEncryptor.UpdateExpressionValue(ctx, item.Expr.Right, encryptor.coder, encryptor.getTokenizerDataWithSetting(item.Setting)) if err != nil { - // in case of several Observers registered (TokenizeQuery, HashQuery) - // we might catch pure searchable queries with TokenizeQuery so that data will be returned unchanged as setting is not matched - // we just ignore error to let process next Observer - if err == queryEncryptor.ErrUpdateLeaveDataUnchanged && !item.Setting.IsTokenized() { - return query, false, nil - } - logrus.WithError(err).Debugln("Failed to update expression") return query, false, err } From bcc366a67ab5ca52d6a0307d908252cd8c5cc2f2 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Wed, 8 Feb 2023 19:33:23 +0000 Subject: [PATCH 5/7] zhars/actualize_prometheus_metrics_collecting Added TODO for adding more metrics to track once we support v2 queries for translator in tests --- tests/test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.py b/tests/test.py index 1a12e9a74..b23621e6c 100644 --- a/tests/test.py +++ b/tests/test.py @@ -2224,6 +2224,7 @@ def testAcraServer(self): self.ACRASERVER_PROMETHEUS_PORT), labels) def testAcraTranslator(self): + # TODO: added more metrics tracking when support /v2 translator queries labels = { 'acratranslator_connections_total': {'min_value': 1}, From b0258a80063326ccd75a71b642e767ede69269e0 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Thu, 9 Feb 2023 12:13:23 +0000 Subject: [PATCH 6/7] zhars/actualize_prometheus_metrics_collecting Added quard case for searchable QueryObserver --- hmac/decryptor/hashQuery.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hmac/decryptor/hashQuery.go b/hmac/decryptor/hashQuery.go index f0b88de1d..fb63cb712 100644 --- a/hmac/decryptor/hashQuery.go +++ b/hmac/decryptor/hashQuery.go @@ -20,6 +20,8 @@ import ( "context" "fmt" + "github.com/sirupsen/logrus" + "github.com/cossacklabs/acra/decryptor/base" queryEncryptor "github.com/cossacklabs/acra/encryptor" "github.com/cossacklabs/acra/encryptor/config" @@ -27,7 +29,6 @@ import ( "github.com/cossacklabs/acra/keystore" "github.com/cossacklabs/acra/sqlparser" "github.com/cossacklabs/acra/utils" - "github.com/sirupsen/logrus" ) // HashDecryptStore that used by HashQuery @@ -92,6 +93,10 @@ func (encryptor *HashQuery) OnQuery(ctx context.Context, query base.OnQueryObjec // Now that we have condition expressions, perform rewriting in them. hashSize := []byte(fmt.Sprintf("%d", hmac.GetDefaultHashSize())) for _, item := range items { + if !item.Setting.IsSearchable() { + continue + } + // column = 'value' ===> substring(column, 1, ) = 'value' item.Expr.Left = &sqlparser.SubstrExpr{ Name: item.Expr.Left.(*sqlparser.ColName), From 0fcc5186f9c8f4c0a12bca11c8f48d6cb83ed2f9 Mon Sep 17 00:00:00 2001 From: Artem Zhmaka Date: Thu, 9 Feb 2023 14:40:25 +0000 Subject: [PATCH 7/7] zhars/actualize_prometheus_metrics_collecting Removed unnecessary teardown call --- tests/test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test.py b/tests/test.py index b23621e6c..f6d323f68 100644 --- a/tests/test.py +++ b/tests/test.py @@ -2122,16 +2122,15 @@ def setUp(self): self.searchableTransparentTest.encryptor_table = BaseSearchableTransparentEncryption().get_encryptor_table() base.metadata.create_all(self.engine_raw, [self.searchableTransparentTest.encryptor_table]) - # init searchable transparent encryption test + # init tokenization test self.tokenizationTest = TestTokenization() self.tokenizationTest.engine_raw = self.engine_raw self.tokenizationTest.engine1 = self.engine1 self.tokenizationTest.engine2 = self.engine2 def tearDown(self): - self.searchableTransparentTest.tearDown() base.metadata.drop_all(self.engine_raw, [self.searchableTransparentTest.encryptor_table]) - self.tokenizationTest.tearDown() + super().tearDown() def fork_acra(self, popen_kwargs: dict = None, **acra_kwargs: dict): acra_kwargs.update(encryptor_config_file=self.ENCRYPTOR_CONFIG)