Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]EdegxFoundry:运行再边缘侧开源的,厂商中立的,灵活可定制的,支持交互操作的软件平台。可与设备、传感器、执行器和其他物联网对象的物理设备进行交互。简单地说,EdgeX是一种边缘中间件平台,服务于信息系统和IOT设备的感知操作(有关edgex的更多介绍,可查看edgex的的官方文档Introduction-EdgeXFoundryDocumentation)。Edegx官方提供了基于docker,可快速启动dockercompose文件,让用户可以快速体验到edgex提…

大家好,又见面了,我是你们的朋友全栈君。

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]


Edegx Foundry: 运行在边缘侧开源的,厂商中立的,灵活可定制的,支持交互操作的软件平台。可与设备、传感器、执行器和其他物联网对象的物理设备进行交互。简单地说,EdgeX是一种边缘中间件平台,服务于信息系统 和 IOT设备的感知操作(有关edgex的更多介绍,可查看edgex的的官方文档Introduction – EdgeX Foundry Documentation)。

Edegx 官方提供了基于docker,可快速启动docker compose 文件,让用户可以快速体验到edgex提供的功能 。edgexfoundry/edgex-compose: EdgeX Foundry Docker Compose release compose files and tools for building EdgeX compose files (github.com) 。

如果仅限于体验或者使用Edgex 那么基于docker compose模式已经足够。

edgex 模块众多,基于微服务的架构部署运行。如果想深入了解Edgex的运行架构或者基于Edgex进行定制开发,以及进行edgex的高可用部署。就必要了解一下Edgex模块间的运行逻辑和关系,尤其是edgex 的security模式。

下图是edgex 的 docker-compose.ym定义的模块间依赖关系。(依赖关系,而非启动顺序,docker compose 的depends_on不能保证容器中微服务的启动顺序)

    command: /app-service-configurable -cp=consul.http://edgex-core-consul:8500 --registry
      --confdir=/res
    container_name: edgex-app-rules-engine
    depends_on:
    - consul
    - data
    - security-bootstrapper
    entrypoint:
    - /edgex-init/ready_to_run_wait_install.sh

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

本系列文章将介绍edgex security模式下和安全相关的几个模块。 不会过于侧重源码分析,更多的是关于模块间的依存关系的分析介绍。

security-bootstrapper

注意上文图中的依赖关系,使用的时docker的声明式定义depends_on,这个依赖能够管理到的粒度是容器级别的依赖,而并不能保证edgex的各个子模块的微服务的启动顺序,如依赖的http 服务是否已经完成启动,依赖的文件是否创建成功等等,edgex提供了对这些依赖进行check的校验工具security-bootstrapper。Edgex的 ADR  更加详细的介绍了edgex的启动过程的设计思路。

        这个模块是edgex提供的工具模块之一。 按照上图分析,edgex微服务模块之间是有一些依赖关系的。edgex 使用此模块提供几个辅助功,帮助edgex实现各个微服务,等待其依赖服务启动之后再启动之前。

对应的源码仓库 .(本文后续没有特殊说明,那么文件位置都是在这个代码仓库下的相对位置)

edgexfoundry

源码位置:

cmd/security-bootstrapper/main.go

security-bootstrapper 也可以启动一个微服务(gate命令选项),但不属于edgex对外提供的服务,主要是为其他微服务模块提供一个类似看门的机制,供其他微服务再启动时进行check,来确认security-bootstrapper 已经完成了启动。

除gate命令外,其他命令主要是在edgex的启动脚本entrypoint-scripts  中使用。edgex security模式下所有的微服务都是通过 entrypoint-scripts 脚本来启动的。关于entrypoint-scripts的介绍,在每个模块单独分析吧。

security-bootstrapper的 DockerFile (cmd/security-bootstrapper/Dockerfile) 定义了其启动命令选项 gate .

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

举个docker-compose 中的demo

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

Please specify command for security-bootstrapper.exe
Usage: security-bootstrapper.exe [options] <command> [arg...]
Options:
    -h, --help    Show this message
    --confdir     Specify local configuration directory

Commands:
    gate              Do security bootstrapper gating on stages while starting services
    genPassword       Generate a random password
    getHttpStatus     Do an HTTP GET call to get the status code
    help              Show available commands (this text)
    listenTcp         Start up a TCP listener
    pingPgDb          Test Postgres database readiness
    setupRegistryACL  Set up registry's ACL and configure the access
    waitFor           Wait for the other services with specified URI(s) to connect:
                      the URI(s) can be communication protocols like tcp/tcp4/tcp6/http/https or files

每一个命令选项对应的源码位置

internal/security/bootstrapper/command/{option}/command.go   

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

 命令选项

gate:

 gate选项主要有三个阶段,在configuartion.toml中定义了每个阶段的行为

  1. 发布start信号(监听端口,StartPort:54324)
  2. 等待基础服务完成启动(redis postgre consul)
  3. 发布一个Ready信号(监听端口 ToRunPort 54329)

      Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

func (c *cmd) Execute() (statusCode int, err error) {
	c.loggingClient.Infof("Security bootstrapper running %s", CommandName)

	bootstrapServer := tcp.NewTcpServer()
	c.loggingClient.Debugf("init phase: attempts to start up the listener on bootstrap host: %s, port: %d",
		c.config.StageGate.BootStrapper.Host, c.config.StageGate.BootStrapper.StartPort)

	// in a separate go-routine so it won't block the main thread execution
     //==========================================================
    //==========================================================
    // 1 启动 端口 start port
     //==========================================================
    //==========================================================
	go openGatingSemaphorePort(bootstrapServer, c.config.StageGate.BootStrapper.StartPort, c.loggingClient,
		"Raising bootstrap semaphore for secure bootstrapping")

	// wait on for others to be done: each of tcp dialers is a blocking call
    //==========================================================
    //==========================================================
    // 2.1 等待consul
    //==========================================================
    //==========================================================
	c.loggingClient.Debug("Waiting on dependent semaphores required to raise the ready-to-run semaphore ...")
	if err := tcp.DialTcp(
		c.config.StageGate.Registry.Host,
		c.config.StageGate.Registry.ReadyPort,
		c.loggingClient); err != nil {
		retErr := fmt.Errorf("found error while waiting for readiness of Registry at %s:%d, err: %v",
			c.config.StageGate.Registry.Host, c.config.StageGate.Registry.ReadyPort, err)
		return interfaces.StatusCodeExitWithError, retErr
	}
	c.loggingClient.Info("Registry is ready")
     //==========================================================
    //==========================================================
    // 2.2 等待postgre启动
     //==========================================================
    //==========================================================
	if err := tcp.DialTcp(
		c.config.StageGate.KongDB.Host,
		c.config.StageGate.KongDB.ReadyPort,
		c.loggingClient); err != nil {
		retErr := fmt.Errorf("found error while waiting for readiness of KongDB at %s:%d, err: %v",
			c.config.StageGate.KongDB.Host, c.config.StageGate.KongDB.ReadyPort, err)
		return interfaces.StatusCodeExitWithError, retErr
	}
	c.loggingClient.Info("KongDB is ready")
     //==========================================================
    //==========================================================
    // 2.3 等待redis启动
     //==========================================================
    //==========================================================
	if err := tcp.DialTcp(
		c.config.StageGate.Database.Host,
		c.config.StageGate.Database.ReadyPort,
		c.loggingClient); err != nil {
		retErr := fmt.Errorf("found error while waiting for readiness of Database at %s:%d, err: %v",
			c.config.StageGate.Database.Host, c.config.StageGate.Database.ReadyPort, err)
		return interfaces.StatusCodeExitWithError, retErr
	}
	c.loggingClient.Info("Database is ready")

	// Reached ready-to-run phase
	c.loggingClient.Debugf("ready-to-run phase: attempts to start up the listener on ready-to-run port: %d",
		c.config.StageGate.Ready.ToRunPort)

	readyToRunServer := tcp.NewTcpServer()
     //==========================================================
    //==========================================================
    // 3 发布启动完成信号 监听(ToRunPort )
     //==========================================================
    //==========================================================
	go openGatingSemaphorePort(readyToRunServer, c.config.StageGate.Ready.ToRunPort, c.loggingClient,
		"Raising ready-to-run semaphore for secure bootstrapping")

......
.....
}

genPassword:

func (c *cmd) Execute() (int, error) {
	c.loggingClient.Infof("Security bootstrapper running %s", CommandName)

	randomBytes := make([]byte, randomBytesLength)
	_, err := rand.Read(randomBytes) // all of salt guaranteed to be filled if err==nil
	if err != nil {
		return interfaces.StatusCodeExitWithError, err
	}

	randPass := base64.StdEncoding.EncodeToString(randomBytes)
	// output the randPass to stdout
	fmt.Fprintln(os.Stdout, randPass)

	return interfaces.StatusCodeExitNormal, nil
}

        主要作用生成随机的的密码,逻辑比较简单,生成一个随机的33位的byte,然后计算其base64

getHttpStatus

        获取指定的http服务的状态,比简单就忽略了

listenTcp

        启动一个tcp监听

func (c *cmd) Execute() (int, error) {
	c.loggingClient.Infof("Security bootstrapper running %s", CommandName)

	tcpServer := tcp.NewTcpServer()

	// block and listening forever until internal error
	if err := tcpServer.StartListener(c.tcpPort, c.loggingClient, c.tcpHost); err != nil {
		return interfaces.StatusCodeExitWithError, err
	}

	return interfaces.StatusCodeExitNormal, nil
}

pingPgDb

        这是一个专门为检测kong的数据库状态的命令,kong支持的数据库不止是postgre。比简单就忽略了

setupRegistryACL

        命令选项中最复杂的一个操作。在consul的启动脚本中使用。

        截取一段consul_wait_install.sh (cmd/security-bootstrapper/entrypoint-scripts/consul_wait_install.sh)

docker-entrypoint.sh agent \
  -ui \
  -bootstrap \
  -server \
  -config-file=/edgex-init/consul-bootstrapper/config_consul_acl.json \
  -client 0.0.0.0 &
# wait for the secretstore tokens ready as we need the token for bootstrapping
echo "$(date) Executing waitFor on Consul with waiting on TokensReadyPort \
  tcp://${STAGEGATE_SECRETSTORESETUP_HOST}:${STAGEGATE_SECRETSTORESETUP_TOKENS_READYPORT}"
/edgex-init/security-bootstrapper --confdir=/edgex-init/res waitFor \
  -uri tcp://"${STAGEGATE_SECRETSTORESETUP_HOST}":"${STAGEGATE_SECRETSTORESETUP_TOKENS_READYPORT}" \
  -timeout "${STAGEGATE_WAITFOR_TIMEOUT}"

# we don't want to exit out the whole Consul process when ACL bootstrapping failed, just that
# Consul won't have ACL to be used
set +e
# call setupRegistryACL bootstrapping command, containing both ACL bootstrapping and re-configure consul access steps
/edgex-init/security-bootstrapper --confdir=/edgex-init/res setupRegistryACL

源码位置:

这里不要着急看源码,需要先理解下valut 的 consul Secret engines 功能。 在下面有链接。看懂了 consul Secret engines功能这段代码就不难理解了。代码就不贴了,比较长。

internal/security/bootstrapper/command/setupacl/command.go

这个命令选项主要做了以下几个事情

1 因为edgex启用了acl控制,所以在consul完成启动后需要生成acl的最高权限的token(bootstrap token),所以第一件事情就是生成consul的  bootstrap token 。edgex是通过rest 接口生成的这个token

http://edgex-core-consul:8500/v1/acl/bootstrap

2 开启vault的 consul Secret engines 功能 (关于这块的介绍可以单独起一篇介绍了)

需要注意的是在执行这个命令之前,consul会检查vault已经完成了启动,这个在后面分析consul启动过程时会在介绍。

vault 官方文档

命令行操作     

 Consul – Secrets Engines | Vault by HashiCorp (vaultproject.io)

Rest Api        

Consul – Secrets Engines – HTTP API | Vault by HashiCorp (vaultproject.io)

consul 官方文档

consul acl

在vault 中托管consul的access token 生成策略,及生成策略在cosul中ACL权限

vault进行consul访问密钥的管理可查看官方文档

简单来说就是因为consul开启ACL之后,访问consul也时需要access token的,consul的申请acl token 需要用到管理权限的token去申请(management token)。因为management token有很高的权限, 暴露出去必然会带来很大的安全风险。

curl \
    --header "X-Consul-Token: my-management-token" \
    --request PUT \
    --data '{"Name": "sample", "Type": "management"}' \
    https://consul.rocks/v1/acl/create

所以vault 提供了consul secret engine 功能,在vault中管理consul 的management token,同时配置生成consul access token(ACL) 的规则,在vault中这些规则通过role来进行管理。

应用在需要访问consul时,通过调用vault的api 来获得一个访问consul的access token。

如此通过vault 来管理consul 的access token。

配置完成后访问vault ui可以在 secrets Engines 下看到consul相关的配置

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

3 保存consul的 bootsrap token 到文件

BootstrapTokenPath

Edgex foundry(Ireland-2.0版本)- Security 模式启动过程分析security-bootstrapper[通俗易懂]

4 edgex将consul的ACL规则放到了代码里面

internal/security/bootstrapper/command/setupacl/aclpolicies.go

const (
	// edgeXPolicyRules are rules for edgex services
	// in Phase 2, we will use the same policy for all EdgeX services
	// TODO: phase 3 will have more finer grained policies for each service
	edgeXPolicyRules = `
	# HCL definition of server agent policy for EdgeX
	agent "" {
		policy = "read"
	}
	agent_prefix "edgex" {
		policy = "write"
	}
	node "" {
  		policy = "read"
	}
	node_prefix "edgex" {
		policy = "write"
	}
	service "" {
		policy = "write"
	}
	service_prefix "" {
		policy = "write"
	}
	# allow key value store put
	# once the default_policy is switched to "deny",
	# this is needed if wants to allow updating Key/Value configuration
	key "" {
		policy = "write"
	}
	key_prefix "" {
		policy = "write"
	}
	`

	// edgeXServicePolicyName is the name of the agent policy for edgex
	edgeXServicePolicyName = "edgex-service-policy"

	consulCreatePolicyAPI     = "/v1/acl/policy"
	consulReadPolicyByNameAPI = "/v1/acl/policy/name/%s"

	aclNotFoundMessage = "ACL not found"
)

waitFor

       这个操作是所有命令选项中唯一的阻塞操作,会一直等待指定的资源可以用之后返回,也是在entrypoint-scripts脚本中使用最多多的命令选项

等待的资源对象支持,tcp端口,文件,http服务支持等待超时参数

func (c *cmd) waitForDependencies() error {
	dependencyChan := make(chan struct{})
	waitErr := make(chan error, 1)

	go func() {
		for _, uri := range c.parsedURIs {
			c.loggingClient.Infof("Waiting for: [%s] with timeout: [%s]", uri.String(), c.timeout.String())

			switch uri.Scheme {
			case "file":
				c.waitForFile(uri)
			case "tcp", "tcp4", "tcp6":
				c.waitForSocket(uri.Scheme, uri.Host)
			case "unix":
				c.waitForSocket(uri.Scheme, uri.Path)
			case "http", "https":
				c.waitForHTTP(uri)
			default:
				waitErr <- fmt.Errorf("invalid host protocol provided: %s. supported protocols are: file, tcp, tcp4, "+
					"tcp6, unix and http", uri.Scheme)
				return
			}
		}

		c.waitGroup.Wait()
		close(dependencyChan)
	}()

	select {
	case err := <-waitErr:
		return err
	case <-dependencyChan:
		break
	case <-time.After(c.timeout):
		return fmt.Errorf("Timeout after %s waiting on dependencies to become available: %v", c.timeout, c.uris)
	}

	return nil
}

后面将会更新的内容

  1. Vault

  2. security-secretstore-setup

  3. secrets-config & proxy-setup

  4. kong db

  5. kong

  6. consul

  7. Redis db

  8. 核心模块

  9. windows 下的edgex开发环境

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/162200.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Java学习之文件io流篇

    Java学习之文件io流篇0x00前言在平时的一些开发中,普遍都会让脚本运行的时候,去存储一些脚本运行结果的数据,例如开发一个爬虫,这时候我们爬取下来的内容,就需要存储到本地,那么这时候就会用到

    2021年12月12日
  • ie兼容性视图在哪里设置win10_ie11兼容性视图设置

    ie兼容性视图在哪里设置win10_ie11兼容性视图设置在访问一些网站时被告知只能使用IE浏览器进行访问,我个人更新了win11的预览版本之后更是在系统中找不到IE的踪迹,edge中的“兼容性视图”选项并没有直接在设置出显示出来,于是一番努力后我发现了在edge中使用IE兼容性视图的设置方法。一、打开“设置”在edge右上角“…”选项中选择“设置”打开。二、设置“InternetExplorer模式”在设置页面的左侧选择“默认浏览器”,右侧将“允许在InternetExplorer模式下重新加载网站”的下拉选择由“默认值”改为“允许

  • 数据库的唯一索引_数据库唯一索引是什么

    数据库的唯一索引_数据库唯一索引是什么唯一索引是不允许表中任何两行具有相同索引值的索引。  当现有的数据中存在重复的键值时,大多数数据库不允许把新创建的唯一索引与表一起保存。数据库还可能防止添加将在表中创建重复键值的新数据。主键索引数据库表经常有一列或列组合,其值唯一标识表中的每一行。该列称为表的主键。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键

  • 微信多开(免费版)_wechat国际版

    微信多开(免费版)_wechat国际版惊喜!!!放在前面在我发布文章这一天,下午打开wechaty博客的时候发现重磅:绕过登录限制,wechaty免费版web协议重新荣光这篇文章,可以完美实现无需token即可实现微信登录效果及代

  • swing58_ML2437A

    swing58_ML2437A给定一个长度为 n 的整数序列,初始时序列为 {1,2,…,n−1,n}。序列中的位置从左到右依次标号为 1∼n。我们用 [l,r] 来表示从位置 l 到位置 r 之间(包括两端点)的所有数字构成的子序列。现在要对该序列进行 m 次操作,每次操作选定一个子序列 [l,r],并将该子序列中的所有数字进行翻转。例如,对于现有序列 1 3 2 4 6 5 7,如果某次操作选定翻转子序列为 [3,6],那么经过这次操作后序列变为 1 3 5 6 4 2 7。请你求出经过 m 次操作后的序列。输入格式第

  • 树莓派4b支持5gwifi吗_树莓派4和4b的区别

    树莓派4b支持5gwifi吗_树莓派4和4b的区别树莓派4b与Manjaro,安装、配置、修复WiFi频段5G和CountryCode安装Manjaro到树莓派4b下载Manjaro烧录系统到SD卡并启动修复无线网络5G频段更新软件仓库安装缺失的功能安装Manjaro到树莓派4bManjaroLinux(或简称Manjaro)是基于ArchLinux的Linux发行版,使用Xfce、GNOME和KDEPlasma作为默认桌面环境,和Arch一样,采用滚动更新。其目标是为PC提供易于使用的自由的操作系统。Manjaro

    2022年10月20日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号