Skip to content

Commit

Permalink
配置加密支持选择加密算法 (#1140)
Browse files Browse the repository at this point in the history
Co-authored-by: raywlwang <raywlwang@tencent.com>
  • Loading branch information
alexwanglei and raywlwang committed May 31, 2023
1 parent 019e335 commit 90b39c8
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 89 deletions.
46 changes: 31 additions & 15 deletions cache/config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,35 @@ type Entry struct {
Content string
Md5 string
Version uint64
DataKey string
Tags []*model.ConfigFileTag
// 创建的时候,设置过期时间
ExpireTime time.Time
// 标识是否是空缓存
Empty bool
}

func (e *Entry) GetDataKey() string {
for _, tag := range e.Tags {
if tag.Key == utils.ConfigFileTagKeyDataKey {
return tag.Value
}
}
return ""
}

func (e *Entry) GetEncryptAlgo() string {
for _, tag := range e.Tags {
if tag.Key == utils.ConfigFileTagKeyEncryptAlgo {
return tag.Value
}
}
return ""
}

func (e *Entry) Encrypted() bool {
return e.GetDataKey() != ""
}

// newFileCache 创建文件缓存
func newFileCache(ctx context.Context, storage store.Store) FileCache {
cache := &fileCache{
Expand Down Expand Up @@ -240,9 +262,9 @@ func (fc *fileCache) GetOrLoadIfAbsent(namespace, group, fileName string) (*Entr
// 从数据库中加载数据
atomic.AddInt32(&fc.loadCnt, 1)

file, dataKey, err := fc.getConfigFileReleaseAndDataKey(namespace, group, fileName)
file, tags, err := fc.getConfigFileReleaseAndTags(namespace, group, fileName)
if err != nil {
configLog.Error("[Config][Cache] load config file release error.",
configLog.Error("[Config][Cache] load config file release and tags error.",
zap.String("namespace", namespace),
zap.String("group", group),
zap.String("fileName", fileName),
Expand Down Expand Up @@ -270,7 +292,7 @@ func (fc *fileCache) GetOrLoadIfAbsent(namespace, group, fileName string) (*Entr
Content: file.Content,
Md5: file.Md5,
Version: file.Version,
DataKey: dataKey,
Tags: tags,
ExpireTime: fc.getExpireTime(),
}

Expand All @@ -289,16 +311,16 @@ func (fc *fileCache) GetOrLoadIfAbsent(namespace, group, fileName string) (*Entr
return newEntry, nil
}

func (fc *fileCache) getConfigFileReleaseAndDataKey(
namespace, group, fileName string) (*model.ConfigFileRelease, string, error) {
func (fc *fileCache) getConfigFileReleaseAndTags(
namespace, group, fileName string) (*model.ConfigFileRelease, []*model.ConfigFileTag, error) {
file, err := fc.storage.GetConfigFileRelease(nil, namespace, group, fileName)
if err != nil {
configLog.Error("[Config][Cache] load config file release error.",
zap.String("namespace", namespace),
zap.String("group", group),
zap.String("fileName", fileName),
zap.Error(err))
return nil, "", err
return nil, nil, err
}
tags, err := fc.storage.QueryTagByConfigFile(namespace, group, fileName)
if err != nil {
Expand All @@ -307,15 +329,9 @@ func (fc *fileCache) getConfigFileReleaseAndDataKey(
zap.String("group", group),
zap.String("fileName", fileName),
zap.Error(err))
return nil, "", err
}
var dataKey string
for _, tag := range tags {
if tag.Key == utils.ConfigFileTagKeyDataKey {
dataKey = tag.Value
}
return nil, nil, err
}
return file, dataKey, nil
return file, tags, nil
}

// Remove 删除缓存对象
Expand Down
2 changes: 1 addition & 1 deletion config/bootstrap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ func assembleConfigFile() *apiconfig.ConfigFile {

func assembleEncryptConfigFile() *apiconfig.ConfigFile {
configFile := assembleConfigFile()
configFile.IsEncrypted = utils.NewBoolValue(true)
configFile.Encrypted = utils.NewBoolValue(true)
configFile.EncryptAlgo = utils.NewStringValue("AES")
return configFile
}
Expand Down
100 changes: 64 additions & 36 deletions config/client_config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,10 @@ func (s *Server) GetConfigFileForClient(ctx context.Context,

// 从缓存中获取配置内容
entry, err := s.fileCache.GetOrLoadIfAbsent(namespace, group, fileName)

if err != nil {
log.Error("[Config][Service] get or load config file from cache error.",
zap.String("requestId", requestID),
zap.Error(err))

return api.NewConfigClientResponseWithMessage(
apimodel.Code_ExecuteException, "load config file error")
}
Expand Down Expand Up @@ -94,41 +92,15 @@ func (s *Server) GetConfigFileForClient(ctx context.Context,
zap.String("file", fileName),
zap.Uint64("version", entry.Version))

// 加密配置返回用公钥加密的数据密钥
if entry.DataKey != "" && publicKey != "" {
dataKeyBytes, err := base64.StdEncoding.DecodeString(entry.DataKey)
if err != nil {
log.Error("[Config][Service] base64 decode data key error.",
zap.String("requestId", requestID),
zap.String("dataKey", entry.DataKey),
zap.Error(err))
}
cipherDataKey, err := rsa.EncryptToBase64(dataKeyBytes, publicKey)
if err != nil {
log.Error("[Config][Service] rsa encrypt data key error.",
zap.String("requestId", requestID),
zap.String("dataKey", entry.DataKey),
zap.Error(err))
}
resp := utils2.GenConfigFileResponse(namespace, group, fileName,
entry.Content, entry.Md5, entry.Version, true, cipherDataKey)
log.Info("[Config][Client] client get config file resp.",
configFile, err := transferEntry2APIModel(client, entry)
if err != nil {
log.Error("[Config][Service] transfer entry to api model error.",
zap.String("requestId", requestID),
zap.String("file", resp.GetConfigFile().GetFileName().GetValue()),
zap.String("dataKey", resp.GetConfigFile().GetDataKey().GetValue()),
zap.Bool("isEncrypted", resp.GetConfigFile().GetIsEncrypted().GetValue()))
return resp
zap.Error(err))
return api.NewConfigClientResponseWithMessage(
apimodel.Code_ExecuteException, "transfer entry to api model error")
}
isEntrypted := entry.DataKey != ""
resp := utils2.GenConfigFileResponse(namespace, group, fileName,
entry.Content, entry.Md5, entry.Version, isEntrypted, "")

log.Info("[Config][Client] client get config file resp.",
zap.String("requestId", requestID),
zap.String("file", resp.GetConfigFile().GetFileName().GetValue()),
zap.String("dataKey", resp.GetConfigFile().GetDataKey().GetValue()),
zap.Bool("isEncrypted", resp.GetConfigFile().GetIsEncrypted().GetValue()))
return resp
return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, configFile)
}

// CreateConfigFileFromClient 调用config_file接口获取配置文件
Expand Down Expand Up @@ -211,9 +183,65 @@ func (s *Server) doCheckClientConfigFile(ctx context.Context, configFiles []*api
}

if compartor(configFile, entry) {
return utils2.GenConfigFileResponse(namespace, group, fileName, "", entry.Md5, entry.Version, false, "")
return utils2.GenConfigFileResponse(namespace, group, fileName, "", entry.Md5, entry.Version)
}
}

return api.NewConfigClientResponse(apimodel.Code_DataNoChange, nil)
}

func transferEntry2APIModel(client *apiconfig.ClientConfigFileInfo,
entry *cache.Entry) (*apiconfig.ClientConfigFileInfo, error) {
namespace := client.GetNamespace().GetValue()
group := client.GetGroup().GetValue()
fileName := client.GetFileName().GetValue()
publicKey := client.GetPublicKey().GetValue()

configFile := &apiconfig.ClientConfigFileInfo{
Namespace: utils.NewStringValue(namespace),
Group: utils.NewStringValue(group),
FileName: utils.NewStringValue(fileName),
Content: utils.NewStringValue(entry.Content),
Version: utils.NewUInt64Value(entry.Version),
Md5: utils.NewStringValue(entry.Md5),
Encrypted: utils.NewBoolValue(entry.Encrypted()),
}
for _, tag := range entry.Tags {
if tag.Key != utils.ConfigFileTagKeyDataKey && tag.Key != utils.ConfigFileTagKeyEncryptAlgo {
configFile.Tags = append(configFile.Tags, &apiconfig.ConfigFileTag{
Key: utils.NewStringValue(tag.Key),
Value: utils.NewStringValue(tag.Value),
})
}
}

dataKey := entry.GetDataKey()
encryptAlgo := entry.GetEncryptAlgo()
if dataKey != "" && publicKey != "" {
dataKeyBytes, err := base64.StdEncoding.DecodeString(dataKey)
if err != nil {
log.Error("[Config][Service] base64 decode data key error.",
zap.String("dataKey", dataKey),
zap.Error(err))
return nil, err
}
cipherDataKey, err := rsa.EncryptToBase64(dataKeyBytes, publicKey)
if err != nil {
log.Error("[Config][Service] rsa encrypt data key error.",
zap.String("dataKey", dataKey),
zap.Error(err))
}
configFile.Tags = append(configFile.Tags,
&apiconfig.ConfigFileTag{
Key: utils.NewStringValue(utils.ConfigFileTagKeyDataKey),
Value: utils.NewStringValue(cipherDataKey),
},
&apiconfig.ConfigFileTag{
Key: utils.NewStringValue(utils.ConfigFileTagKeyEncryptAlgo),
Value: utils.NewStringValue(encryptAlgo),
},
)
return configFile, nil
}
return configFile, nil
}
4 changes: 2 additions & 2 deletions config/config_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (s *Server) CreateConfigFile(ctx context.Context, configFile *apiconfig.Con
}

// 配置加密
if configFile.IsEncrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" {
if configFile.Encrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" {
if err := s.encryptConfigFile(ctx, configFile, configFile.EncryptAlgo.GetValue(), ""); err != nil {
log.Error("[Config][Service] encrypt config file error.",
utils.ZapRequestID(requestID),
Expand Down Expand Up @@ -112,7 +112,7 @@ func (s *Server) CreateConfigFile(ctx context.Context, configFile *apiconfig.Con

retConfigFile := transferConfigFileStoreModel2APIModel(createdFile)

if configFile.IsEncrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" {
if configFile.Encrypted.GetValue() && configFile.EncryptAlgo.GetValue() != "" {
if err := s.decryptConfigFile(ctx, retConfigFile); err != nil {
log.Error("[Config][Service] decrypt config file error.",
utils.ZapRequestID(requestID),
Expand Down
37 changes: 21 additions & 16 deletions config/config_file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ func TestConfigFileCRUD(t *testing.T) {

t.Run("step12-create-entrypted", func(t *testing.T) {
configFile := assembleConfigFile()
configFile.IsEncrypted = utils.NewBoolValue(true)
configFile.Encrypted = utils.NewBoolValue(true)
configFile.EncryptAlgo = utils.NewStringValue("AES")
rsp := testSuit.testService.CreateConfigFile(testSuit.defaultCtx, configFile)
assert.Equal(t, api.ExecuteSuccess, rsp.Code.GetValue())
Expand Down Expand Up @@ -768,30 +768,35 @@ func TestServer_GetConfigFileBaseInfo(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

Convey("获取配置文件", t, func() {
Convey("获取配置文件基本信息", t, func() {
Convey("存储层-查询配置文件-返回error", func() {
storage := storemock.NewMockStore(ctrl)
storage.EXPECT().GetConfigFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("mock storage error"))
testSuit.testServer.storage = storage

configFile := assembleConfigFile()
got := testSuit.testService.GetConfigFileBaseInfo(testSuit.defaultCtx, configFile.Namespace.Value, configFile.Group.Value, configFile.Name.Value)
So(apimodel.Code_StoreLayerException, ShouldEqual, apimodel.Code(got.GetCode().GetValue()))
})

Convey("解密配置文件-返回error", func() {
storage := storemock.NewMockStore(ctrl)
storage.EXPECT().GetConfigFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(&model.ConfigFile{
CreateBy: operator,
}, nil)
storage.EXPECT().QueryTagByConfigFile(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return([]*model.ConfigFileTag{{Key: utils.ConfigFileTagKeyDataKey, Value: "abc"}}, nil)
testSuit.testServer.storage = storage

crypto := &aes.AESCrypto{}
encryptFunc := ApplyMethod(reflect.TypeOf(crypto), "Decrypt", func(_ *aes.AESCrypto, plaintext string, key []byte) (string, error) {
return "", errors.New("mock encrypt error")
})
defer encryptFunc.Reset()

configFile := assembleEncryptConfigFile()
testSuit.defaultCtx = context.WithValue(testSuit.defaultCtx, utils.ContextUserNameKey, configFile.CreateBy.GetValue())
got := testSuit.testService.CreateConfigFile(testSuit.defaultCtx, configFile)
configFile := assembleConfigFile()
got := testSuit.testService.GetConfigFileBaseInfo(testSuit.defaultCtx, configFile.Namespace.Value, configFile.Group.Value, configFile.Name.Value)
So(apimodel.Code_DecryptConfigFileException, ShouldEqual, apimodel.Code(got.GetCode().GetValue()))
})

Convey("存储层-查询配置文件-返回error", func() {
storage := storemock.NewMockStore(ctrl)
storage.EXPECT().GetConfigFileGroup(gomock.Any(), gomock.Any()).AnyTimes().Return(&model.ConfigFileGroup{}, nil)
storage.EXPECT().GetConfigFile(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes().Return(nil, errors.New("mock storage error"))
testSuit.testServer.storage = storage

configFile := assembleConfigFile()

got := testSuit.testService.CreateConfigFile(testSuit.defaultCtx, configFile)
So(apimodel.Code_StoreLayerException, ShouldEqual, apimodel.Code(got.GetCode().GetValue()))
})
})
}
16 changes: 7 additions & 9 deletions config/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,14 @@ func CheckContentLength(content string) error {

// GenConfigFileResponse 为客户端生成响应对象
func GenConfigFileResponse(namespace, group, fileName, content, md5str string,
version uint64, isEncrypted bool, dataKey string) *apiconfig.ConfigClientResponse {
version uint64) *apiconfig.ConfigClientResponse {
configFile := &apiconfig.ClientConfigFileInfo{
Namespace: utils.NewStringValue(namespace),
Group: utils.NewStringValue(group),
FileName: utils.NewStringValue(fileName),
Content: utils.NewStringValue(content),
Version: utils.NewUInt64Value(version),
Md5: utils.NewStringValue(md5str),
IsEncrypted: utils.NewBoolValue(isEncrypted),
DataKey: utils.NewStringValue(dataKey),
Namespace: utils.NewStringValue(namespace),
Group: utils.NewStringValue(group),
FileName: utils.NewStringValue(fileName),
Content: utils.NewStringValue(content),
Version: utils.NewUInt64Value(version),
Md5: utils.NewStringValue(md5str),
}
return api.NewConfigClientResponse(apimodel.Code_ExecuteSuccess, configFile)
}
Expand Down
2 changes: 1 addition & 1 deletion config/watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (wc *watchCenter) notifyToWatchers(publishConfigFile *model.ConfigFileRelea
}

response := utils2.GenConfigFileResponse(publishConfigFile.Namespace, publishConfigFile.Group,
publishConfigFile.FileName, "", publishConfigFile.Md5, publishConfigFile.Version, false, "")
publishConfigFile.FileName, "", publishConfigFile.Md5, publishConfigFile.Version)

watcherMap := watchers.(*sync.Map)
watcherMap.Range(func(clientId, watchCtx interface{}) bool {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ require (
require (
github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/agiledragon/gomonkey/v2 v2.10.1
github.com/polarismesh/specification v1.3.2-alpha.1
github.com/polarismesh/specification v1.3.2-alpha.2
)

require gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219 h1:XnFyNUWnciM6zgXaz6tm+Egs35rhoD0KGMmKh4gCdi0=
github.com/polarismesh/go-restful-openapi/v2 v2.0.0-20220928152401-083908d10219/go.mod h1:4WhwBysTom9Eoy0hQ4W69I0FmO+T0EpjEW9/5sgHoUk=
github.com/polarismesh/specification v1.3.2-alpha.1 h1:+VWCtValTedIrNYaOPhpT8Lt1At+mhimUs9yj0Darsc=
github.com/polarismesh/specification v1.3.2-alpha.1/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU=
github.com/polarismesh/specification v1.3.2-alpha.2 h1:cMghyvCnRVM5ca2kYCGHOgIIxVnokiMvw0720q8a8RA=
github.com/polarismesh/specification v1.3.2-alpha.2/go.mod h1:rDvMMtl5qebPmqiBLNa5Ps0XtwkP31ZLirbH4kXA0YU=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
Expand Down

0 comments on commit 90b39c8

Please sign in to comment.