使用 Gitlab,在 CI/CD 管道中,如何动态创建 SSH 密钥对并将其保存为 CI/CD 项目变量?

问题描述 投票:0回答:1

根据 Gitlab 文档,我手动创建了 SSH 密钥对并将它们添加到项目的 CI/CD 变量中,以帮助预配和配置基础设施。这对于一些资产来说已经足够好了,但是随着资产列表的增长,手动创建密钥对并添加它们,或者对多个但不相关的资产使用相同的密钥对,或者稍后出于安全原因轮换密钥,是不可持续的。相反,当创建新资产时,我想动态创建一个新的 SSH 密钥对,将其添加到项目的 CI/CD 变量中,然后使用这些密钥继续我的管道作业。

对于上下文,这是我的存储库的目录结构:

iac
|__ server1
    |__main.tf
|__ server2
...
|__ server10

在工作中

before_script
,创建 SSH 密钥对很容易,但之后发生的事情是我遇到的困难。

job1:
  before_script:
  - ssh-keygen -b 4096 -N '' -q -f ~/.ssh/id_rsa <<< y
  - *<what command goes here?>*

在一种方法中我通过curl和api与项目交互,但是将键的值放入字符串中会显示日志中的键内容。 (我确信有办法解决这个问题,但我还没有解决)。

在另一种方法中,我动态地将

gitlab_ssh_keys.tf
文件添加到
server<number
目录中。请注意,这不一定是 Terraform 问题,因为我将使用最好的方法来完成工作;但是,Gitlab 有一个 Terraform 提供商,并且基础设施是通过 Terraform 进行管理的。这感觉很优雅,因为如果基础设施被破坏,变量也会被清理(否则,使用 Bash,我需要确保脚本处理这个问题)

# (rough pipeline pseudo code)
job:
  before_script:
  - ssh-keygen -b 4096 -N '' -q -f ~/.ssh/id_rsa <<< y
  - |
provider "gitlab" {
  token = var.gitlab_token # <- pre-configured in the project's CICD variables $CI_JOB_TOKEN is available, but it doesn't work here.
}

cat <<EOF > ${serverName}/gitlab_ssh_keys.tf
resource "gitlab_project_variable" "${serverName}_ssh_private_key" {
  project   = ${CI_PROJECT_ID}
  key       = "${servername}_private_ssh_key"
  value     = chomp(file("~/.ssh/id_rsa"))
  protected = true
  type = file
}

resource "gitlab_project_variable" "${serverName}_ssh_public_key {
... ... etc
}
EOF
  - <then add the file to the branch/repo dynamically, but how?>

在这两种方法中,我都在努力寻找正确的令牌和/或 SSH 密钥来让管道与其自身交互。 GItlab 拥有数量惊人的代币类型、密钥类型以及每种类型的用例,找到合适的类型一直是一项挑战。我正在工作通过这个例子,但作者不清楚选择哪一个以及授予它什么权限。

所以我的第一个问题是让项目的管道可以动态地与自身交互,并且考虑到最小特权原则,我应该使用什么令牌和/或 ssh 密钥和权限的组合?这将是仅在管道中使用的“机器人”类型。

我的第二个问题是,如果我使用 Terraform 方法,什么是高效的工作流程以及如何避免在管道更新分支时创建循环?请注意,该示例提到使用

[skip ci]
。这可能仍然有效,但我还没有做到这一点。那个帖子也已经有两年多了,自那以后发生了很多变化。可能有更好的方法。

想法?谢谢!

terraform gitlab-ci cicd gitlab-api
1个回答
0
投票

我想我会尝试扩展你的 terraform 解决方案。

我不会定义 pre_script 任务,而是创建当前您的第一个任务所需的整个任务。

在您的存储库中,您可以为 terraform 代码添加一个

ssh_keygen/
目录。

您可以使用 tls 提供程序 来生成您的密钥。

第1步:添加tf代码

首先,您需要定义要为其生成密钥对的机器列表:

variables.tf

variable "project_name" {
  type        = list(string)
  description = "The project identifier"
}

variable "ssh_hosts" {
  type        = list(string)
  description = "List of hosts to generate a keypair for"
}

main.tf
——示例不包括提供者定义,但您需要在此处定义它。

resource "tls_private_key" "ssh_key" {
  for_each = toset(var.ssh_hosts)
  algorithm   = "ED25519"
}

resource "gitlab_project_variable" "host_private_key" {
  for_each = toset(var.ssh_hosts)
  project   = var.project_name
  key       = "${each.value}_ssh_private_key"
  value     = tls_private_key.ssh_key[each.value].private_key_openssh
  protected = true
}

resource "gitlab_project_variable" "host_public_key" {
  for_each = toset(var.ssh_hosts)
  project   = var.project_name
  key       = "${each.value}_ssh_public_key"
  value     = tls_private_key.ssh_key[each.value].public_key_openssh
  protected = true
}

这样,只要您将主机列表作为

ssh_hosts
变量的输入传递,您就会获得为每个主机生成的 ssh 密钥,并将其添加到 gitlab 中的项目变量中。

注意:我不确定(或者更确切地说,我几乎肯定它不会)如果在运行中添加变量,gitlab 是否能够重新加载其环境,这就是为什么此阶段需要与其余阶段完全分开的原因管道。

第 2 步:.gitlab-ci.yml

在运行主要任务之前,您需要将以下阶段添加到 gitlab-ci 管道中。


ssh_keypair_generation:
  variables:
    TF_VAR_project_name: ${CI_PROJECT_ID}
  stage: ssh_keygen
  before_script:
    - export TF_VAR_ssh_hosts=$(find iac -mindepth 1 -maxdepth 1 -type d -printf '"%f"\n' | jq -s .) # or any other logic to create a json list that contains the list of your hosts
  script:
    - terraform -chdir=./ssh_keygen init
    - terraform -chdir=./ssh_keygen plan -out=path/to/plan.tfplan
    - terraform -chdir=./ssh_keygen apply -auto-approve path/to/plan.tfplan
  rules: [] # your rules

这应该允许您为

iac/
目录中的每个主机动态创建 ssh 密钥,并在添加/删除主机时动态添加和删除它们。

在后续阶段中,您只需要使用

needs
属性,以确保在运行其他任何操作之前生成密钥,并确保重新加载环境变量(在新任务中)。

job1_job:
  needs: ["ssh_keypair_generation"]
...

最后,如果您的管道更新了自己的分支,您可以通过在提交消息中使用

[skip-ci]
来触发新作业。

如果您需要更多详细信息,请告诉我。

© www.soinside.com 2019 - 2024. All rights reserved.