首页 > 其他分享 >go 云资产凭证管理

go 云资产凭证管理

时间:2023-01-04 11:15:36浏览次数:46  
标签:凭证 nil err 资产 Service syncer Secret go return

目录

go 云资产凭证管理

我们对4个厂商的凭证进行抽象:

TX_CLOUD_SECRET_ID=xxx
TX_CLOUD_SECRET_KEY=xxx
AL_CLOUD_ACCESS_KEY=xx
AL_CLOUD_ACCESS_SECRET=xxx
HW_CLOUD_ACCESS_KEY=xxx
HW_CLOUD_ACCESS_SECRET=xxx
VS_HOST=xxx
VS_USERNAME=xxxx
VS_PASSWORD=xxx

我们定义2中凭证类型

const (
	CrendentialAPIKey CrendentialType = iota
	CrendentialPassword
)

type CrendentialType int

针对vsphere, 需要独立出一个字段: VS_HOST 用来保存 vshpere服务的地址
我们定义secret表,来存储我们通过过程中要使用到的这些密钥:

CREATE TABLE `secret` (
  `id` varchar(64) NOT NULL,
  `create_at` bigint(13) NOT NULL,
  `description` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
  `vendor` tinyint(1) NOT NULL,
  `address` varchar(255) NOT NULL,
  `allow_regions` text NOT NULL,
  `crendential_type` tinyint(1) NOT NULL,
  `api_key` varchar(255) NOT NULL,
  `api_secret` text NOT NULL,
  `request_rate` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_key` (`api_key`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Secret 相关接口定义

定义数据结构

type Secret struct {
	Id       string `json:"id"`        // 全局唯一Id
	CreateAt int64  `json:"create_at"` // 创建时间

	*CreateSecretRequest
}

type CreateSecretRequest struct {
	Description     string          `json:"description" validate:"required,lte=100"` // 描述
	Vendor          resource.Vendor `json:"vendor"`                                  // 厂商
	AllowRegions    []string        `json:"allow_regions"`                           // 允许同步的区域
	CrendentialType CrendentialType `json:"crendential_type"`                        // 凭证类型
	Address         string          `json:"address"`                                 // 服务地址, 云商不用填写
	APIKey          string          `json:"api_key" validate:"required,lte=100"`     // key
	APISecret       string          `json:"api_secret" validate:"required,lte=100"`  // secrete
	RequestRate     int             `json:"request_rate"`                            // 请求速率限制, 默认1秒5个
}

定义接口

type Service interface {
	SecretService
}

type SecretService interface {
	CreateSecret(context.Context, *CreateSecretRequest) (*Secret, error)
	QuerySecret(context.Context, *QuerySecretRequest) (*SecretSet, error)
	DescribeSecret(context.Context, *DescribeSecretRequest) (*Secret, error)
}

全局实例添加该Service

package pkg

import (
	"github.com/infraboard/cmdb/pkg/host"
	"github.com/infraboard/cmdb/pkg/syncer"
)

var (
	Host   host.Service
	Syncer syncer.Service
)

Secret CRUD实现

impl模块里面定义个secret.go, 用于实现SecretService, 具体参考: secret CRUD

然后编写HTTP暴露模块:

func (h *handler) QuerySecret(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	req := syncer.NewQuerySecretRequest()
	set, err := h.service.QuerySecret(r.Context(), req)
	if err != nil {
		response.Failed(w, err)
		return
	}
	response.Success(w, set)
}

func (h *handler) CreateSecret(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	req := syncer.NewCreateSecretRequest()
	if err := request.GetDataFromRequest(r, req); err != nil {
		response.Failed(w, err)
		return
	}

	ins, err := h.service.CreateSecret(r.Context(), req)
	if err != nil {
		response.Failed(w, err)
		return
	}

	response.Success(w, ins)
}

func (h *handler) DescribeSecret(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	req := syncer.NewDescribeSecretRequest(ps.ByName("id"))
	set, err := h.service.DescribeSecret(r.Context(), req)
	if err != nil {
		response.Failed(w, err)
		return
	}

	response.Success(w, set)
}

最后由handler注册:

var (
	api = &handler{}
)

type handler struct {
	service syncer.Service
	log     logger.Logger
}

func (h *handler) Config() error {
	h.log = zap.L().Named("Syncer")
	if pkg.Syncer == nil {
		return fmt.Errorf("dependence service syncer not ready")
	}
	h.service = pkg.Syncer
	return nil
}

func RegistAPI(r *httprouter.Router) {
	api.Config()
	r.POST("/secrets", api.CreateSecret)
	r.GET("/secrets", api.QuerySecret)
	r.GET("/secrets/:id", api.DescribeSecret)
}

注册服务与加载路由

secret 服务注册: cmd/start.go

// 初始化服务层 Ioc初始化
if err := impl.Service.Config(); err != nil {
	return err
}
pkg.Host = impl.Service

// Syncer Service
if err := syncer.Service.Config(); err != nil {
	return err
}
pkg.Syncer = syncer.Service

注册http服务 protocol/http.go

// Start 启动服务
func (s *HTTPService) Start() error {
	// 装置子服务路由
	hostAPI.RegistAPI(s.r)
	syncerAPI.RegistAPI(s.r)
	...
}

Post测试
通过Postman测试接口的使用性

Secret Key加密与脱敏

现在我们的Secret Key都是明文存储的, 为了安全, 我们加密存储,

为了确保安全, Secret在返回的时候,需要做脱敏, 及时是秘文也不应该显示

Secret加密存储

我们采用cbc来实现对称加密: 这是之前封装的模块 cbc加密

为了能区分 原始数据和密码数据,我们给秘文数据加一个前缀,以避免重复加密或者解密

conf/config.go:

const (
	CIPHER_TEXT_PREFIX = "@ciphered@"
)

然后为我们secret对象 添加加解密的方法:

func (s *Secret) EncryptAPISecret(key string) error {
	// 判断文本是否已经加密
	if strings.HasPrefix(s.APISecret, conf.CIPHER_TEXT_PREFIX) {
		return fmt.Errorf("text has ciphered")
	}

	cipherText, err := cbc.Encrypt([]byte(s.APISecret), []byte(key))
	if err != nil {
		return err
	}

	base64Str := base64.StdEncoding.EncodeToString(cipherText)
	s.APISecret = fmt.Sprintf("%s%s", conf.CIPHER_TEXT_PREFIX, base64Str)
	return nil
}

func (s *Secret) DecryptAPISecret(key string) error {
	// 判断文本是否已经是明文
	if !strings.HasPrefix(s.APISecret, conf.CIPHER_TEXT_PREFIX) {
		return fmt.Errorf("text is plan text")
	}

	base64CipherText := strings.TrimPrefix(s.APISecret, conf.CIPHER_TEXT_PREFIX)

	cipherText, err := base64.StdEncoding.DecodeString(base64CipherText)
	if err != nil {
		return err
	}

	planText, err := cbc.Decrypt([]byte(cipherText), []byte(key))
	if err != nil {
		return err
	}

	s.APISecret = string(planText)
	return nil
}

Secret脱敏显示
为secret对象补充一个脱敏方法:

func (s *Secret) Desense() {
	if s.APISecret != "" {
		s.APISecret = "******"
	}
}

对List方法查询出来的数据进行过敏:

	for rows.Next() {
		ins := syncer.NewDefaultSecret()
		...
		ins.Desense()
		set.Add(ins)
	}

针对Get方法, 在API暴露时脱敏:

func (h *handler) DescribeSecret(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	req := syncer.NewDescribeSecretRequest(ps.ByName("id"))
	ins, err := h.service.DescribeSecret(r.Context(), req)
	if err != nil {
		response.Failed(w, err)
		return
	}

	ins.Desense()
	response.Success(w, ins)
}

这样我们系统内部交互时, 通过Describe拿到的数据 依然是可以解密的

参考
CMDB项目源码

标签:凭证,nil,err,资产,Service,syncer,Secret,go,return
From: https://www.cnblogs.com/liwenchao1995/p/17024274.html

相关文章

  • go time的定时器简单总结
    go的标准库中的time包为我们提供了多个定时器的接口,总共分为以下几个:time.After,到了给定的duration的时间时,返回可读chan,也不会阻止程序运行,相当于一个消息通知time.Sle......
  • [Godot] 网络物体的同步
    网络物体的同步方案服务器客户端初始化服务器初始化客户端通知游戏开始 生成网络物体老鼠,分配nid记录老鼠的资源路径  连接服务器客户......
  • Shank's Baby-Step-Giant_Step Algorithm(BSGS)
    解模方程(\(n\)为素数)\[a^x\equivb(\bmodn)\]因为欧拉定理\(a^{\phi(n)}\equiv1(\bmodn)\)(\(n\)为素数)。有\[0\lex\len-1\]设\(m=\sqrt{n+......
  • 如何创建Django项目
    创建Django项目前置条件:已完成Python环境和PyCharm安装|在命令行输入pip命令安装pipinstall-ihttps://pypi.douban.com/simpledjango或指定相应的django版本:......
  • go get / go install 配置系统代理(centos 7)
    在goget或者goinstall时,代理报错,比如proxyconnecttcp:dialtcp:lookup3128:nosuchhostdialtcp:lookup proxy.golang.com.cn on8.8.4.4:53:readudp192......
  • (1055, "'bbs02.app01_category.name' isn't in GROUP BY")
    后台报错:[err]1055--'xxx'isn'tinGROUPBY解决方案:初步判断是数据库(版本?配置?)的问题进入mysql的my.ini配置文件ctrl+f搜索找到字段:ONLY_FULL_GROUP_BY,将其删除......
  • MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(2)-Swagger框架集成
    Swagger是什么?Swagger是一个规范且完整API文档管理框架,可以用于生成、描述和调用可视化的RESTful风格的Web服务。Swagger的目标是对RESTAPI定义一个标准且和语......
  • 解决国内使用Google搜索引擎
    1.下载IGG谷歌访问助手,然后对文件解压,如下图。  2.将此扩展配置到GoogleChrome浏览器中。打开扩展程序  打开开发者模式 将扩展文件拖入此页面 ......
  • Django-restframework
    环境安装与配置DRF需要以下依赖:Python(3.5以上)Django(2.2以上)DRF是以Django子应用的方式提供的,所以我们可以直接利用已有的Django环境而无需重新创建。安装DRF......
  • @AspectJ support (good)
    AspectJ类型匹配的通配符:*:匹配任何数量字符;..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数。+:匹配指定类型的子类型;仅能作为......