# istio中如何对服务进行角色验证

## envoy 中的证书验证

* combined\_validation\_context

组合的证书验证上下文包含默认的CertificateValidationContext和SDS配置.当SDS服务器返回动态CertificateValidationContext时,动态和默认的CertificateValidationContext都将合并到新的CertificateValidationContext中以进行验证.此合并是通过Message::MergeFrom()完成的,因此动态的CertificateValidationContext会覆盖默认CertificateValidationContext中的单个字段,并将重复的字段连接到默认CertificateValidationContext中,并且逻辑OR应用于布尔字段。

* validation\_context\_sds\_secret\_config

通过SDS API获取验证上下文的配置。

* tls\_certificate\_sds\_secret\_configs

通过SDS API获取TLS证书的配置

* default\_validation\_context

如何验证对等证书。

* match\_subject\_alt\_names

Subject Alternative Name匹配器的可选列表.envoy将验证所提供证书的`Subject Alternative Name`是否与指定的匹配项之一匹配.当证书具有通配符DNS SAN条目时,为了匹配特定的客户端,应在字符串匹配器中将其配置为完全匹配类型.例如,如果证书的DNS SAN条目具有`*.example.com`,则仅允许`api.example.com`,则应按如下所示进行配置。

```yaml
match_subject_alt_names:
  exact: "api.example.com"
```

## 验证下游证书配置

```javascript
{
  "@type": "type.googleapis.com/envoy.api.v2.auth.DownstreamTlsContext", //验证下游,接收请求
  "common_tls_context": {
    "alpn_protocols": [
      "istio-peer-exchange",
      "h2",
      "http/1.1"
    ],
    "tls_certificate_sds_secret_configs": [ //获取证书
      {
        "name": "default",
        "sds_config": {
          "api_config_source": {
            "api_type": "GRPC",
            "grpc_services": [
              {
                "envoy_grpc": {
                  "cluster_name": "sds-grpc"
                }
              }
            ]
          }
        }
      }
    ],
    "combined_validation_context": {  //组合验证规则
      "default_validation_context": {
      },
      "validation_context_sds_secret_config": {  //验证CA
        "name": "ROOTCA",
        "sds_config": {
          "api_config_source": {
            "api_type": "GRPC",
            "grpc_services": [
              {
                "envoy_grpc": {
                  "cluster_name": "sds-grpc"
                }
              }
            ]
          }
        }
      }
    }
  },
  "require_client_certificate": true // Envoy将拒绝没有有效客户端证书的连接。
}
```

## 验证上游证书配置

```javascript
{
  "name": "envoy.transport_sockets.tls",
  "typed_config": {
    "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext", //验证上游,发出请求
    "common_tls_context": {
      "alpn_protocols": [
        "istio-peer-exchange",
        "istio",
        "h2"
      ],
      "tls_certificate_sds_secret_configs": [
        {
          "name": "default",
          "sds_config": {
            "api_config_source": {
              "api_type": "GRPC",
              "grpc_services": [
                {
                  "envoy_grpc": {
                    "cluster_name": "sds-grpc"
                  }
                }
              ]
            }
          }
        }
      ],
      "combined_validation_context": {    //组合验证规则
        "default_validation_context": {
          "match_subject_alt_names": [  SAN匹配器
            {
              "exact": "spiffe://cluster.local/ns/istio-system/sa/default"
            }
          ]
        },
        "validation_context_sds_secret_config": {
          "name": "ROOTCA",
          "sds_config": {
            "api_config_source": {
              "api_type": "GRPC",
              "grpc_services": [
                {
                  "envoy_grpc": {
                    "cluster_name": "sds-grpc"
                  }
                }
              ]
            }
          }
        }
      }
    },
    "sni": "outbound_.14250_._.jaeger-collector-headless.istio-system.svc.cluster.local"
  }
}
```

对于特殊情况,istio中请求某些基础服务是直接透传的,不会根据spiffeid验证

```javascript
{
  "name": "envoy.transport_sockets.tls",
  "typed_config": {
    "@type": "type.googleapis.com/envoy.api.v2.auth.UpstreamTlsContext",
    "common_tls_context": {
      "validation_context": {
        "trusted_ca": {
          "filename": "./var/run/secrets/istio/root-cert.pem"
        },
        "match_subject_alt_names": [  //未经过envoy,域名如下
          {
            "exact": "istiod.istio-system.svc"
          }
        ]
      },
      "alpn_protocols": [
        "h2"
      ],
      "tls_certificate_sds_secret_configs": [
        {
          "name": "default",
          "sds_config": {
            "api_config_source": {
              "api_type": "GRPC",
              "grpc_services": [
                {
                  "envoy_grpc": {
                    "cluster_name": "sds-grpc"
                  }
                }
              ]
            }
          }
        }
      ]
    }
  }
}
```

## pilot agent处理SDS流程

constructProxyConfig 构造proxy config getDNSDomain 根据registry生成域名

istio\_agent.NewAgent 初始化agent

sa.Start(role.Type == model.SidecarProxy, podNamespaceVar.Get()) 开始启动SDS agent,默认为sidecar模式,网关需要启动为route模式

### NewServer

创建SDSserver

对于sidecar模式,将执行initWorkloadSdsService进行grpc server初始化,

initWorkloadSdsService 初始化SDS service

通过s.workloadSds.register(s.grpcWorkloadServer),进行服务注册,需要实现以下接口

```go
type SecretDiscoveryServiceServer interface {
    DeltaSecrets(SecretDiscoveryService_DeltaSecretsServer) error
    // SDS API
    StreamSecrets(SecretDiscoveryService_StreamSecretsServer) error
    // 获取secret
    FetchSecrets(context.Context, *envoy_api_v2.DiscoveryRequest) (*envoy_api_v2.DiscoveryResponse, error)
}
```

### FetchSecrets

FetchSecrets处理单次证书请求 通过s.st.GenerateSecret生成secret并返回,然后调用GenerateSecret,判断请求证书是否为rootca,若不是将调用generateSecret

如果开启了third-part的exchanger,则进行exchanger进行验证,现在支持Google auth 生成csr后通过sendRetriableRequest提交给pilot-discovery, sendRetriableRequest调用CSRSign方法进行证书签发,这里实际上会请求istiod(pilot-discovery)

```go
发送请求到认证中心签发证书
func (c *citadelClient) CSRSign


func (c *istioCertificateServiceClient) CreateCertificate(ctx context.Context, in *IstioCertificateRequest, opts ...grpc.CallOption) 

请求下面的接口进行证书签发
/istio.v1.auth.IstioCertificateService/CreateCertificate"
```

### StreamSecrets

FetchSecrets处理双向通信,具体操作和FetchSecrets类似

### startXDS

创建xdsclient和xdsserver,用于处理envoy请求和请求pilot-discovery

## ca server签发证书流程

### s.maybeCreateCA

查看目录是否有对应的文件,否则生成自签名证书,作为根证书,后续将使用该证书签发证书

### s.startCA

```go
caOpts := &CAOptions{
    TrustDomain: s.environment.Mesh().TrustDomain,
    Namespace:   args.Namespace,
}
```

实际上调用的 s.RunCA

s.RrunCA 调用detectAuthEnv,获取k8s token pay load,内容如下示例

```javascript
{"iss":"kubernetes/serviceaccount","kubernetes.io/serviceaccount/namespace":"istio-system","kubernetes.io/serviceaccount/secret.name":"istiod-service-account-token-h2mxh","kubernetes.io/serviceaccount/service-account.name":"istiod-service-account","kubernetes.io/serviceaccount/service-account.uid":"2f08fe65-6a69-4c2a-881b-8823b90dea60","sub":"system:serviceaccount:istio-system:istiod-service-account"}
```

### caserver.NewWithGRPC

注册以下 Authenticator

* ClientCertAuthenticator

对于VM,允许使用以前颁发的证书进行授权。对于VM,将返回具有从SAN提取的身份的调用方,应为SPIFFE身份。

1. 判断auth type
2. 判断证书链的合法性
3. KubeJWTAuthenticator

1.解析bear token 2. 根据集群名称获取kubeclient 3. 调用tokenreview\.ValidateK8sJwt函数对token进行验证 4. 调用k8s api server token review接口对token进行验证

然后对server进行初始化

## caServer.Run()

注册下列 grpc api 用于处理证书请求 pb.RegisterIstioCertificateServiceServer(grpcServer, s)

## \_IstioCertificateService\_CreateCertificate\_Handler

srv.(IstioCertificateServiceServer).CreateCertificate(ctx, req.(\*IstioCertificateRequest))

实际调用以下方法创建证书 func (s \*Server) CreateCertificate

首先调用s.authenticate(ctx)用于认证客户端身份,该方法调用authn.Authenticate(ctx),也就是上面所注册的验证器进行客户端身份验证,返回caller

```
&Caller{
    AuthSource: AuthSourceIDToken,
    Identities: []string{fmt.Sprintf(identityTemplate, a.trustDomain, callerNamespace, callerServiceAccount)},
}
```

返回的caller主要包含证书的身份用于后续签发时的Subject Alternative Name,格式如下

```
spiffe://cluster.local/ns/foo/sa/httpbin
```

该字段也会用于服务授权

验证成功后

调用s.ca.GetCAKeyCertBundle().GetAll()获取证书链以及跟证书 s.ca.Sign根据csr,subjectIDs,requestedLifetime,对csr进行签发,requestedLifetime默认为24小时

最终调用util.GenCertFromCSR签发证书

![](http://img.rocdu.top/20200729/发布流程.jpg)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://rocdu.gitbook.io/deep-understanding-of-istio/6/4.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
