Back
Featured image of post ArgoCD插件之vault-plugin使用趟坑记录

ArgoCD插件之vault-plugin使用趟坑记录

背景

最近尝试将家里的k3s集群部署方式统一一下,于是盘点了一下当前使用的多种混合的部署方式:

  • 原生k8s manifest
  • 基于kustomize部署
  • 基于helm部署
  • 基于helmfile部署

因为部署方式的多样性,导致CD(持续部署)脚本写起来不是太方便,有一些并没有被很好的自动化管理起来。正凑之前粗看了一下ArgoCD,倒不如借此实践一番。在真正迁移之前,要做一些技术验证:先尝试把敏感信息的管理方案跑一下。

以下是原先基于helmfile可以直接引用vault的机密信息。比如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 安装dnspod webhook
- name: cert-manager-webhook-dnspod
  namespace: cert-manager
  # 每次下载太慢,直接放仓库吧
  chart: ./../../charts/cert-manager-webhook-dnspod
  needs:
    - cert-manager
  set:
    - name: groupName 
      value: "mywebsit.com"
  values:
    - values.yaml
    - secrets:
        apiID: "{{ fetchSecretValue "ref+vault://tencentcloud-kv/dnspod#/id" }}"
        apiToken: "{{ fetchSecretValue "ref+vault://tencentcloud-kv/dnspod#/token" }}"

这里因为apiID/apiToken是涉及机密的,我们不想在代码仓库中出现明文的“密码“类型的机密信息的话,就要像这样引用一种机密管理方案。我本身觉得Helmfile是挺好用,只是因为不统一,而且基于对ArgoCD的UI和理念认同,于是得试下ArgoCD的解法。

如何正确安装argocd-vault-plugin插件?

我们用关键词很容易找到argocd-valut-plugin插件。并且推荐使用sidecar container模式,这里可跳到已经准备好的一键部署清单中(真贴心啊)。 这个清单有如下几个文件:

  • argocd-repo-server.yaml 基于init container下载插件以及定义了三个Sidecar,用于三种类型的资源。
  • cmp-plugin.yaml 定义了三种类型资源的插件,它的相关命令,可关注generate部分。
  • kustomization.yaml kustomize文件,不必多言。

请不要着急直接部署,要调整下先!!!不然会有几个坑,可否容我一一道来?

Sidecar所需ENV未设置

如果你直接部署,并且发现vault在部署时并未生效,查看argocd-repo-server容器可能有如下错误日志:

1
2
3
4
5
6
7
8
9
time="2023-08-15T06:44:40Z" level=error msg="finished unary call with code Unknown" error="plugin sidecar failed. error generating manifests in cmp: rpc error: code = Unknown desc = error generating manifests: `sh -c \"kustomize build . | argocd-vault-plugin generate -\"` failed exit status 1: Error: Must provide a supported Vault Type, received 
Usage:
  argocd-vault-plugin generate <path> [flags]

Flags:
  -c, --config-path string         path to a file containing Vault configuration (YAML, JSON, envfile) to use
  -h, --help                       help for generate
  -s, --secret-name string         name of a Kubernetes Secret in the argocd namespace containing Vault configuration data in the argocd namespace of your ArgoCD host (Only available when used in ArgoCD). The namespace can be overridden by using the format <namespace>:<name>
      --verbose-sensitive-output   enable verbose mode for detailed info to help with debugging. Includes sensitive data (credentials), logged to stderr" grpc.code=Unkno

以上关键信息是:Error: Must provide a supported Vault Type,显然是因为我们没有正确传递插件所需要的参数(如VAULT地址,类型等)。

官方文档提中到三种注入方式:

  • k8s secret
  • config file
  • env var

我试了一下ENV VAR完全没问题,但这种用法显然不符合我们的诉求(在插件中要显式传递这些环境变量太过烦琐!)。从使用上我感觉最合适简单的应该是k8s secret的方式,也方便更新。我们可准备一个secret,比如名为vault-configuration,内容如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
kind: Secret
apiVersion: v1
metadata:
  name: vault-configuration
  namespace: argocd
type: Opaque
data:
  VAULT_ADDR: "base64 of your vault addr"
  AVP_AUTH_TYPE: "YXBwcm9sZQ=="
  AVP_ROLE_ID: "your-role-id"
  AVP_SECRET_ID: "your-secret-id"
  AVP_TYPE: "dmF1bHQ="

注意所有内容需要base64编码。 上面的环境变量视你VAULT的认证方式而定,详细可查看上面文档,我是基于AppRole的认证。把这个文件apply到集群(argocd名字空间下)后,我们就准备好了要插件要使用的配置了。

注:以上这个内容也涉及机密,它可手动部署,或由CI/CD过程结合CD的敏感信息管理(如Github Action的Secrets and variables)来部署它,在代码仓库中不必完整存在。

插件所引用的脚本未修改

上面文档中提到过,我们如果有Secret的话,可以使用如argocd-vault-plugin generate /some/path -s vault-configuration 来调用插件。你直接查看最开始官方提供的关于cmp-plugin.yaml文件,会发现它默认是不支持使用Secret作为配置的,可以按如下修改(展示Diff部分):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
diff --git a/manifests/cmp-sidecar/cmp-plugin.yaml b/manifests/cmp-sidecar/cmp-plugin.yaml
index 19d5aab..fbbaf9f 100644
--- a/manifests/cmp-sidecar/cmp-plugin.yaml
+++ b/manifests/cmp-sidecar/cmp-plugin.yaml
@@ -25,7 +25,7 @@ data:
         command:
           - sh
           - "-c"
-          - "kustomize build . | argocd-vault-plugin generate -"
+          - "kustomize build . | argocd-vault-plugin -s vault-configuration generate -"
       lockRepo: false
   avp-helm.yaml: |
     ---
@@ -53,7 +53,7 @@ data:
           - "-c"
           - |
             helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE ${ARGOCD_ENV_HELM_ARGS} . |
-            argocd-vault-plugin generate -
+            argocd-vault-plugin -s vault-configuration generate -
       lockRepo: false
   avp.yaml: |
     apiVersion: argoproj.io/v1alpha1
@@ -71,7 +71,11 @@ data:
       generate:
         command:
           - argocd-vault-plugin
+          - "-s"
+          - "vault-configuration"
           - generate
           - "."
       lockRepo: false

现在你可以部署以上资源了,可检查集群中ConfigMap资源argocd/cmp-plugin是否已经刷新为我们改后的状态。

Secret还有权限要设置

如果你兴高彩烈准备用起来了,会发现还有个错误等着你:

1
Error: secrets \"vault-configuration\" is forbidden: User \"system:serviceaccount:argocd:argocd-repo-server\" cannot get resource \"secrets\" in API group \"\" in the namespace \"argocd\"

这就涉及k8s的RBAC机制了,我们只需要给argocd-repo-server授权即可:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: argocd
  name: secret-reader
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get", "watch", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secrets
  namespace: argocd
subjects:
  - kind: ServiceAccount
    name: argocd-repo-server
    namespace: argocd
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

将创建一个角色(Role),该角色拥有在argocd命名空间中读取 secrets 资源的权限,并将该角色绑定到 argocd-repo-server 服务账户上。将以上YAML代码保存到文件中(例如:rbac.yaml),然后运行以下命令: kubectl apply -f rbac.yaml 这样一来,你的服务账号就应该具备了访问 secrets 的权限了。

实际运用

现在你或许真可以用起来了,以下给个示例:

在kustomize中使用

你可以简单测试一下使用方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
apiVersion: v1
kind: Secret
metadata:
  name: vault-example-using-2-ways
  annotations:
    avp.kubernetes.io/path: "tencentcloud-kv/data/dnspod"
stringData:
  res1: <token>
  res3: <path:tencentcloud-kv/data/aksk#access_key>
type: Opaque

以上演示了两种方式访问vault的secret。一种是基于Annotations,另一种是直接全路径指定。

在helm中使用

如果我们想这样写,声明helm chart再传递一串Values给它,好像很不错:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: dnspod-helm
  namespace: argocd
spec:
  destination:
    namespace: cert-manager
    server: https://kubernetes.default.svc
  project: defaultjk
  source:
    repoURL: "https://github.com/qqshfox/cert-manager-webhook-dnspod.git"
    targetRevision: HEAD
    path: "deploy/cert-manager-webhook-dnspod"
    plugin:
      name: argocd-vault-plugin-helm
      env:
        - name: HELM_VALUES
          # 使用vault来保存敏感数据
          value: |
            secrets:
              apiID: <path:tencentcloud-kv/data/dnspod#id>
              apiToken: <path:tencentcloud-kv/data/dnspod#token>
            groupName: "hi.kevin"            
  syncPolicy:
    automated: {} # 如果您希望自动同步,请保留此行

很不幸,再次报错。原因是啥?经历上面几次坑人的洗礼,我们大概知道插件的运作机制了。于是习惯性去查看cmp-plugin.yaml,果然凶手就在你面前,默认的方式是基于${ARGOCD_ENV_HELM_ARGS}来渲染:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  generate:
        # **IMPORTANT**: passing `${ARGOCD_ENV_HELM_ARGS}` effectively allows users to run arbitrary code in the Argo CD 
        # repo-server (or, if using a sidecar, in the plugin sidecar). Only use this when the users are completely trusted. If
        # possible, determine which Helm arguments are needed by your users and explicitly pass only those arguments.
        command:
          - sh
          - "-c"
          - |
            helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE ${ARGOCD_ENV_HELM_ARGS} . |
            argocd-vault-plugin generate -            

我们可以这样调整一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
diff --git a/manifests/cmp-sidecar/cmp-plugin.yaml b/manifests/cmp-sidecar/cmp-plugin.yaml
index 19d5aab..f53d1a3 100644
--- a/manifests/cmp-sidecar/cmp-plugin.yaml
+++ b/manifests/cmp-sidecar/cmp-plugin.yaml
@@ -49,11 +49,11 @@ data:
         # repo-server (or, if using a sidecar, in the plugin sidecar). Only use this when the users are completely trusted. If
         # possible, determine which Helm arguments are needed by your users and explicitly pass only those arguments.
         command:
-          - sh
+          - bash
           - "-c"
           - |
-            helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE ${ARGOCD_ENV_HELM_ARGS} . |
-            argocd-vault-plugin generate -
+            helm template $ARGOCD_APP_NAME -n $ARGOCD_APP_NAMESPACE  -f <(echo "$ARGOCD_ENV_HELM_VALUES") . |
+            argocd-vault-plugin -s vault-configuration generate -
       lockRepo: false
   avp.yaml: |
     apiVersion: argoproj.io/v1alpha1
@@ -71,7 +71,10 @@ data:
:

以上将环境变量中HELM_VALUES传送给helm去渲染,如此这般,上述的“自以为是”才是真的“是”。

一点思考

或许是因为argocd-vault-plugin的灵活性太高了,导致官方文档中很难针对某一种用法给出完整用例,需要通读,来回读一些内容才能搞明白哪一步可能出错了。遇事不慌,日志来帮,何况它还是开源的,随时翻出源码瞧一瞧,一切都还来得及救~

这篇就先写到这里。有考虑把ArgoCD的完整实践记录下来,包括GitOps的组织和使用,一些高级特性的使用,多集群等的用法,似乎有可能成一个系列。有兴致了、材料准备到位了再视情况吧~

欢迎提出你的想法和建议,也包括鼓励:)