如何使用 service-accounts-for-instances 为 google 云存储对象生成 Signed URL
How to generate Signed URL for google cloud storage objects using service-accounts-for-instances
我正在使用 signed-urls 让我的客户临时访问 google 云存储对象
我有一个服务帐户 json,如下所示:
{
"type": "service_account",
"project_id": "my-project",
"private_key_id": "abcdef1234567890",
"private_key": "-----BEGIN PRIVATE KEY-----\n<key...>\n-----END PRIVATE KEY-----\n",
"client_email": "my-app@my-project.iam.gserviceaccount.com",
"client_id": "1234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/my-app%my-project.iam.gserviceaccount.com"
}
下面是我如何创建签名的 url - 用长生不老药编写的代码(来自 gcs_signer lib 的代码)
def sign_url(private_key, client_email, bucket, object) do
verb = "GET"
md5_digest = ""
content_type = ""
expires = DateTime.utc_now() |> DateTime.to_unix() |> Kernel.+(1 * 3600)
resource = "/#{bucket}/#{object}"
signature = [verb, md5_digest, content_type, expires, resource]
|> Enum.join("\n") |> generate_signature(private_key)
url = "https://storage.googleapis.com#{resource}"
qs = %{
"GoogleAccessId" => client_email,
"Expires" => expires,
"Signature" => signature
} |> URI.encode_query
Enum.join([url, "?", qs])
end
defp generate_signature(string, private_key) do
private_key = process_key(private_key)
string
|> :public_key.sign(:sha256, private_key)
|> Base.encode64
end
defp process_key(private_key) do
private_key
|> :public_key.pem_decode
|> (fn [x] -> x end).()
|> :public_key.pem_entry_decode
|> normalize_private_key
end
defp normalize_private_key(private_key) do
# grab privateKey from the record tuple
private_key
|> elem(3)
|> (fn pk -> :public_key.der_decode(:RSAPrivateKey, pk) end).()
end
这是我使用 json 文件
中的 private_key
创建了一个签名 url
出于安全原因,我们改用 service-accounts-for-instances 而不是使用 json 凭据
我的问题是如何在我没有 json 凭据的情况下使用 service-accounts-for-instances
创建签名的 url?
我唯一拥有的是 servie_account_email
,它看起来像这样:my-app-gsa@my-project.iam.gserviceaccount.com
我应该使用 signBlob api 吗?如果是这样,我的 curl 请求会是什么样子?
我试图重现您的用例:
创建两个服务帐户:
gcloud iam service-accounts create signblob --description signblob
gcloud iam service-accounts create signforme --description signforme
# signblob will sign for signforme
为 signblob
服务帐户设置 IAM 角色 (roles/iam.serviceAccountTokenCreator
):
gcloud projects add-iam-policy-binding myproject --member signblob@myproject.iam.gserviceaccount.com --role roles/iam.serviceAccountTokenCreator
使用 signblob
服务将 VM 创建为服务帐户:
gcloud compute instances create instance-10 --zone=us-central1-a --service-account=signblob@myproject.iam.gserviceaccount.com
SSH 到刚刚创建的实例:
gcloud compute ssh instance-10 --zone=us-central1-a
在实例上创建文件:
nano file
cat file
# This is a file
使用 gcloud
工具为 signforme
服务帐户使用 --log-http
标志签署文件(blob):
gcloud iam service-accounts sign-blob --iam-account signblob-source@myproject.iam.gserviceaccount.com file output --log-http
输出:
signed blob [file] as [output] for [signforme@myproject.iam.gserviceaccount.com]
这是我 运行 在 VM instance-10
之前创建的 curl 命令:
curl --request POST 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/signblob-source%40myproject.iam.gserviceaccount.com:signBlob?prettyPrint=true&key=[API KEY]' --header 'Authorization: Bearer [ACCESS TOKEN]' --header 'Accept: application/json' --header 'Content-Type: application/json' --data '{"payload":"VGhpcyBpcyBhIGZpbGUgCg=="}' --compressed
其中访问令牌是 gcloud auth application-default print-access-token
输出:
{"keyId": ,"signedBlob":}
这是我在 elixir returns 中的代码,一个加密的 link 格式如下:
https://storage.googleapis.com/my-bucket/my-file?Expires=1576437298&GoogleAccessId=my-gsa%40project.iam.gserviceaccount.com&Signature=FUgBzvfFCa0YAL
关注 google api signBlob
@base_url @https://storage.googleapis.com
def generate_encrypted_url() do
gcp_service_account = "my-gsa@project.iam.gserviceaccount.com"
bucket = "my-bucket",
object ="my-file"
get_signed_url(gcp_service_account, bucket, object)
end
def get_signed_url(gcp_service_account, bucket, object) do
%Tesla.Client{pre: [{Tesla.Middleware.Headers, :call, [auth_headers]}]} = get_connection()
headers = [{"Content-Type", "application/json"}] ++ auth_headers
url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/#{gcp_service_account}:signBlob"
expires = DateTime.utc_now() |> DateTime.to_unix() |> Kernel.+(1 * 3600)
resource = "/#{bucket}/#{object}"
signature = ["GET", "", "", expires, resource] |> Enum.join("\n") |> Base.encode64()
body = %{"payload" => signature} |> Poison.encode!()
{:ok, %{status_code: 200, body: result}} = HTTPoison.post(url, body, headers)
%{"signedBlob" => signed_blob} = Poison.decode!(result)
qs = %{
"GoogleAccessId" => gcp_service_account,
"Expires" => expires,
"Signature" => signed_blob
} |> URI.encode_query
Enum.join(["#{@base_url}#{resource}", "?", qs])
end
def get_connection() do
{:ok, token} = Goth.Token.for_scope("https://www.googleapis.com/auth/cloud-platform")
GoogleApi.Storage.V1.Connection.new(token.token)
end
我正在使用 signed-urls 让我的客户临时访问 google 云存储对象
我有一个服务帐户 json,如下所示:
{
"type": "service_account",
"project_id": "my-project",
"private_key_id": "abcdef1234567890",
"private_key": "-----BEGIN PRIVATE KEY-----\n<key...>\n-----END PRIVATE KEY-----\n",
"client_email": "my-app@my-project.iam.gserviceaccount.com",
"client_id": "1234567890",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/my-app%my-project.iam.gserviceaccount.com"
}
下面是我如何创建签名的 url - 用长生不老药编写的代码(来自 gcs_signer lib 的代码)
def sign_url(private_key, client_email, bucket, object) do
verb = "GET"
md5_digest = ""
content_type = ""
expires = DateTime.utc_now() |> DateTime.to_unix() |> Kernel.+(1 * 3600)
resource = "/#{bucket}/#{object}"
signature = [verb, md5_digest, content_type, expires, resource]
|> Enum.join("\n") |> generate_signature(private_key)
url = "https://storage.googleapis.com#{resource}"
qs = %{
"GoogleAccessId" => client_email,
"Expires" => expires,
"Signature" => signature
} |> URI.encode_query
Enum.join([url, "?", qs])
end
defp generate_signature(string, private_key) do
private_key = process_key(private_key)
string
|> :public_key.sign(:sha256, private_key)
|> Base.encode64
end
defp process_key(private_key) do
private_key
|> :public_key.pem_decode
|> (fn [x] -> x end).()
|> :public_key.pem_entry_decode
|> normalize_private_key
end
defp normalize_private_key(private_key) do
# grab privateKey from the record tuple
private_key
|> elem(3)
|> (fn pk -> :public_key.der_decode(:RSAPrivateKey, pk) end).()
end
这是我使用 json 文件
private_key
创建了一个签名 url
出于安全原因,我们改用 service-accounts-for-instances 而不是使用 json 凭据
我的问题是如何在我没有 json 凭据的情况下使用 service-accounts-for-instances
创建签名的 url?
我唯一拥有的是 servie_account_email
,它看起来像这样:my-app-gsa@my-project.iam.gserviceaccount.com
我应该使用 signBlob api 吗?如果是这样,我的 curl 请求会是什么样子?
我试图重现您的用例:
创建两个服务帐户:
gcloud iam service-accounts create signblob --description signblob gcloud iam service-accounts create signforme --description signforme # signblob will sign for signforme
为
signblob
服务帐户设置 IAM 角色 (roles/iam.serviceAccountTokenCreator
):gcloud projects add-iam-policy-binding myproject --member signblob@myproject.iam.gserviceaccount.com --role roles/iam.serviceAccountTokenCreator
使用
signblob
服务将 VM 创建为服务帐户:gcloud compute instances create instance-10 --zone=us-central1-a --service-account=signblob@myproject.iam.gserviceaccount.com
SSH 到刚刚创建的实例:
gcloud compute ssh instance-10 --zone=us-central1-a
在实例上创建文件:
nano file cat file # This is a file
使用
gcloud
工具为signforme
服务帐户使用--log-http
标志签署文件(blob):gcloud iam service-accounts sign-blob --iam-account signblob-source@myproject.iam.gserviceaccount.com file output --log-http
输出:
signed blob [file] as [output] for [signforme@myproject.iam.gserviceaccount.com]
这是我 运行 在 VM instance-10
之前创建的 curl 命令:
curl --request POST 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/signblob-source%40myproject.iam.gserviceaccount.com:signBlob?prettyPrint=true&key=[API KEY]' --header 'Authorization: Bearer [ACCESS TOKEN]' --header 'Accept: application/json' --header 'Content-Type: application/json' --data '{"payload":"VGhpcyBpcyBhIGZpbGUgCg=="}' --compressed
其中访问令牌是 gcloud auth application-default print-access-token
输出:
{"keyId": ,"signedBlob":}
这是我在 elixir returns 中的代码,一个加密的 link 格式如下:
https://storage.googleapis.com/my-bucket/my-file?Expires=1576437298&GoogleAccessId=my-gsa%40project.iam.gserviceaccount.com&Signature=FUgBzvfFCa0YAL
关注 google api signBlob
@base_url @https://storage.googleapis.com
def generate_encrypted_url() do
gcp_service_account = "my-gsa@project.iam.gserviceaccount.com"
bucket = "my-bucket",
object ="my-file"
get_signed_url(gcp_service_account, bucket, object)
end
def get_signed_url(gcp_service_account, bucket, object) do
%Tesla.Client{pre: [{Tesla.Middleware.Headers, :call, [auth_headers]}]} = get_connection()
headers = [{"Content-Type", "application/json"}] ++ auth_headers
url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/#{gcp_service_account}:signBlob"
expires = DateTime.utc_now() |> DateTime.to_unix() |> Kernel.+(1 * 3600)
resource = "/#{bucket}/#{object}"
signature = ["GET", "", "", expires, resource] |> Enum.join("\n") |> Base.encode64()
body = %{"payload" => signature} |> Poison.encode!()
{:ok, %{status_code: 200, body: result}} = HTTPoison.post(url, body, headers)
%{"signedBlob" => signed_blob} = Poison.decode!(result)
qs = %{
"GoogleAccessId" => gcp_service_account,
"Expires" => expires,
"Signature" => signed_blob
} |> URI.encode_query
Enum.join(["#{@base_url}#{resource}", "?", qs])
end
def get_connection() do
{:ok, token} = Goth.Token.for_scope("https://www.googleapis.com/auth/cloud-platform")
GoogleApi.Storage.V1.Connection.new(token.token)
end