spiffe:规范,协议

        SPIFFE标准定义了一种能够跨异构环境和组织边界完成bootstrap以及向服务发布身份ID的规模框架。

        SPIFFE规范标准化了向工作负载( workload) 分配身份、 验证和确认工作负载身份以及工作负载API以检索身份的过程。

        SPIFFE身份包含在SVID( SPIFFE Verifiable Identity Document) 中, SVID规范提供了实现SVID时必须支持的属性的要求。

spire:是spiffe的实现,是工具

        SPIRE是SPIFFE规范的工具链实现, 也是一种参考实现, 它可以跨不同的部署环境在工作负载( 使用mTLS或JWT) 之间建立信任关系, 签发SPIFFE ID和工作负载API以检索工作负载SVID。

        简而言之, SPIFFE是一个可信bootstrapping和identification框架, 它已作为标准提交并被CNCF( 云原生计算基金会) 接受。

        到目前为止, 该标准具有两个主要实现, 分别是SPIRE和Istio Citadel。

workload:Pod中的主程序和子进程。

spiffe给每一个workload分配一个spiffe id来表示身份放在SVID文件中。

workload API:用来给workload请求SVID的接口,spire中用agent来实现的。

    SPIFFE Workload API用于提供使工作负载能够使用的SPIFFE身份,以及基于的身份验证系统的信息和服务。

    SPIFFE Workload API支持任意数量的本地客户端,从而使其能够引导可以访问它的任何进程的身份。

    1、通常, 希望在每个进程被授予某些身份的情况下即可以每个进程为基础分配身份。

    2、为此, SPIFFE Workload API实现必须能够确定调用者的身份;

X.509身份文档格式标识符:

    1、即SPIFFE ID;

    2、与ID关联的私钥, 由当前workload签署数据,以及一个短存活期的用于建立tls通信的X.509格式的数字证书

    3、可信证书集( trust bundle),由workload用于验证其它workload的X.509-SVID;

JWT身份文档格式标识符:

    1、即SPIFFE ID;

    2、JWT Token;

    3、可信证书集, 由workload用于验证其它workload的身份;

    SPIFFE Workload API需要被实现为gRPC流式服务器,以便快速传播更新, 例如证书吊销等。


SPIRE


        SPIRE是SPIFFEAPI的可用于生产的实现,它负责执行node和workload的身份证明,以便根据一组预定义的条件安全地向workload签发SVID,并验证其他workload的SVID。

SPIRE可以用于多种情况,并可以执行多种与身份相关的功能:

    1、服务之间的安全身份验证。

    2、安全地引入Vault和PinterestKnox等secretstores。

    3、为服务网格中的sidecar代理服务提供身份认证的基础设施。

    4、用于分布式系统组件的PKI的供给和轮替。

image.png

SPIRE架构和组件:

一、SPIRE Server: 签名授权机构, 它通过各SPIRE Agent接收请求。

    1、负责管理和发布已配置的SPIFFE可信域中的所有身份;

    2、它存储注册条目( 用于指定确定特定SPIFFE ID的发布条件的选择器) 和签名密钥

    3、使用node attestation自动认证Agent的身份, 并在经过认证的agent请求时为其本地的workload创建SVID

二、SPIRE Agent: 运行于各工作节点, 用于向节点上的workload提供workload API;

    1、从服务器请求SVID并缓存它们, 直到workload请求其SVID

    2、将SPIFFE Workload API暴露给本地节点上的workload, 并负责证明调用它的workload的身份

    3、提供已识别的workload及其SVID

image.png

SPIRE Server:

SPIRE Server高度插件化, 用户需要通过一系列插件来配置其行为

    1、Node attestor插件负责结合各节点上运行的agent node attestor插件一同来验证节点身份;

    2、Node resolver为SPIRE Server提供一系列selector( 选择器) 从而让Server能够基于其它属性来验证节点身份;

    3、Datastore插件被SPIRE Server用于存储、 查询和更新各种信息,例如registration entries,包括哪些节点身份业已得到证明, 以及这些节点各自相关的选择器等 。

        内置的数据存储插件支持使用SQLite 3或PostgresSQL作为存储后端。
        默认情况下, 它使用SQLite 3

    4、Key manager插件负责控制服务器如何存储用于签署X.509-SVID和JWT-SVID的私钥。
5、默认情况下, SPIRE Server充当其自己的证书颁发机构, 必要时也可以使用Upstream certificate authority (CA) 插件来使用来自不同PKI系统的不同CA。

image.png

SPIRE Agent:

SPIREAgent需要运行于每个运行有workload的节点上,并负责以下任务:

    1、从SPIREServer请求SVID并缓存它们,直到workload请求其SVID

    2、将SPIFFEWorkloadAPI暴露给节点上的workload,并证明调用它的workload的身份

    3、提供已识别的workload及其SVID

SPIREAgent同样基于组合配置一系列插件完成其工作:

    1、Nodeattestor插件同Server端的nodeattestor插件一起完成对agent程序所在节点的身份验证。

    2、Workloadattestor插件通过从节点操作系统查询相关进程的信息,并将其与使用selector注册workload属性时提供给server的信息进行比较,从而验证节点上workload进程的身份。

    3、Keymanager插件,负责让agent用来为发布给workload的X.509-SVID生成私钥。

示例:

image.png

front-envoy.yaml

node:
  id: "front-envoy"
  cluster: "front-envoy"

admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  secrets:
  - name: server_cert
    tls_certificate:
      certificate_chain:
        filename: "/etc/envoy/certs/server.crt"
      private_key:
        filename: "/etc/envoy/certs/server.key"

  listeners:
  - name: listener_http
    address:
      socket_address: { address: 0.0.0.0, port_value: 80 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          codec_type: AUTO
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: backend
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                redirect:
                  https_redirect: true
                  port_redirect: 443
          http_filters:
          - name: envoy.router
            typed_config: {}
  - name: listener_https
    address:
      socket_address: { address: 0.0.0.0, port_value: 443 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_https
          codec_type: AUTO
          route_config:
            name: https_route
            virtual_hosts:
            - name: https_route
              domains: ["*"]
              routes:
              - match:
                  prefix: "/service/gray"
                route:
                  cluster: service-gray
              - match:
                  prefix: "/service/purple"
                route:
                  cluster: service-purple
              - match:
                  prefix: "/"
                route:
                  cluster: mycluster
          http_filters:
          - name: envoy.router
            typed_config: {}
      tls_context:
        common_tls_context:
          tls_certificate_sds_secret_configs:
          - name: server_cert

  clusters:
  - name: spire_agent
    connect_timeout: 0.25s
    http2_protocol_options: {}
    hosts:
      - pipe:
          path: /tmp/agent.sock

  - name: mycluster
    connect_timeout: 0.25s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    http2_protocol_options: {}
    load_assignment:
      cluster_name: mycluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: myservice
                port_value: 80

  - name: service-gray
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: ROUND_ROBIN
    hosts:
      - socket_address:
          address: service-gray
          port_value: 443
    tls_context:
      common_tls_context:
        tls_certificate_sds_secret_configs:
          - name: "spiffe://ilinux.io/front-envoy" # spiffe id 要唯一
            sds_config:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: spire_agent
        combined_validation_context:
          # validate the SPIFFE ID of the server
          default_validation_context:
            verify_subject_alt_name:
              - "spiffe://ilinux.io/service-gray"
          validation_context_sds_secret_config:
            name: "spiffe://ilinux.io"
            sds_config:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: spire_agent

  - name: service-purple
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: ROUND_ROBIN
    hosts:
      - socket_address:
          address: service-purple
          port_value: 443
    tls_context:
      common_tls_context:
        tls_certificate_sds_secret_configs:
          - name: "spiffe://ilinux.io/front-envoy"
            sds_config:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: spire_agent
        combined_validation_context:
          # validate the SPIFFE ID of the server
          default_validation_context:
            verify_subject_alt_name:
              - "spiffe://ilinux.io/service-purple"
          validation_context_sds_secret_config:
            name: "spiffe://ilinux.io"
            sds_config:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: spire_agent

spire-server.conf

server {
	bind_address = "0.0.0.0"
	bind_port = "9081"
	registration_uds_path = "/tmp/spire-registration.sock"
	trust_domain = "ilinux.io"
	data_dir = "/opt/spire/data/server"
	plugin_dir = "/opt/spire/conf/server/plugin"
	log_level = "DEBUG"
	log_file = "/dev/stdout"
	upstream_bundle = false
	svid_ttl = "1h"
	ca_subject = {
		Country = ["CN"],
		Organization = ["SPIFFE"],
		CommonName = "",
	}
}

plugins {
	DataStore "sql" {
		plugin_data {
			database_type = "sqlite3"
				connection_string = "/opt/spire/data/server/datastore.sqlite3"
		}
	}
	NodeAttestor "x509pop" {
		plugin_data {
			ca_bundle_path = "/opt/spire/conf/server/agent-cacert.pem"
		}
	}

	NodeResolver "noop" {
		plugin_data {}
	}

	KeyManager "disk" {
		plugin_data = {
			keys_path = "/opt/spire/data/server/keys.json"
		}
	}
}

service-purple:

service-purple spire-agent.conf

agent {
	data_dir = "/opt/spire/data/agent"
	log_level = "DEBUG"
	log_file = "/dev/stdout"
	plugin_dir = "/opt/spire/conf/agent/plugin"
	server_address = "spire-server"
	server_port = "9081"
	socket_path ="/tmp/agent.sock"
	trust_bundle_path = "/opt/spire/conf/agent/bootstrap.crt"
	trust_domain = "ilinux.io"
	enable_sds = true
}

plugins {
	NodeAttestor "x509pop" {
		plugin_data {
			private_key_path = "/opt/spire/conf/agent/agent.key.pem"
			certificate_path = "/opt/spire/conf/agent/agent.crt.pem"
		}
	}
	KeyManager "disk" {
		plugin_data {
			directory = "/opt/spire/data/agent"
		}
	}
	WorkloadAttestor "unix" {
		plugin_data {
		}
	}
}

service-purple-envoy.yaml:

node:
  id: "service-purple"
  cluster: "service-purple"

admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901

static_resources:
  listeners:
  - name: listener_http
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          codec_type: auto
          stat_prefix: ingress_http
          route_config:
            name: local_route
            virtual_hosts:
            - name: service
              domains:
              - "*"
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: local_service
          http_filters:
          - name: envoy.router
            typed_config: {}

  - name: listener_https
    address:
      socket_address: { address: 0.0.0.0, port_value: 443 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
          stat_prefix: ingress_https
          codec_type: AUTO
          route_config:
            name: https_route
            virtual_hosts:
            - name: https_route
              domains: ["*"]
              routes:
              - match:
                  prefix: "/"
                route:
                  cluster: local_service
          http_filters:
          - name: envoy.router
            typed_config: {}
      tls_context:
        common_tls_context:
          tls_certificate_sds_secret_configs:
          - name: "spiffe://ilinux.io/service-purple"
            sds_config:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: spire_agent
          # obtain the trust bundle from SDS
          validation_context_sds_secret_config:
              name: "spiffe://ilinux.io"
              sds_config:
                api_config_source:
                  api_type: GRPC
                  grpc_services:
                    envoy_grpc:
                      cluster_name: spire_agent

  clusters:
  - name: spire_agent
    connect_timeout: 0.25s
    http2_protocol_options: {}
    hosts:
      - pipe:
          path: /tmp/agent.sock

  - name: local_service
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    load_assignment:
      cluster_name: local_service
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 8080