<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>aws on Barney Parker</title><link>https://barneyparker.com/tags/aws/</link><description>Recent content in aws on Barney Parker</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright><lastBuildDate>Tue, 22 Sep 2020 18:36:55 +0000</lastBuildDate><atom:link href="https://barneyparker.com/tags/aws/index.xml" rel="self" type="application/rss+xml"/><item><title>Authenticate Users with Azure OIDC on AWS Application Load Balancers</title><link>https://barneyparker.com/posts/authenticate-users-with-azure-oidc-on-aws-application-load-balancers/</link><pubDate>Tue, 22 Sep 2020 18:36:55 +0000</pubDate><guid>https://barneyparker.com/posts/authenticate-users-with-azure-oidc-on-aws-application-load-balancers/</guid><description>&lt;p>Securing legacy applications which were deigned for internal use only has become a hot topic in these times of sudden remote working. There isn&amp;rsquo;t the time or money available to re-architect authentication, and its not always easy to provide remote accessibility to systems when the company doesn&amp;rsquo;t have the resources to implement it safely.&lt;/p>
&lt;p>Thankfully AWS Application Load Balancers provide a really simple method of adding an authentication layer to an application which doesn&amp;rsquo;t require any application code changes, and can be linked to an auth provider that you already have via OIDC, in this case the extremely common Office 365!&lt;/p>
&lt;h1 id="openid-connect-and-aws-application-load-balancers">OpenID Connect and AWS Application Load Balancers&lt;/h1>
&lt;p>OpenID Connect is an identity layer built on top of OAuth2 which can be used natively with AWS ALBs. An ALB requires a listener which has rules on how to forward traffic based on the incomming connection. In the vast majority of cases just a simple default listener rule to &amp;ldquo;forward&amp;rdquo; to the application target group is all that is used.&lt;/p>
&lt;p>In order to make use of OIDC, all we need to do is add a condition which checks for an OIDC auth cookie, and if its not present it starts the OIDC OAuth2 authentication flow!&lt;/p>
&lt;h1 id="creating-an-azure-app-registration">Creating an Azure App Registration&lt;/h1>
&lt;p>We need to create an application registration in Azure Active Directory. This will give us the various URLs and magic values we will need to be able to configure OIDC on our ALB.&lt;/p>
&lt;h2 id="create-the-app-registration">Create the App Registration&lt;/h2>
&lt;p>Log in to Azure at &lt;a href="https://portal.azure.com">portal.azure.com&lt;/a>&lt;/p>
&lt;p>Under &lt;strong>Azure Services&lt;/strong> click &lt;strong>Azure Active Directory&lt;/strong>&lt;/p>
&lt;p>In the left had menu, under &lt;strong>Manage&lt;/strong> click &lt;strong>App Registrations&lt;/strong>&lt;/p>
&lt;p>On the top bar, click &lt;strong>New registration&lt;/strong>&lt;/p>
&lt;p>Start by entering a name for your application. This helps identify the purpose of this configuration&lt;/p>
&lt;p>Under &lt;strong>Supported Account Types&lt;/strong> select the option &lt;strong>Accounts in this organizational directory only (&lt;!-- raw HTML omitted --> - Single tenant)&lt;/strong>. This will ensure only your companies&amp;rsquo; Office 365 users will be able to use our application.&lt;/p>
&lt;p>Finally, click the &lt;strong>Register&lt;/strong> button.&lt;/p>
&lt;p>Next we&amp;rsquo;re going to need to collect some information that we need to supply to the ALB.&lt;/p>
&lt;h2 id="create-the-client-secret">Create the Client Secret&lt;/h2>
&lt;p>In the left side menu, click &lt;strong>Certificates &amp;amp; secrets&lt;/strong>&lt;/p>
&lt;p>Under &lt;strong>Client Secrets&lt;/strong> click &lt;strong>New client secret&lt;/strong>&lt;/p>
&lt;p>Add a description to remind you what this secret is for. The application registration could be used for multiple applications, but each application should have its own secret, so the description will be helpful if the secret ever needs to be removed.&lt;/p>
&lt;p>Select an expiry. Most likely &lt;strong>Never&lt;/strong> is the best choice here.&lt;/p>
&lt;p>Finally click the &lt;strong>Add button&lt;/strong>&lt;/p>
&lt;p>The Client Secrets list will now contain your new secret. Copy the &lt;strong>Value&lt;/strong> field and add it to the following Terraform code:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">variable&lt;/span> &lt;span class="s2">&amp;#34;oidc_client_secret&amp;#34;&lt;/span> {
&lt;span class="n"> default&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;paste secret here&amp;gt;&amp;#34;&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="collect-our-other-information">Collect Our Other Information&lt;/h2>
&lt;p>On the left hand menu again, click &lt;strong>Overview&lt;/strong>&lt;/p>
&lt;p>In the &lt;strong>Essentials&lt;/strong> pane, copy the &lt;strong>Application (client) ID&lt;/strong> and paste it in to the following Terraform code:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">variable&lt;/span> &lt;span class="s2">&amp;#34;oidc_client_id&amp;#34;&lt;/span> {
&lt;span class="n"> default&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;paste client id here&amp;gt;&amp;#34;&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next copy the &lt;strong>Directory (tenant) ID&lt;/strong> and paste in to the following Terraform code:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">variable&lt;/span> &lt;span class="s2">&amp;#34;oidc_tenant_id&amp;#34;&lt;/span> {
&lt;span class="n"> default&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;lt;paste tenant id here&amp;gt;&amp;#34;&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>And we have all the information we need to go ahead and implement OIDC in our listener.&lt;/p>
&lt;h2 id="redirect-uris">Redirect URIs&lt;/h2>
&lt;p>Part of the OAuth flow requires the load balancer to redirect to the Azure authorization endpoint. We need to configure this in Azure&lt;/p>
&lt;p>In the &lt;strong>Overview&lt;/strong> / &lt;strong>Essentials&lt;/strong> block, click the &lt;strong>Redirect URIs&lt;/strong> link&lt;/p>
&lt;p>Click the &lt;strong>Add Platform&lt;/strong> button&lt;/p>
&lt;p>Click &lt;strong>Web&lt;/strong>&lt;/p>
&lt;p>Enter the url to the root of the load balancer and append &lt;code>/oauth2/idpresponse&lt;/code>. e.g. if our app is at &lt;code>https://app.exmaple.com&lt;/code> this should show &lt;code>https://app.exmaple.com/oauth2/idpresponse&lt;/code>&lt;/p>
&lt;p>A little further down the page, under &lt;strong>Implicit Grants&lt;/strong> tick the option &lt;strong>Access Tokens&lt;/strong>, and untick &lt;strong>ID Tokens&lt;/strong>&lt;/p>
&lt;h1 id="create-our-oidc-alb-listeners">Create our OIDC ALB Listener(s)&lt;/h1>
&lt;p>For OIDC to work we need to be using HTTPS. It&amp;rsquo;s unwise to expose an application over HTTP anyway, so lets enforce this by adding a listener with a default action that redirects http traffic to https:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_lb_listener&amp;#34; &amp;#34;http&amp;#34;&lt;/span> {
&lt;span class="n"> load_balancer_arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_lb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">application&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">arn&lt;/span>
&lt;span class="n"> port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;80&amp;#34;&lt;/span>
&lt;span class="n"> protocol&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HTTP&amp;#34;&lt;/span>
&lt;span class="k">default_action&lt;/span> {
&lt;span class="n"> type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;redirect&amp;#34;&lt;/span>
&lt;span class="k">redirect&lt;/span> {
&lt;span class="n"> port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;443&amp;#34;&lt;/span>
&lt;span class="n"> protocol&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HTTPS&amp;#34;&lt;/span>
&lt;span class="n"> status_code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HTTP_301&amp;#34;&lt;/span>
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we need an HTTPS listener&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_lb_listener&amp;#34; &amp;#34;https&amp;#34;&lt;/span> {
&lt;span class="n"> load_balancer_arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_lb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">application&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">arn&lt;/span>
&lt;span class="n"> port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;443&amp;#34;&lt;/span>
&lt;span class="n"> protocol&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;HTTPS&amp;#34;&lt;/span>
&lt;span class="n"> ssl_policy&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;ELBSecurityPolicy-2016-08&amp;#34;&lt;/span>
&lt;span class="n"> certificate_arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;arn:aws:iam::012345678999:server-certificate/test_cert_abc&amp;#34;&lt;/span>
&lt;span class="k">default_action&lt;/span> {
&lt;span class="n"> type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;authenticate-oidc&amp;#34;&lt;/span>
&lt;span class="k">authenticate_oidc&lt;/span> {
&lt;span class="n"> authorization_endpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://login.microsoftonline.com/${var.oidc_tenant_id}/oauth2/v2.0/authorize&amp;#34;&lt;/span>
&lt;span class="n"> client_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">var&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">oidc_client_id&lt;/span>
&lt;span class="n"> client_secret&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">var&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">oidc_client_secret&lt;/span>
&lt;span class="n"> issuer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://login.microsoftonline.com/${var.oidc_tenant_id}/v2.0&amp;#34;&lt;/span>
&lt;span class="n"> token_endpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://login.microsoftonline.com/${var.oidc_tenant_id}/oauth2/v2.0/token&amp;#34;&lt;/span>
&lt;span class="n"> user_info_endpoint&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;https://graph.microsoft.com/oidc/userinfo&amp;#34;&lt;/span>
}
}
&lt;span class="k">default_action&lt;/span> {
&lt;span class="n"> type&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;forward&amp;#34;&lt;/span>
&lt;span class="n"> target_group_arn&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_lb_target_group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">application&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">arn&lt;/span>
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="outbound-traffic-alb">Outbound Traffic ALB&lt;/h2>
&lt;p>An essential part to add is an Egress rule to the security group attached to the ALB for HTTPS traffic to allow the ALB to contact Azure to confirm tokens:&lt;/p>
&lt;div class="highlight">&lt;pre class="chroma">&lt;code class="language-hcl" data-lang="hcl">&lt;span class="k">resource&lt;/span> &lt;span class="s2">&amp;#34;aws_security_group_rule&amp;#34; &amp;#34;applications_alb_egress_https&amp;#34;&lt;/span> {
&lt;span class="n"> from_port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">443&lt;/span>
&lt;span class="n"> to_port&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="m">443&lt;/span>
&lt;span class="n"> protocol&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;tcp&amp;#34;&lt;/span>
&lt;span class="n"> cidr_blocks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;0.0.0.0/0&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;span class="n"> security_group_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">aws_security_group&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">app_alb&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="k">id&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="notes">Notes&lt;/h1>
&lt;p>For brevity I have not included the Terraform code for the load balancer or target group, however this is relatively trivial to add.&lt;/p>
&lt;p>Once added you should find a couple of extra headers in the requests forwarded to our application:&lt;/p>
&lt;p>&lt;code>x-amzn-oidc-accesstoken&lt;/code> holds the full JSON Web Token (JWT)
&lt;code>x-amzn-oidc-data&lt;/code> is the user claims in JWT format
&lt;code>x-amzn-oidc-identity&lt;/code> contains the subject field (&lt;code>sub&lt;/code>) from the user info endpoint&lt;/p></description></item></channel></rss>