根据 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 解决方案。
我不会定义 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]
来触发新作业。
如果您需要更多详细信息,请告诉我。