<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rakib Al Hasan</title>
    <description>The latest articles on DEV Community by Rakib Al Hasan (@syedrakib).</description>
    <link>https://dev.to/syedrakib</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F270767%2F5751efb6-de96-4f74-85c5-143009200ead.jpeg</url>
      <title>DEV Community: Rakib Al Hasan</title>
      <link>https://dev.to/syedrakib</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syedrakib"/>
    <language>en</language>
    <item>
      <title>Terraform on GoogleCloud - impersonating with short-lived AccessTokens &amp; ServiceAccounts</title>
      <dc:creator>Rakib Al Hasan</dc:creator>
      <pubDate>Mon, 20 Apr 2020 22:38:53 +0000</pubDate>
      <link>https://dev.to/syedrakib/terraform-on-googlecloud-impersonating-with-short-lived-accesstokens-serviceaccounts-ca6</link>
      <guid>https://dev.to/syedrakib/terraform-on-googlecloud-impersonating-with-short-lived-accesstokens-serviceaccounts-ca6</guid>
      <description>&lt;h1&gt;
  
  
  Your Current Setup
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;you have a Google Cloud Platform (GCP) project&lt;/li&gt;
&lt;li&gt;you have a Terraform script&lt;/li&gt;
&lt;li&gt;you have the JSON Key of a ServiceAccount in your Terraform script&lt;/li&gt;
&lt;li&gt;your ServiceAccount has full (owner) access to your GCP - to be able to create &amp;amp; destroy anything &amp;amp; everything in GCP as &amp;amp; when needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The Problem Statement
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Point number 4&lt;/strong&gt; above is the problem statement. You have a JSON key outside in the world that has FULL access to do anything with your GCP. If anyhow that JSON key is obtained by someone(despite all sorts of encryption / protection / etc etc), you run the risk of a lot of damage. That's a big risk in security perspective and we can do better than that.&lt;/p&gt;

&lt;h1&gt;
  
  
  Enter Impersonation
&lt;/h1&gt;

&lt;p&gt;The idea is simple.&lt;/p&gt;

&lt;p&gt;The executor ServiceAccount (for which you have a JSON key that is literally floating out there in the wild jungle called "the internet") will only have super-limited / super-controlled / super-tight access to your GCP. That's all it is allowed to do. Nothing more than that. Kinda secure that way.&lt;/p&gt;

&lt;p&gt;That TF executor ServiceAccount will "&lt;em&gt;impersonate&lt;/em&gt;" another super ServiceAccount - the mighty one who will have all the privileges and permissions to do anything &amp;amp; everything with your GCP as required by Terraform to create/modify/destroy resources.&lt;/p&gt;

&lt;p&gt;However, this super-mighty ServiceAccount will not have any JSON key (so nothing about it is floating out there on the internet - kinda secure that way) and it will allow only very specific ServiceAccounts (for example, the executor ServiceAccount in this case) to "&lt;em&gt;impersonate&lt;/em&gt;" it.&lt;/p&gt;

&lt;h1&gt;
  
  
  How does it work in practice?
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Let's assign some names first:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;let's call the ServiceAccount with limited permissions our &lt;code&gt;tf-executor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;let's call the super-mighty ServiceAccount our &lt;code&gt;tf-owner&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Let's write our Terraform providers
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;credentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path_to_tf_executor_service_account_json_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;alias&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tf_executor"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"google_service_account_access_token"&lt;/span&gt; &lt;span class="s2"&gt;"impersonated"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;provider&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;google&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tf_executor&lt;/span&gt;
  &lt;span class="nx"&gt;target_service_account&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tf_owner_service_account_email&lt;/span&gt;
  &lt;span class="nx"&gt;scopes&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"cloud-platform"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;lifetime&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1800s"&lt;/span&gt; &lt;span class="c1"&gt;# 30 minutes - max can be set up to 60 minutes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;access_token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_service_account_access_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;impersonated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gcp_project_id&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"google-beta"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;access_token&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;google_service_account_access_token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;impersonated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;access_token&lt;/span&gt;
  &lt;span class="nx"&gt;project&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gcp_project_id&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Terraform v0.12.24&lt;/span&gt;
&lt;span class="c1"&gt;# provider.google v3.13.0&lt;/span&gt;
&lt;span class="c1"&gt;# provider.google-beta v3.13.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Some things to note in the script above
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;there are 2 google providers and 1 google-beta provider. Ignore the importance of google-beta provider for this discussion. It is here just to show that we can have multiple providers "&lt;em&gt;impersonating&lt;/em&gt;" the same ServiceAccount&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;there is a google provider with an alias&lt;br&gt;
there is a google provider without alias&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the aliased google provider uses the &lt;code&gt;tf-executor&lt;/code&gt; ServiceAccount via its JSON key file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;the data block uses the aliased google provider to call google APIs to request for a new access token on behalf of &lt;code&gt;tf-owner&lt;/code&gt; - this new access token will last for 30 minutes - max can be set up to 60 minutes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;this new access_token from the data block has cloud-platform scope. This means the access token has full access across all of GCP - as long as the IAM roles assigned to the &lt;code&gt;tf-owner&lt;/code&gt; ServiceAccount allow it - more on this inside the "Roles for tf-owner" section below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;this new access_token from the data block is then used by the non-aliased google provider and the non-aliased google-beta provider - thus "&lt;em&gt;impersonating&lt;/em&gt;" the &lt;code&gt;tf-owner&lt;/code&gt; ServiceAccount&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;now, we can use these non-aliased providers in our Terraform resources and modules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mentioning &lt;code&gt;provider = google.tf_executor&lt;/code&gt; inside the resource will refer to the aliased google provider - which really has only very limited permissions by itself&lt;/li&gt;
&lt;li&gt;mentioning &lt;code&gt;provider = google&lt;/code&gt; (or by not even mentioning it because it is implied by default) inside the resource will refer to the non-aliased google provider - which now has all the necessary permissions to perform everything&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This way, throughout the rest of our Terraform script, our "&lt;em&gt;impersonated&lt;/em&gt;" google provider (aka our non-aliased google provider) will have all the necessary permissions (on behalf of &lt;code&gt;tf-owner&lt;/code&gt;) to perform all terraform operations like create/modify/destroy as needed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Roles &amp;amp; Permissions
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Roles for &lt;code&gt;tf-executor&lt;/code&gt;:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;roles/storage.admin&lt;/code&gt; - to be able to query GCS bucket if that is what you are using to store our TFStates. This is required even before the &lt;code&gt;tf-executor&lt;/code&gt; gets to "&lt;em&gt;impersonate&lt;/em&gt;" the &lt;code&gt;tf-owner&lt;/code&gt;. Hence, we need to provide this bit explicitly. You may further tighten this permission by adding a condition to this role so that it can access only the specific GCS bucket that is dealing with the TFStates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;roles/iam.serviceAccountTokenCreator&lt;/code&gt; - to be able to perform the work of the data block - requesting access token on behalf of another ServiceAccount&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;that's all - nothing more than that&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Roles for &lt;code&gt;tf-owner&lt;/code&gt;:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;roles/owner&lt;/code&gt; - to be able to create/modify/destroy anything &amp;amp; everything inside your GCP project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;or you may consider not giving the owner role at all but instead just the specific admin roles of specific GCloud resources if you want to further tighten the permissions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Benefits
&lt;/h1&gt;

&lt;p&gt;This actually helps tighten the access and makes sure that the JSON key file that is out there sitting in the internet cannot do anything much by itself - It also has to know additional things like the &lt;code&gt;tf-owner&lt;/code&gt; ServiceAccount email address etc to be able to fully exploit its potentials&lt;/p&gt;

&lt;h1&gt;
  
  
  Watch Out
&lt;/h1&gt;

&lt;p&gt;This certainly doesn't mean it's now OKAY to pay less attention to the security / encryption / storage of the &lt;code&gt;tf-executor&lt;/code&gt; ServiceAccount JSON key.&lt;/p&gt;

&lt;p&gt;You still gotta do all that. But the risks associated with it being compromised is measurably reduced now.&lt;/p&gt;




&lt;p&gt;With inspirations from &lt;a href="https://medium.com/wescale/how-to-generate-and-use-temporary-credentials-on-google-cloud-platform-b425ef95a00d" rel="noopener noreferrer"&gt;https://medium.com/wescale/how-to-generate-and-use-temporary-credentials-on-google-cloud-platform-b425ef95a00d&lt;/a&gt;&lt;br&gt;
This article originally appeared in &lt;a href="https://medium.com/@syedrakib/terraform-on-gcp-impersonating-with-limited-access-on-serviceaccount-9dae6e2be11c" rel="noopener noreferrer"&gt;https://medium.com/@syedrakib/terraform-on-gcp-impersonating-with-limited-access-on-serviceaccount-9dae6e2be11c&lt;/a&gt;&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>gcloud</category>
      <category>serviceaccount</category>
      <category>accesstoken</category>
    </item>
  </channel>
</rss>
