TL;DR: Generating and distributing service account keys poses severe security risks to your organization. They are long-lived credentials that are not automatically rotated. These keys can be leaked accidentally or maliciously allowing attackers to gain access to your sensitive GCP resources. Additionally, when used actions cannot be attributable back to a human. You don’t actually have to download these long-lived keys. There’s a better way!
TL; DR:生成和分发服务帐户密钥会对组织造成严重的安全风险。 它们是长期存在的凭证,不会自动轮换。 这些密钥可能会意外或恶意泄露,从而使攻击者可以访问您的敏感GCP资源。 另外,当使用的动作不能归因于人类时。 您实际上不必下载这些长期存在的密钥。 有更好的方法!
服务帐户,OAuth2和您 (Service Accounts, OAuth2 and You)
For some background, almost every change you want to make in Google Cloud from creating a GKE cluster to reading from a GCS bucket is handled using an API. This API is authenticated using the OAuth2 protocol, which basically means there’s a short lived (1 hour default) access token attached to every authenticated request. If you’re familiar with the whole “Sign in with Google” popup, that’s OAuth2 hard at work authenticating you with your Google credentials. Once you’re authenticated, an access token is attached to all your API requests whether you’re using gcloud
, terraform
, SDKs, or the console. In Google Cloud, we use a lot of automation and web services which similarly need those tokens, but robots aren’t very good at opening browsers and typing in passwords so they need some sort of verifiable identity. Enter Service Accounts.
在某些背景下,几乎您要在Google Cloud中进行的所有更改(从创建GKE群集到从GCS存储桶读取)都使用API进行处理。 此API使用OAuth2协议进行身份验证,这基本上意味着每个经过身份验证的请求都附有短暂的访问令牌(默认为1小时)。 如果您熟悉整个“使用Google登录”弹出窗口,则OAuth2会很努力地使用Google凭据对您进行身份验证。 通过身份验证后,无论您使用的是gcloud
, terraform
,SDK还是控制台,访问令牌都会附加到所有API请求中。 在Google Cloud中,我们使用了很多自动化和Web服务,它们同样需要这些令牌,但是机器人并不是很擅长打开浏览器并输入密码,因此它们需要某种可验证的身份。 输入服务帐户 。
Service accounts allow automated users to prove their identity using a public/private key pair in the form of a JSON file. A service account also has the same ability as users or groups to bind to IAM roles to do things in GCP. To make an API request, a service account will sign a JWT token with its private key and the Google authentication system will verify that signature with the public key, granting an access token. This basic (and oversimplified) concept is important for later parts of this post. If you’d like to read more about this flow, check out RFC 7523.
服务帐户允许自动化用户使用JSON文件形式的公用/专用密钥对来证明其身份。 服务帐户还具有与用户或组绑定到IAM角色以在GCP中执行操作的功能。 要发出API请求,服务帐户将使用其私钥对JWT令牌进行签名,Google身份验证系统将使用公钥来验证该签名,并授予访问令牌。 这个基本(且过于简化)的概念对于本文的后续部分很重要。 如果您想了解有关此流程的更多信息,请参阅RFC 7523 。
You should never need to generate and download a service account key to use a service account within Google Cloud infrastructure.
您无需生成和下载服务帐户密钥即可在Google Cloud基础架构中使用服务帐户。
Service accounts are very easy to use within Google Cloud. Most, if not all, compute resources (i.e. GCE instances, GKE Pods, Cloud Functions, etc.) support the ability to attach a service account. This allows these resources to act as the service account, call Google SDKs and APIs within the bounds of permissions granted to the service account. You should never need to generate and download a service account key to use a service account within Google Cloud infrastructure. A risk emerges when developers think they need a service account to accomplish a task, so they generate and download a key.
服务帐户在Google Cloud中非常易于使用。 大多数(如果不是全部)计算资源(例如,GCE实例,GKE Pod,Cloud Functions等)都支持附加服务帐户的功能。 这样一来,这些资源就可以充当服务帐户,并在授予该服务帐户的权限范围内调用Google SDK和API。 您无需生成和下载服务帐户密钥即可在Google Cloud基础架构中使用服务帐户。 当开发人员认为他们需要服务帐户来完成任务时,就会产生风险,因此他们生成并下载密钥。
I cannot tell you how often I see documentation or tutorials instruct folks to download these service account keys and use them indefinitely or worse, store them in their source code working directory. Doing this, you’re literally one line in a .gitignore
from committing this highly sensitive secret to Github and getting breached.
我无法告诉您我经常看到文档或教程指示人们下载这些服务帐户密钥并无限期或更糟地使用它们,并将它们存储在其源代码工作目录中。 这样做,实际上是将.gitignore
提交给Github并遭到破坏的行。
短暂的代币FTW! (Short lived tokens FTW!)
Remember how I said that if you have the Service Account’s private key, you can sign a JWT token and be granted an API access token? Well there is a way to do that without ever needing to download the key.
还记得我怎么说的话:如果您拥有服务帐户的私钥,那么您可以签名JWT令牌并被授予API访问令牌? 好吧,有一种方法可以做到,而无需下载密钥。
Let’s say I have a service account that is used for GKE so it has the role roles/container.developer
. We’ll call this service account k8s@project.iam.gserviceaccount.com
. Let’s further say that my user ryan@example.com
isn’t allowed to download the key to this service account and doesn’t have direct permissions to mess with GKE but what I do have is the magic role roles/iam.serviceAccountTokenCreator
. Now all I have to do to setup my GKE credentials with the gcloud command is:
假设我有一个用于GKE的服务帐户,因此它具有角色roles/container.developer
。 我们将此服务帐户k8s@project.iam.gserviceaccount.com
。 再说ryan@example.com
,我的用户ryan@example.com
不允许将密钥下载到该服务帐户,并且没有直接权限与GKE混淆,但是我所拥有的只是魔术角色roles/iam.serviceAccountTokenCreator
。 现在,使用gcloud命令设置我的GKE凭据所需要做的就是:
gcloud
This is great because it allows this command to use a service account without actually having the key! Not only that but you always know you are impersonating because a warning message pops up letting you know.
这很棒,因为它允许该命令使用服务帐户而不实际拥有密钥! 不仅如此,而且您始终知道自己在冒充别人,因为会弹出警告消息让您知道。
WARNING: This command is using service account impersonation. All API calls will be executed as [
But if you’re running multiple commands with the same service account, this can be annoying to type over and over. Instead, let’s set this with gcloud config
.
但是,如果您使用同一个服务帐户运行多个命令,那么一遍又一遍地输入可能会很烦人。 相反,让我们使用gcloud config
设置。
gcloud config set auth/impersonate_service_account \
gcloud container clusters get-credentials my-cluster
# Other gcloud commands :)
This is much better! Now we can impersonate a user for multiple commands without having to constantly add that --impersonate-service-account
flag. But what if, like me, you constantly switch back and forth between service accounts for different projects. We can write a very simple bash script to simplify typing and remembering service accounts that you use frequently.
这样好多了! 现在,我们可以模拟用户执行多个命令,而不必不断添加--impersonate-service-account
标志。 但是,如果像我一样,您不断在不同项目的服务帐户之间来回切换,该怎么办? 我们可以编写一个非常简单的bash脚本,以简化键入和记住您经常使用的服务帐户。
#!/bin/bashIMPERSONATE='gcloud config set auth/impersonate_service_account'impersonate() {
sa=$1
echo "Impersonating $sa"
$IMPERSONATE $sa
}case $1 in
gke)
impersonate k8s@project.iam.gserviceaccount.com
;;
admin)
impersonate admin@other-project.iam.gserviceaccount.com
;;
clear)
gcloud config unset auth/impersonate_service_account
;;
*)
echo "Usage: Updates impersonated service account"
echo " gsa [gke|admin|clear]"
esac
Now you could execute this with the following before using gcloud
:
现在,您可以在使用gcloud
之前使用以下命令执行此gcloud
:
$ gsa gke
Impersonating k8s@project.iam.gserviceaccount.com
Updated property [auth/impersonate_service_account].
$ gsa clear
Unset property [auth/impersonate_service_account].
You could make this more robust by reading from a config file if you like, but I think a single-file script gets the point across. Now for every new service account you want to use, simply add it and a short name for it to this script and you’re off.
如果愿意,您可以通过读取配置文件来增强此功能,但我认为单文件脚本可以解决这一问题。 现在,对于您要使用的每个新服务帐户,只需将其和它的简称添加到此脚本中就可以了。
那Terraform呢? (What about Terraform?)
We’ve spent all this time talking about a better way to consume service accounts through gcloud
but what about the very common use case of Terraform? For development purposes, we need to test our infrastructure as code somehow. Thankfully, the google terraform provider supports directly passing an OAuth2 token as an environment variable. All you have to do to get this token and tell Terraform about it is this:
我们一直在讨论通过gcloud
消费服务帐户的更好方法,但是Terraform的非常常见的用例又如何呢? 出于开发目的,我们需要以某种方式对基础结构进行测试。 值得庆幸的是, Google terraform提供程序支持直接将OAuth2令牌作为环境变量传递。 获得此令牌并将其告知Terraform所要做的就是:
export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform apply
You could further simplify this by wrapping it in a Makefile, but regardless, now you have a token that will only live in your environment for 1 hour and be useless to an attacker after that!
您可以通过将其包装在Makefile中来进一步简化此操作,但是无论如何,现在您有了一个令牌,该令牌只能在您的环境中使用1个小时,之后对攻击者无用!
归因和记录 (Attribution and Logging)
The SecOps folks may be thinking, how do I attribute and audit actions taken by a user impersonating a service account? In Cloud Logging, every API call executed by a service account that has been impersonated has the following structure within it:
SecOps人士可能会思考,我该如何归因和审核模拟服务帐户的用户所采取的措施? 在Cloud Logging中,已被模拟的服务帐户执行的每个API调用均具有以下结构:
{
"principalEmail": "k8s@project.iam.gserviceaccount.com",
"serviceAccountDelegationInfo": [
{
"firstPartyPrincipal": {
"principalEmail": "ryan@example.com"
}
}
]
}
You can also get a higher level of detail if you enable Data Access logs. The below Cloud Logging filter will include every API call that ryan@example.com
made while impersonating. I’m sure you can find other clever filters as well.
如果启用了数据访问日志,则还可以获得更高级别的详细信息。 下面的Cloud Logging过滤器将包含ryan@example.com
在模拟时进行的每个API调用。 我相信您也可以找到其他聪明的过滤器。
protoPayload.authenticationInfo.serviceAccountDelegationInfo.0.firstPartyPrincipal.principalEmail="ryan@example.com"
I’ll also point out that this level of attribution is impossible if you allow users to download service account keys since they are effectively using shared credentials, assuming more than one person has access to download the same key. To illustrate, this is the only info you get in the logs if I could download the k8s
service account key and used it.
我还要指出, 如果您允许用户下载服务帐户密钥 ,则这种归属级别是不可能的,因为他们假设有多个人可以下载同一密钥,因此他们实际上是在使用共享凭据。 为了说明这一点,如果我可以下载k8s
服务帐户密钥并使用它,那么这是您在日志中获得的唯一信息。
authenticationInfo: {
principalEmail: "k8s@project.iam.gserviceaccount.com"
serviceAccountKeyName: "//iam.googleapis.com/projects/project/serviceAccounts/k8s@project.iam.gserviceaccount.com/keys/caed69e352ae12ab17e1962de5bac31062495876"
}
If I’m a forensic analyst or an auditor, there’s no way I could figure out definitively what human executed this API request unless each user has their own service account or key and that’s defined somewhere. Even still, that is very difficult to trace back.
如果我是法医分析师或审计师,除非每个用户都有自己的服务帐户或密钥,并且在某个地方定义,否则我无法确切确定执行此API请求的人员。 即使如此,这也很难追溯。
注意事项 (Caveats)
There are some use cases where downloading a service account key to your workstation is necessary, but they are not the norm. Keeping secrets like this short-lived locally should be the goal, the same way we should enable MFA and not use hunter2
as our password. For the obvious cases where service account keys must be downloaded to use GCP resources from your datacenter or another cloud, I’d recommend taking a look at HashiCorp Vault which has a plugin to checkout short-lived service account keys.
在某些用例中,有必要将服务帐户密钥下载到您的工作站,但这不是正常的情况。 我们的目标是在本地这样短暂的机密,这与启用MFA而不是使用hunter2
作为我们的密码的方式相同。 对于明显的情况,必须下载服务帐户密钥才能使用数据中心或其他云中的GCP资源,我建议您看一下HashiCorp Vault ,它具有一个可签出短期服务帐户密钥的插件。
So now that you know, please stop downloading service account keys! :)
因此,既然您知道了,请停止下载服务帐户密钥! :)
下一步 (Next Steps)
Consider turning on the “Disable service account key creation” Organization policy to prevent users from downloading service account keys.
考虑打开“ 禁用服务帐户密钥创建 ”组织策略,以防止用户下载服务帐户密钥。
Read more about Short Lived Credentials from the Google Cloud docs.
从Google Cloud文档中了解有关短期证书的更多信息。
Read up on VPC Service Controls, which can protect your data even if a service account key does get leaked.
If you do need to download keys for use outside GCP or other edge cases, check out my other blog on securely deploying HashiCorp Vault to Google Cloud
如果您确实需要下载密钥以在GCP或其他极端情况下使用,请查看我的其他博客,以安全地将HashiCorp Vault部署到Google Cloud
翻译自: https://medium.com/@jryancanty/stop-downloading-google-cloud-service-account-keys-1811d44a97d9