<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Iam on Mark Wolfe&#39;s Blog</title>
    <link>https://www.wolfe.id.au/tags/iam/</link>
    <description>Recent content in Iam on Mark Wolfe&#39;s Blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Sun, 12 Nov 2023 08:55:22 +1000</lastBuildDate><atom:link href="https://www.wolfe.id.au/tags/iam/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Avoid accidental exposure of authenticated Amazon API Gateway resources</title>
      <link>https://www.wolfe.id.au/2023/11/12/avoid-accidental-exposure-of-authenticated-amazon-api-gateway-resources/</link>
      <pubDate>Sun, 12 Nov 2023 08:55:22 +1000</pubDate>
      
      <guid>https://www.wolfe.id.au/2023/11/12/avoid-accidental-exposure-of-authenticated-amazon-api-gateway-resources/</guid>
      <description>&lt;p&gt;I have been working with &lt;a href=&#34;https://aws.amazon.com/api-gateway/&#34;&gt;Amazon API Gateway&lt;/a&gt; for a while and one thing I noticed is there are a few options for authentication, which can be confusing to developers, and lead to security issues. This post will cover one of the common security pitfalls with API Gateway and how to mitigate it.&lt;/p&gt;
&lt;p&gt;If your using &lt;code&gt;AWS_IAM&lt;/code&gt; authentication on an API Gateway, then make sure you set the default authorizer for all API resources. This will avoid accidental exposing an API if you mis-configure, or omit an authentication method for an API resource as the default is &lt;code&gt;None&lt;/code&gt;.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>I have been working with <a href="https://aws.amazon.com/api-gateway/">Amazon API Gateway</a> for a while and one thing I noticed is there are a few options for authentication, which can be confusing to developers, and lead to security issues. This post will cover one of the common security pitfalls with API Gateway and how to mitigate it.</p>
<p>If your using <code>AWS_IAM</code> authentication on an API Gateway, then make sure you set the default authorizer for all API resources. This will avoid accidental exposing an API if you mis-configure, or omit an authentication method for an API resource as the default is <code>None</code>.</p>
<p>In addition to this there is a way to apply a resource policy to an API Gateway, which will enforce a specific iam access check on all API requests. Combining the override to default authorizer, and the resource policy allows us to apply multiply layers of protection to our API, allowing us to follow the principle of defense in depth.</p>
<p>So to summarise, to protect your API with IAM authentication is as follows:</p>
<ol>
<li>Enable a default authorizer method on the API Gateway resource.</li>
<li>Enable an authentication method on the API.</li>
<li>Assign an API resource policy which requires IAM authentication to access the API.</li>
</ol>
<p>Doing this with <a href="https://aws.amazon.com/serverless/sam/">AWS SAM</a> is fairly straight forward, to read more about it see the <a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-apiauth.html">SAM ApiAuth documentation</a>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">  </span><span class="nt">AthenaWorkflowApi</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">Type</span><span class="p">:</span><span class="w"> </span><span class="l">AWS::Serverless::Api</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">Properties</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="l">...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">Auth</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># Specify a default authorizer for the API Gateway API to protect against missing configuration</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">DefaultAuthorizer</span><span class="p">:</span><span class="w"> </span><span class="l">AWS_IAM</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c"># Configure Resource Policy for all methods and paths on an API as an extra layer of protection</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">ResourcePolicy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="c"># The AWS accounts to allow</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">AwsAccountWhitelist</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">           </span>- !<span class="l">Ref AWS::AccountId</span><span class="w">
</span></span></span></code></pre></div><p>Through the magic of AWS SAM this results in a resource policy which looks like the following, this results in all the API methods being protected and only accessible by users authenticated to this account, and only where they are granted access via an IAM policy.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;Version&#34;</span><span class="p">:</span> <span class="s2">&#34;2012-10-17&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;Statement&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Effect&#34;</span><span class="p">:</span> <span class="s2">&#34;Allow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Principal&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;AWS&#34;</span><span class="p">:</span> <span class="s2">&#34;arn:aws:iam::123456789012:root&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Action&#34;</span><span class="p">:</span> <span class="s2">&#34;execute-api:Invoke&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Resource&#34;</span><span class="p">:</span> <span class="s2">&#34;arn:aws:execute-api:us-west-2:123456789012:abc123abc1/Prod/POST/athena/run_s3_query_template&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Effect&#34;</span><span class="p">:</span> <span class="s2">&#34;Allow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Principal&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nt">&#34;AWS&#34;</span><span class="p">:</span> <span class="s2">&#34;arn:aws:iam::123456789012:root&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Action&#34;</span><span class="p">:</span> <span class="s2">&#34;execute-api:Invoke&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;Resource&#34;</span><span class="p">:</span> <span class="s2">&#34;arn:aws:execute-api:us-west-2:123456789012:abc123abc1/Prod/POST/athena/run_query_template&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>I typically use an openapi spec to define the API, using the extensions provided by AWS such as <code>x-amazon-apigateway-auth</code> to define the authorisation.</p>
<p>With the default authentication set to <code>AWS_IAM</code> hitting an API which is missing <code>x-amazon-apigateway-auth</code> using curl returns the following error.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;message&#34;</span><span class="p">:</span><span class="s2">&#34;Missing Authentication Token&#34;</span><span class="p">}</span>
</span></span></code></pre></div><p>With default authentication disabled, and the resource policy enabled the API returns the following error, which illustrates the principle of defense in depth.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span><span class="nt">&#34;Message&#34;</span><span class="p">:</span><span class="s2">&#34;User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-east-1:********9012:abc123abc1/Prod/POST/athena/run_query_template&#34;</span><span class="p">}</span>
</span></span></code></pre></div>]]></content:encoded>
    </item>
    
    <item>
      <title>AWS User Federation with Keycloak</title>
      <link>https://www.wolfe.id.au/2017/11/05/aws-user-federation-with-keycloak/</link>
      <pubDate>Sun, 05 Nov 2017 10:22:47 +1000</pubDate>
      
      <guid>https://www.wolfe.id.au/2017/11/05/aws-user-federation-with-keycloak/</guid>
      <description>&lt;p&gt;As a user of Amazon Web Services (AWS) in large organisations I am always mindful of providing a mechanism to enable single sign on (SSO) to simplify the login process for users, enable strict controls for the organisation, and simplify on/off boarding for operations staff. As an advocate for open source I was happy find &lt;a href=&#34;http://www.keycloak.org/index.html&#34;&gt;Keycloak&lt;/a&gt;, which is developed by &lt;a href=&#34;https://www.redhat.com/en&#34;&gt;Redhat&lt;/a&gt; and is now an option for organisations looking for an open solution to identity federation with AWS.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>As a user of Amazon Web Services (AWS) in large organisations I am always mindful of providing a mechanism to enable single sign on (SSO) to simplify the login process for users, enable strict controls for the organisation, and simplify on/off boarding for operations staff. As an advocate for open source I was happy find <a href="http://www.keycloak.org/index.html">Keycloak</a>, which is developed by <a href="https://www.redhat.com/en">Redhat</a> and is now an option for organisations looking for an open solution to identity federation with AWS.</p>
<p>This post will detail how I configured Keycloak with AWS SAML federation.</p>
<p>To demonstrate Keycloak I have setup a docker-compose project which can be cloned from <a href="https://github.com/wolfeidau/keycloak-docker-compose">https://github.com/wolfeidau/keycloak-docker-compose</a>.</p>
<p>Assuming you have <a href="https://www.docker.com/docker-mac">docker for mac</a> installed you should be able to navigate to the project then run.</p>
<pre tabindex="0"><code>docker-compose up -d
</code></pre><p>Then to ensure it is all working you should be able to navigate to http://0.0.0.0:18080/auth/admin/master/console/#/realms/master.</p>
<h3 id="setup-of-the-aws-saml-client">Setup of the AWS SAML Client</h3>
<p>To simplify the automated setup we can export a client configuration file containing the AWS SAML configuration, in my case I did this in the master realm then exported it.</p>
<p>First thing you need to do is download <a href="https://signin.aws.amazon.com/static/saml-metadata.xml">https://signin.aws.amazon.com/static/saml-metadata.xml</a>, just put it in your Downloads folder.</p>
<p>Once you login navigate to clients http://0.0.0.0:18080/auth/admin/master/console/#/realms/master/clients then hit the create button and import the <code>saml-metadata.xml</code> file, then hit save.</p>
<figure>
    <img loading="lazy" src="/images/2017-11-05_keycloak-create-aws-client.png"/> <figcaption>
            Keycloak AWS Client Creation
        </figcaption>
</figure>

<p>Now configure:</p>
<ul>
<li><strong>IDP Initiated SSO URL Name</strong> to <code>amazon-aws</code></li>
<li><strong>Base URL</strong> to <code>/auth/realms/wolfeidau/protocol/saml/clients/amazon-aws</code></li>
</ul>
<figure>
    <img loading="lazy" src="/images/2017-11-05_keycloak-configure-aws-client.png"/> <figcaption>
            Keycloak AWS Client Configuration
        </figcaption>
</figure>

<p>Lastly under the Scope tab disable Full Scope Allowed, this will ensure we only pass through the roles configured in our client to AWS.</p>
<figure>
    <img loading="lazy" src="/images/2017-11-05_keycloak-configure-aws-client-scopes.png"/> <figcaption>
            Keycloak AWS Client Scope Configuration
        </figcaption>
</figure>

<p>Now you can navigate back to http://0.0.0.0:18080/auth/admin/master/console/#/realms/master/clients and hit the export button next to the aws client.</p>
<h3 id="keycloak-setup-using-admin-cli">Keycloak Setup Using Admin CLI</h3>
<p>As a big proponent of automation I really wanted to illustrate, and indeed learn how to automate setup of keycloak, hence the CLI approach.</p>
<p>To get the tools we need for this guide download keycloak from <a href="http://www.keycloak.org/downloads.html">Keycloak Downloads</a> and extract this to say <code>$HOME/Development/keycloak</code> then add <code>$HOME/Development/keycloak/bin</code> to your <code>$PATH</code> as per <a href="http://www.keycloak.org/docs/latest/server_admin/topics/admin-cli.html">Keycloak administration CLI docs</a>.</p>
<pre tabindex="0"><code>export PATH=$PATH:$HOME/Development/keycloak/bin
</code></pre><p><strong>Note:</strong> Commands which create new objects generate a unique GUID which looks like <code>6c684579-51a1-4bdf-a694-d641199874d8</code>, you will need to adjust those values in the subsequent commands.</p>
<p>Now we can use the administration CLI program to configure our keycloak service.</p>
<p>To test it out and configure your account locally.</p>
<pre tabindex="0"><code>kcadm.sh config credentials --server http://0.0.0.0:18080/auth --realm master --user admin
</code></pre><p>Create a realm, in my case I am naming this <code>wolfeidau</code>.</p>
<pre tabindex="0"><code>$ kcadm.sh create realms -s realm=wolfeidau -s enabled=true
</code></pre><p>Import the keycloak client for AWS and add it to the <code>wolfeidau</code> realm we created, the JSON file is in the <code>keycloak-docker-compose</code> project.</p>
<pre tabindex="0"><code>$ kcadm.sh create clients -r wolfeidau -s clientId=&#34;urn:amazon:webservices&#34; -s enabled=true -f urn-amazon-webservices.json
Created new client with id &#39;6c684579-51a1-4bdf-a694-d641199874d8&#39;
</code></pre><p>Create our AWS role under the AWS client, note this is an example name you will need to replace 123456789012 with your account id.</p>
<pre tabindex="0"><code>kcadm.sh create clients/6c684579-51a1-4bdf-a694-d641199874d8/roles -r wolfeidau -s &#39;name=arn:aws:iam::123456789012:role/wolfeidau-admin,arn:aws:iam::123456789012:saml-provider/docker-keycloak&#39; -s &#39;description=AWS Administration Access&#39;
Created new role with id &#39;docker-keycloak&#39;
</code></pre><p>Create a group to grant AWS administration access.</p>
<pre tabindex="0"><code>$ kcadm.sh create groups -r wolfeidau -s name=aws-admins
Created new group with id &#39;dd02ed86-dd49-47c6-bd8a-5f74844b56d0&#39;
</code></pre><p>Add a role to the group, note this is an example name you will need to replace 123456789012 with your account id.</p>
<pre tabindex="0"><code>$ kcadm.sh add-roles -r wolfeidau --gname &#39;aws-admins&#39; --cclientid &#39;urn:amazon:webservices&#39;  --rolename &#39;arn:aws:iam::123456789012:role/wolfeidau-admin,arn:aws:iam::123456789012:saml-provider/docker-keycloak&#39;
</code></pre><p>Create a user for testing.</p>
<pre tabindex="0"><code>$ kcadm.sh create users -r wolfeidau -s username=wolfeidau -s email=mark@wolfe.id.au -s enabled=true
Created new user with id &#39;eb02cbfd-fa9c-4094-a437-3a218be53fe9&#39;
</code></pre><p>Reset the users password and require update on login.</p>
<pre tabindex="0"><code>$ kcadm.sh update users/eb02cbfd-fa9c-4094-a437-3a218be53fe9/reset-password -r wolfeidau -s type=password -s value=NEWPASSWORD -s temporary=true -n
</code></pre><p>Add the user to our AWS administration group.</p>
<pre tabindex="0"><code>$ kcadm.sh update users/eb02cbfd-fa9c-4094-a437-3a218be53fe9/groups/dd02ed86-dd49-47c6-bd8a-5f74844b56d0 -r wolfeidau -s realm=wolfeidau -s userId=eb02cbfd-fa9c-4094-a437-3a218be53fe9 -s groupId=dd02ed86-dd49-47c6-bd8a-5f74844b56d0 -n
</code></pre><p>Export the metadata file required by AWS to setup the SAML provider.</p>
<pre tabindex="0"><code>$ kcadm.sh get -r wolfeidau clients/6c684579-51a1-4bdf-a694-d641199874d8/installation/providers/saml-idp-descriptor &gt; client-tailored-saml-idp-metadata.xml
</code></pre><h3 id="aws-setup">AWS Setup</h3>
<p>Create the AWS SAML Provider in your account using the metadata file exported from keycloak.</p>
<pre tabindex="0"><code>aws iam create-saml-provider --saml-metadata-document file://client-tailored-saml-idp-metadata.xml --name docker-keycloak
</code></pre><p>Deploy the cloudformation template supplied in the <code>keycloak-docker-compose</code> project, this contains the SAML SSO IAM roles and saves clicking around in the UI.</p>
<pre tabindex="0"><code>aws cloudformation create-stack --capabilities CAPABILITY_IAM --stack-name sso-roles --template-body file://sso-roles-cfn.yaml
</code></pre><p><strong>Note:</strong> You can just create the saml provider and launch the cloudformation from the AWS console.</p>
<h3 id="logging-into-aws">Logging Into AWS</h3>
<p>Now you should be ready to log into AWS using keycloak using the link http://0.0.0.0:18080/auth/realms/wolfeidau/protocol/saml/clients/amazon-aws.</p>
<h3 id="command-line-saml-authentication">Command Line SAML Authentication</h3>
<p>To enable the use of SAML by command line tools such as ansible and the AWS CLI my colleagues and I developed <a href="https://github.com/Versent/saml2aws">saml2aws</a>.</p>
<h3 id="references">References</h3>
<ul>
<li><a href="http://www.keycloak.org/docs/latest/server_admin/topics/admin-cli.html">Keycloak Admin CLI Docs</a></li>
<li><a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/troubleshoot_saml.html">Troubleshooting SAML 2.0 Federation with AWS</a></li>
<li><a href="https://stories.scandiweb.com/sign-in-to-amazon-aws-using-saml-protocol-and-keycloak-as-identity-provider-e3798387de99">Sign in to Amazon AWS using SAML protocol and Keycloak as Identity Provider</a></li>
</ul>
<!-- 
Import the file into the AWS console by navigating to [IAM Identity Providers](https://console.aws.amazon.com/iam/home?region=us-west-2#/providers), click on your provider which in my case named `docker-keycloak` and click on the Upload Metadata.

![IAM Identity Providers](/images/2017-11-05_keycloak-screen-iam-identity-providers.png) -->]]></content:encoded>
    </item>
    
  </channel>
</rss>
