<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Grip Engineering Blog]]></title><description><![CDATA[Grip Security provides comprehensive visibility, governance and data security to help enterprises effortlessly secure a burgeoning and chaotic SaaS ecosystem.]]></description><link>https://eng.grip.security</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1654780784728/9DNYj1AOu.png</url><title>Grip Engineering Blog</title><link>https://eng.grip.security</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 19:52:07 GMT</lastBuildDate><atom:link href="https://eng.grip.security/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How to Easily Start Arguing Using Data]]></title><description><![CDATA[I came across using data as a tool about six years into my career. Well, I knew the basic concepts and usage, but it wasn’t really a tool I used. I wasn’t comfortable enough with it to use it seamlessly, and I always found a way around it. But I shou...]]></description><link>https://eng.grip.security/how-to-easily-start-arguing-using-data</link><guid isPermaLink="true">https://eng.grip.security/how-to-easily-start-arguing-using-data</guid><category><![CDATA[General Programming]]></category><category><![CDATA[Data Science]]></category><category><![CDATA[pandas]]></category><dc:creator><![CDATA[Yuval Sarel]]></dc:creator><pubDate>Wed, 06 Sep 2023 11:19:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687179605219/c0a97c4a-151d-4563-85c9-50299836a686.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I came across using data as a tool about six years into my career. Well, I knew the basic concepts and usage, but it wasn’t really a tool I used. I wasn’t comfortable enough with it to use it seamlessly, and I always found a way around it. But I shouldn’t have.</p>
<p>In this post, I’ll explain how you can get so comfortable that you will use it all the time, not only when you are asked to. So if you want to have less friction when data is involved (or should be involved), this post is for you!</p>
<h1 id="heading-data-driven-decision-making-overview">Data-Driven Decision-Making - Overview</h1>
<p>Data-Driven Decision Making (DDDM) transitioned from a trend to a “must-have” for every organization long ago. It’s an essential skill for teams to prioritize tasks, locate pain points, or optimize processes. This is important not only for decision-makers, but also for team members trying to drive decisions and gain influence.</p>
<p>Looking into the formality of DDDM, I’ve found many different definitions, all amounting to pretty much the same. One example is <a target="_blank" href="https://www.tableau.com/learn/articles/data-driven-decision-making#:~:text=What%2520is%2520data%252Ddriven%2520decision,goals%252C%2520objectives%252C%2520and%2520initiatives.">this</a> definition from Tableau:</p>
<blockquote>
<p><em>“[The act of ] using facts, metrics, and data to guide strategic business decisions that align with your goals, objectives, and initiatives.”</em></p>
</blockquote>
<p>But we’re not here to talk about all the benefits or ways to implement DDDM - we’re here to talk about <strong>you,</strong> and how <strong>you</strong> can leverage the fact that this is how decisions are made in your company/team to your advantage.</p>
<p>First, we need to understand the basic structure of a “data project” and get some hands-on experience - and this is exactly what we’re going to do. In the next section, we’ll walk through the steps of a data project with a public database. This should give you the tools to perform “fun side projects” like this or implement the methods using your data and your team’s questions.</p>
<h1 id="heading-data-based-argument-cycle-example">Data-Based Argument Cycle - Example</h1>
<p>In this simulation, we’re working for a company that’s building a sports streaming product and, as part of the process, has built databases about basketball players and teams. We will go through all the steps of the process, explaining what exactly we’re doing in each step, and emphasizing where the process is probably different from what you might experience in your organization.</p>
<h2 id="heading-defining-the-question">Defining the question</h2>
<p>To define the question, we first need to define the problem, so let’s imagine a scenario:</p>
<p>You noticed that when ordering the users on their “Minutes Played” field, you get that the player who played the most minutes is Vin Baker, even though you know that’s not the case. You are convinced that something is off, but you want some proof to back this up. If you cannot access the code or if finding the issue in the code is difficult, you can try to back up your “this is a bug” argument with data!</p>
<h2 id="heading-accessing-the-data">Accessing the data</h2>
<p>The first thing you should do is learn where your organization stores its data and how to access it (for example, the data is stored in AWS RDS and is accessible using an SQL interface).</p>
<p>In our example (and because we’re not actually working for a streaming company), we’ll get the data from a public source - <a target="_blank" href="https://www.basketball-reference.com">https://www.basketball-reference.com</a>. We’ll first run this Python code to get the data:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

url = <span class="hljs-string">'https://www.basketball-reference.com/leagues/NBA_1995_totals.html'</span>
df = pd.read_html(url)[<span class="hljs-number">0</span>] 

df.to_csv(<span class="hljs-string">'all_players_1995.csv'</span>)
</code></pre>
<p>These lines will create a .csv file containing our database, which in this case will be a data frame (but for now is a table) where each row represents a player with their respective stats.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678357298841/26e0d14b-8d06-4ebc-bf25-737c6132e2af.png" alt /></p>
<p>While understanding all aspects of the data is important, it is usually unnecessary to go into every detail of the first query. Usually, you’d want to get a general <a target="_blank" href="https://www.grip.security/">grip</a> (badum-tss) and go into details only when a specific question is given. So let’s get to the question.</p>
<h2 id="heading-querying">Querying</h2>
<p>In the case of this example, we have a .csv file and not a fully-functioning DB with an SQL interface, so we’ll improvise such an interface. We’ll run the following Python lines creating a global DataFrame (called “df”) that will be accessible with SQL:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pandasql <span class="hljs-keyword">import</span> sqldf
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd

df = pd.read_csv(<span class="hljs-string">'all_players_1995.csv'</span>) <span class="hljs-comment"># read the CSV </span>
df = df[df[<span class="hljs-string">'Player'</span>] != <span class="hljs-string">'Player'</span>] <span class="hljs-comment"># drop unrelated rows (specific for this)</span>
df[<span class="hljs-string">'MP'</span>] = df[<span class="hljs-string">'MP'</span>].astype(int) <span class="hljs-comment"># convert column type (MP=Minutes Played)</span>
</code></pre>
<p>Now that we’re able to run SQL queries, let's first test the initial problem and locate the top five players, which will show the following result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678357312248/eda025e2-cb80-4164-b0db-2a98158874fc.png" alt /></p>
<p>It seems like our suspicions were right. Vin Baker does have the most minutes played! But feeling familiar with the data, we can notice that there is also a column called “Tm," representing the team of the player! This means that a player might have played for more than one team and thus have more than one row.</p>
<p>This is also easy to test using SQL, combining all rows of the same player and summing up their minutes:</p>
<pre><code class="lang-python">q = <span class="hljs-string">''' SELECT Player, SUM(MP)
FROM df
GROUP BY Player
ORDER BY 2 DESC
LIMIT 5'''</span>
sqldf(q, globals())
</code></pre>
<p>Which presents the following result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678357348273/9ef85ef3-a54a-4875-af41-0f2f01e1aee6.png" alt /></p>
<p>Bingo!</p>
<h2 id="heading-presenting-results">Presenting results</h2>
<p>Showing the tables above along with an explanation would have been enough to make your case, but in other cases, we may want to drive the point further with illustrations of the data. This is also relatively easy to do, like in this example showing the correlation between minutes played and points scored (column name is PTS):</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> plotly.express <span class="hljs-keyword">as</span> px

q = <span class="hljs-string">''' SELECT Player, SUM(MP) as MP, SUM(PTS) as PTS
FROM df
GROUP BY Player'''</span>

agg_df = sqldf(q, globals())
fig = px.scatter(agg_df, x=<span class="hljs-string">"MP"</span>, y=<span class="hljs-string">"PTS"</span>)
fig.show()
</code></pre>
<p>Which will show the following graph:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678357370891/01dbeabc-21dc-486d-b440-5c0e138c2e9c.png" alt /></p>
<h1 id="heading-just-another-tool">Just Another Tool</h1>
<p>Imagine you’re a developer, and you’ve found a bug in the data presented to a client. Instead of starting a cycle of meetings with your manager, a PM, and an analyst - you can come prepared for the first meeting with answers to many of the questions that they might ask:</p>
<ol>
<li><p>How badly is the client affected?</p>
</li>
<li><p>How many other clients were affected?</p>
</li>
<li><p>How will fixing the issue affect the database?</p>
</li>
</ol>
<p>The goal is not to master data but to feel comfortable enough with each part of the process so it feels easy. Even if you have a way around the issue, it will slow you down in the long run - it’s like debugging with “prints” because you don’t feel comfortable with the debugger.</p>
<p>As I said, I adopted data as a tool relatively late, not for lack of knowledge. But intuitively enough, the more I went through the cycle, the easier it became, and the quicker I found myself using it again. I now use the data in my organization as easily as I use Google Search. It’s just another tool.</p>
<p>So the next time you feel like you’d want to check something in your environment, I recommend you go through these steps and answer them on your own. Asking for help is okay (and even recommended), but ensure it’s only help and not outsourcing.</p>
<p>And if you can’t wait until you find the next opportunity in your team (or you’re afraid you’ll embarrass yourself, which you won’t), you can always use the code here and research some basketball. As they say - “practice makes perfect” :)</p>
]]></content:encoded></item><item><title><![CDATA[Enabling Chrome Extension Logging with Datadog: A Journey of Trial and Error]]></title><description><![CDATA[Logging is a critical aspect of developing and maintaining software applications, providing valuable insights into their behavior and aiding in troubleshooting. When it comes to Chrome extensions, enabling comprehensive logging from various component...]]></description><link>https://eng.grip.security/enabling-chrome-extension-logging-with-datadog-a-journey-of-trial-and-error</link><guid isPermaLink="true">https://eng.grip.security/enabling-chrome-extension-logging-with-datadog-a-journey-of-trial-and-error</guid><category><![CDATA[chrome extension]]></category><category><![CDATA[logging]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Datadog]]></category><dc:creator><![CDATA[Guy Goldenberg]]></dc:creator><pubDate>Mon, 24 Jul 2023 12:00:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687178039891/6badaff4-3f52-417f-84ca-b42495b7d81d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Logging is a critical aspect of developing and maintaining software applications, providing valuable insights into their behavior and aiding in troubleshooting. When it comes to Chrome extensions, enabling comprehensive logging from various components, such as content scripts, pop-ups, and background scripts, can present unique challenges. Additionally, streaming these logs to a centralized monitoring platform, like Datadog, adds another layer of complexity. In this blog post, we will delve into the intricacies of logging from different parts of a Chrome extension and explore effective strategies for streaming those logs to Datadog for comprehensive monitoring and analysis.</p>
<h2 id="heading-chrome-extension-logging-architecture">🕸️ Chrome Extension Logging: Architecture</h2>
<p>Our journey began with the challenge of understanding how to enable logging across all parts of a Chrome extension effectively. Since our goal is to stream all of the logs to a centralized location, we've started exploring different techniques to capture and consolidate logs from content scripts, pop-ups, and background scripts. By understanding the logging capabilities and limitations of each component, our goal was to create a unified and comprehensive logging solution.</p>
<p>One challenge we encountered during our journey was that content scripts in Chrome extensions may face restrictions imposed by Content Security Policies (CSPs), which can block HTTP requests required for logging purposes. To overcome this limitation, we found it necessary to centralize the logging mechanism within the service worker. By leveraging the capabilities of the service worker, we were able to capture and consolidate the logs from content scripts and other components, ensuring uninterrupted logging even in the presence of CSP restrictions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687177960305/fa0910d5-1b1b-45da-88df-0ecc6864e93f.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-streaming-extension-logs-to-datadog">🎥 Streaming Extension Logs to Datadog</h2>
<p>Now that we've got all of the logs in one centralized location (our service worker), we want to start streaming them to Datadog. We assumed that it would be very simple and straightforward to use the <a target="_blank" href="https://docs.datadoghq.com/logs/log_collection/javascript/">Datadog Browser Log Collection</a>. This assumption was a major mistake.</p>
<h4 id="heading-the-challenge-missing-document-and-window">🫥 The Challenge - Missing <code>document</code> and <code>window</code></h4>
<p>We've finished implementing the entire architecture as described. There was only one thing left and it was using <code>@datadog/browser-logs</code> to stream the logs to Datadog.</p>
<p>We've implemented the basic functionality and were disappointed to discover the following error:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1685546068625/3c9524f0-2459-4087-b0ce-5fd7c3cfc2eb.png" alt class="image--center mx-auto" /></p>
<p>It seems like the Datadog logging library for the web requires <code>document</code> and <code>window</code> configured in the global context. Since we are running inside a service worker, both of these variables don't exist in our context.</p>
<p>A short Google search raised the following open Github issues in the Datadog logging library</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/DataDog/browser-sdk/issues/432">https://github.com/DataDog/browser-sdk/issues/432</a></div>
<p> </p>
<p>Despite this setback, we saw the issue as surmountable. We began implementing minor polyfills for <code>document</code> and <code>window</code> to get the library working. Although the library stopped raising exceptions after implementing the necessary polyfills, it didn't send the logs to Datadog.</p>
<p>At this point, we've come to the understanding that we need to rethink our process and find new ways to achieve the same goal.</p>
<p>After some brainstorming, we've come up with the following options:</p>
<ul>
<li><p>❌ Stream the logs from one of the content scripts instead of the service worker</p>
<ul>
<li><p>CSP constraints on content scripts are very limiting</p>
</li>
<li><p>Many content scripts can be loaded at the same time to multiple tabs - which requires a complex architecture</p>
</li>
</ul>
</li>
<li><p>❌ Find another logging library and platform that supports Chrome extensions</p>
<ul>
<li>There's absolutely no logging library for Chrome extensions - All existing logging libraries are either for regular websites or for Node.JS</li>
</ul>
</li>
<li><p>❓ Upload the logs to our backend which in turn will stream the logs to Datadog</p>
<ul>
<li>This will dramatically increase the load on our servers</li>
</ul>
</li>
<li><p>❓ Implement the same API requests as Datadog's logging SDK performs</p>
<ul>
<li>This is an "undocumented" <code>browser-intake</code> API. It may break or change at any moment without us knowing.</li>
</ul>
</li>
<li><p>❓ Check out if there's an official Datadog API we can call directly instead of using a logging library using <code>@datadog/datadog-api-client</code></p>
<ul>
<li><p>There is an official API for logging - <a target="_blank" href="https://docs.datadoghq.com/api/latest/logs/#send-logs">Datadog Logs API reference</a></p>
</li>
<li><p>According to the documentation, the API requires an API key and not a <a target="_blank" href="https://docs.datadoghq.com/account_management/api-app-keys/#client-tokens">client token</a>.</p>
<ul>
<li><p><strong>Datadog API keys are private</strong>, we can't use them in our extension since they will be accessible to everyone</p>
<blockquote>
<p>For security reasons, API keys cannot be used to send data from a browser, as they would be exposed client-side in the JavaScript code. Instead, web browsers and other clients use client tokens to send data to Datadog.</p>
</blockquote>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="heading-the-solution-using-datadog-api">✅ The Solution - Using Datadog API</h4>
<p>During the research process, we discovered an interesting insight. Datadog client tokens and API keys are very similar to each other.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>API key</td><td><code>264f471f058cda49a768715edadca33a</code></td></tr>
</thead>
<tbody>
<tr>
<td><strong>Client token</strong></td><td><code>pubcea7edf6c6492736768cbaddb7354ee</code></td></tr>
</tbody>
</table>
</div><p>We can see that the client token structure is identical to the API key (32-byte hex encoded string). The only difference is that the client token starts with <code>pub</code> which probably stands for "public".</p>
<p>Based on these findings, we decided to try using the official Datadog logging API with the client token instead of the API key.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { client, v2 } <span class="hljs-keyword">from</span> <span class="hljs-string">"@datadog/datadog-api-client"</span>;

<span class="hljs-keyword">const</span> configuration = client.createConfiguration({
  authMethods: {
    apiKeyAuth: <span class="hljs-string">"pub**********"</span>, <span class="hljs-comment">// Our client token</span>
  },
});

<span class="hljs-keyword">const</span> apiInstance = <span class="hljs-keyword">new</span> v2.LogsApi(configuration);

<span class="hljs-keyword">const</span> params: v2.LogsApiSubmitLogRequest = {
  body: [
    {
      ddsource: <span class="hljs-string">"grip"</span>,
      ddtags: <span class="hljs-string">"env:staging,version:1.33.7"</span>,
      message: <span class="hljs-string">"Hello World"</span>,
      service: <span class="hljs-string">"guyg"</span>,
    },
  ],
};

<span class="hljs-keyword">await</span> apiInstance.submitLog(params);
</code></pre>
<details><summary>Snippet Notes</summary><div data-type="detailsContent">This snippet is based on <a target="_blank" href="https://docs.datadoghq.com/api/latest/logs/#send-logs">Datadog's Typescript API example</a>. The basic example didn't work out of the box because of the use of <code>Content-Encoding</code>. The <code>Content-Encoding</code> in Datadog's example causes the <code>datadog/datadog-api-client</code> library <code>bufferFrom</code> which is implemented for Node.JS and not for the web. After reading some of the library source code, we've identified that we can remove the default <code>Content-Encoding</code> from the example, and by that the entire flow works for the web.</div></details>

<p>And it worked! 🎉</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1685614981134/15e698b0-7afb-4e47-9ed2-93d5179e90e8.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-future-considerations"><strong>🔄 Future Considerations</strong></h2>
<p>Given our success with this approach, we'll likely keep using it in the future. However, it's important to remember that our workaround relies on a certain similarity between client tokens and API keys. If Datadog changes how client tokens or API keys are structured, our approach may need to be revised.</p>
<p>Moreover, since our workaround employs an 'undocumented' method - using the client token in place of the API key - there could be hidden limitations or restrictions we haven't yet encountered. As a result, we must continuously monitor our logging system and remain prepared for potential troubleshooting and adjustments.</p>
<h2 id="heading-final-thoughts"><strong>🧩 Final Thoughts</strong></h2>
<p>Logging in Chrome extensions and integrating with Datadog presented unique challenges because of specific architecture and platform issues. But, with some creative thinking and technical know-how, we managed to build a solution that works for us.</p>
<p>Our experience shows that problem-solving, understanding your tools, and determination can help overcome tough challenges. Our unique solution for Chrome extension logging is proof that it's possible to find new ways to tackle problems when we're willing to think differently.</p>
<p>We hope our story can help others facing similar challenges in their work. With the right mindset and a willingness to try new approaches, you can solve any problem that comes your way!</p>
<h2 id="heading-references"><strong>📚 References</strong></h2>
<ul>
<li><p><a target="_blank" href="https://developer.chrome.com/docs/extensions/mv3/getstarted/"><strong>Chrome Extensions Documentation</strong></a></p>
</li>
<li><p><a target="_blank" href="https://docs.datadoghq.com/logs/log_collection/javascript/"><strong>Datadog Browser Log Collection</strong></a></p>
</li>
<li><p><a target="_blank" href="https://docs.datadoghq.com/api/latest/logs/#send-logs"><strong>Datadog Logs API Reference</strong></a></p>
</li>
<li><p><a target="_blank" href="https://github.com/DataDog/browser-sdk/issues/432"><strong>GitHub: DataDog/browser-sdk Issue #432</strong></a></p>
</li>
<li><p><a target="_blank" href="https://docs.datadoghq.com/account_management/api-app-keys/#client-tokens"><strong>Datadog API Keys and Client Tokens Documentation</strong></a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Migrate your MongoDB architecture to PostgreSQL, the working way]]></title><description><![CDATA[At the start of 2022, nearly a year since its launch, the R&D group at Grip Security was experiencing quite a pain point.We had a centralized database based on MongoDB that all our deployments were using for daily sync and data transfer, and it was g...]]></description><link>https://eng.grip.security/migrate-your-mongodb-architecture-to-postgresql-the-working-way</link><guid isPermaLink="true">https://eng.grip.security/migrate-your-mongodb-architecture-to-postgresql-the-working-way</guid><category><![CDATA[MongoDB]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Databases]]></category><category><![CDATA[architecture]]></category><dc:creator><![CDATA[Ariel Tzentner]]></dc:creator><pubDate>Tue, 27 Jun 2023 11:13:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1687178483546/5cfceb0d-9e02-4e67-b99a-07967ab6b7d0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>At the start of 2022, nearly a year since its launch, the R&amp;D group at Grip Security was experiencing quite a pain point.<br />We had a centralized database based on MongoDB that all our deployments were using for daily sync and data transfer, and it was getting harder to maintain and keep track of changes.  Worst of all, we were using unstructured documents relationally, forcing our code to validate the documents’ structure and data as more types of information entered our pipeline.</p>
<p>We were already using PostgreSQL in a different database, so we figured we could use it in the centralized one as well. I was tasked with spearheading the migration project – defining the new database, writing the necessary code, and converting our system to use the new DB.</p>
<p>This blog post explains our process of migrating our centralized database architecture from MongoDB to PostgreSQL, without too much focus on data transfer. Let’s dive in!</p>
<h2 id="heading-first-step-defining-the-db-requirements-using-uml-diagrams">First step: Defining the DB requirements using UML diagrams</h2>
<p>Our Mongo database had several unused collections and excess data that no one was querying. We wanted to improve our data model and introduce new relations between our objects. So, the first step was to design the new desired database scheme, based on the existing data structure.</p>
<p>The clearest way of creating the DB structure and explaining the required relations was using a <strong>UML diagram</strong>. UML (Unified Modeling Language) was created to standardize software systems design. We used structural UML diagrams to help visualize the models (i.e., tables) with their properties (i.e., columns) and the relationships between them using specific connectors and arrows.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1687260045276/095bc595-ee90-487a-9616-a4be3074dbbd.png" alt class="image--center mx-auto" /></p>
<p>Explanation: The tables highlighted in blue are collections that existed in MongoDB with a similar name. The white ones are new tables to support our models. Note that we have different relationships between models: one-to-many (<em>interactions</em> to <em>saas_applications</em>), and many-to-many (<em>insight_to_interaction</em>, as demonstrated by the linking table).</p>
<p>Using a UML diagram, we could ensure we were creating the correct data structure and could update it without writing (or deleting) a single line of code.</p>
<h2 id="heading-second-step-defining-the-initial-orm-using-sqlalchemy-and-alembic">Second step: Defining the initial ORM using SQLAlchemy and Alembic</h2>
<p>After we were content with the database structure we designed in the chart, it was time to define these tables in our code. Our product’s backend is mostly written in Python, so we used the <strong>SQLAlchemy</strong> package to define our ORM<sup>1</sup>. SQLAlchemy is a Python package that allows you to create Python classes with columns as their properties to define the database tables. It also provides a clear API to query the tables using these classes.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CorpusInteraction</span>(<span class="hljs-params">Base, TimestampMixin</span>):</span>
    __tablename__ = <span class="hljs-string">"interactions"</span>
    id = Column(
        UUID(as_uuid=<span class="hljs-literal">True</span>),
        primary_key=<span class="hljs-literal">True</span>,
        default=uuid.uuid4,
        server_default=func.gen_random_uuid(),
    )
    event_type = Column(EVENT_TYPE, nullable=<span class="hljs-literal">False</span>)
    interaction_type = Column(INTERACTION_TYPE, nullable=<span class="hljs-literal">False</span>)
    auto_generated = Column(
        Boolean, default=<span class="hljs-literal">False</span>, server_default=false(), nullable=<span class="hljs-literal">False</span>
    )
    saas_application_id = Column(
        UUID(as_uuid=<span class="hljs-literal">True</span>),
        ForeignKey(CorpusSaaSApplication.id),
        default=null()
    )
    saas_application: CorpusSaaSApplication = relationship(
        <span class="hljs-string">"CorpusSaaSApplication"</span>, back_populates=<span class="hljs-string">"tagged_interactions"</span>
    )
    insights = relationship(
        <span class="hljs-string">"CorpusInsight"</span>,
        secondary=insights_to_interactions,
        back_populates=<span class="hljs-string">"interactions"</span>,
        lazy=<span class="hljs-string">"joined"</span>,
    )
</code></pre>
<p>Explanation: This is a class that represents the <em>interactions</em> table (as evident in the <code>__tablename__</code> variable), and each member of the class represents a column of the model. For convenience, we can also define relationships as members of the class, that depend on foreign key columns. For example, <em>saas_application</em> is a relationship that is based on the <em>saas_application_id</em> column in the table.</p>
<p>We also needed a way to automatically create the DB tables and track changes made to their structure. That’s where <strong>Alembic</strong> came in handy. Alembic is a Python package that was made to be used with SQLAlchemy to detect changes made to the ORM and create migrations to be applied to the database. You can think of it as “git” for database changes.</p>
<p>Alembic compares the current defined state of the ORM classes to the one existing in the database and generates the revision automatically (!) listing the changes to be made<sup>2</sup>.</p>
<p>Once we defined an Alembic workspace for the new database, creating the database (in an initial revision) was as simple as a single command line:</p>
<pre><code class="lang-bash">&gt; alembic revision --autogenerate -m <span class="hljs-string">"v1"</span>
&gt; alembic upgrade head
</code></pre>
<p>Further changes to the structure will be handled in the same manner – creating a new revision and upgrading the database. Easy as pie!</p>
<p>Reference notes:</p>
<ol>
<li><p>We chose to define our ORM model in Python, but any other ORM technology could be used instead - if you’re using mostly JavaScript/TypeScript code, <strong>Prisma</strong> is a cutting-edge alternative that defines the ORM model, generates code automatically for the created tables and handles DB changes by creating migrations automatically.</p>
</li>
<li><p>It is considered good practice to go over the code of Alembic’s auto-generated migrations and make changes if necessary.</p>
</li>
</ol>
<h2 id="heading-third-step-migrating-the-data-and-code-to-the-new-database">Third step: Migrating the data and code to the new database</h2>
<p>After we had settled on our desired database model, defined all the necessary relationships, and created an initial migration, it was time to move the data itself - and then came the fun part!</p>
<p>We didn’t need a super-generic solution here – a simple Python script that could read the MongoDB data and build the ORM objects accordingly would suffice! We could use any external third-party software for this task, but we were content with writing a simple once-off script that did the job. We first ran the script locally on a new database to test for failures and bugs, and only then, ran it on our intended production database.</p>
<p>The final task was the most demanding in terms of time and effort. Our centralized DB is used in the production code of our main product, so we needed to make sure the transition was as smooth as possible. To also get up and running quickly with the new PostgreSQL database, we created a module in our code that acted as a “compatibility layer.” Its job was to replace the previous MongoDB functions and calls with SQLAlchemy ones, but convert their results and output to the previous format. We created it so the existing code could still operate without failing because we switched the database infrastructure underneath.</p>
<p>As soon as the compatibility layer was complete and we observed that the production code was still working, we began the important final transition. We slowly switched our code to use the main SQLAlchemy functions and got rid of the compatibility layer - most of them had the same names and parameters!</p>
<p>In a matter of weeks, after we created the populated database, we could use it properly and had gotten rid of all the old Mongo code!</p>
<h2 id="heading-closing-thoughts">Closing thoughts</h2>
<p>Our migration process revolved specifically around MongoDB and PostgreSQL, and it worked quite well in our case. If we were to switch again to a different database infrastructure, we would probably need to build a strong foundation for the new technology first. Because we were already working with PostgreSQL, creating a new DB was easy.</p>
<p>I’m sure there are other things we could improve upon in our process, and I would love to receive your feedback in a comment or even by reaching out via email.</p>
<p>In the meantime, enjoy your fully migrated PostgreSQL database!</p>
]]></content:encoded></item><item><title><![CDATA[Enabling AWS IAM Group Access to an EKS Cluster Using RBAC]]></title><description><![CDATA[👋 Introduction
Growing pains are hard. Your organization is growing, apps are succeeding, and your engineering team has tripled—now, manage your Amazon Kubernetes Service (EKS) and permissions for an expansive team of non-stop complexity. Growing pa...]]></description><link>https://eng.grip.security/enabling-aws-iam-group-access-to-an-eks-cluster-using-rbac</link><guid isPermaLink="true">https://eng.grip.security/enabling-aws-iam-group-access-to-an-eks-cluster-using-rbac</guid><category><![CDATA[AWS]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[kubectl]]></category><category><![CDATA[EKS]]></category><category><![CDATA[AWS IAM]]></category><dc:creator><![CDATA[Guy Goldenberg]]></dc:creator><pubDate>Thu, 09 Jun 2022 19:03:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1654511338388/0ekiKvH9d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">👋 Introduction</h2>
<p>Growing pains are hard. Your organization is growing, apps are succeeding, and your engineering team has tripled—now, manage your Amazon Kubernetes Service (EKS) and permissions for an expansive team of non-stop complexity. Growing pains are hard.</p>
<p>At Grip, we encountered similar frustrations with managing our EKS cluster permissions due to our growing number of engineers and the increasing complexity of our systems. The time invested in managing our EKS permissions model grew significantly, encouraging us to seek out a comprehensive and sustainable solution to this problem.</p>
<h2 id="heading-tldr">📄 TL;DR</h2>
<ul>
<li>Kubernetes has 2 main methods for <strong>authorization:</strong><ul>
<li><strong><a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">RBAC</a></strong> - Role-based access control</li>
<li><a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/abac/">ABAC</a> - Attribute-based access control</li>
</ul>
</li>
<li><strong>Authentication</strong> can be done in multiple methods.<ul>
<li>EKS is configured to use <a target="_blank" href="https://github.com/kubernetes-sigs/aws-iam-authenticator#how-does-it-work">aws-iam-authenticator</a> which allows Kubernetes to integrate with AWS IAM.</li>
</ul>
</li>
<li>There are two configurations required in order to define authentication and authorization:<ul>
<li>Authorization - A <code>RoleBinding</code> or a <code>ClusterRoleBinding</code></li>
<li>Authentication - A <code>ConfigMap</code> named <code>aws-auth</code> which is under the <code>kube-system</code> namespace</li>
</ul>
</li>
<li><strong>There is <em>no standardized method</em> for providing IAM group access to an EKS cluster or namespace.</strong></li>
<li>Our solution utilizes an IAM role to authenticate the user group automatically and transparently when <code>kubectl</code> is being used.</li>
</ul>
<h2 id="heading-rbac-overview">🛂 RBAC Overview</h2>
<p>If you are familiar with Kubernetes RBAC, you can go ahead and jump to <a class="post-section-overview" href="#heading-eks-rbac-integration-with-iam">EKS RBAC Integration with IAM</a></p>
<p>Kubernetes's access control naming convention is similar to AWS IAM, so make sure you don't confuse the two.</p>
<ul>
<li>Kubernetes <code>Role</code> - A set of permissions for a namespace</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="f42044ec56c552ccb0ceb39410c99a31"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/guygrip/f42044ec56c552ccb0ceb39410c99a31" class="embed-card">https://gist.github.com/guygrip/f42044ec56c552ccb0ceb39410c99a31</a></div><ul>
<li>Kubernetes <code>ClusterRole</code> - A set of permissions for an entire <em>cluster</em></li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="970df7ee606ebd01addb574203cf4b36"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/guygrip/970df7ee606ebd01addb574203cf4b36" class="embed-card">https://gist.github.com/guygrip/970df7ee606ebd01addb574203cf4b36</a></div><ul>
<li>Kubernetes RBAC <code>RoleBinding</code> - A binding between <strong>one</strong> <code>Role</code> and <strong>one or more</strong> Users or Groups</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="db9461fef30d01e03b3992c472b4faae"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/guygrip/db9461fef30d01e03b3992c472b4faae" class="embed-card">https://gist.github.com/guygrip/db9461fef30d01e03b3992c472b4faae</a></div><ul>
<li>Kubernetes RBAC <code>ClusterRoleBinding</code> - A binding between <strong>one</strong> <code>ClusterRole</code> and <strong>one or more</strong> Users or Groups</li>
</ul>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="5c52659f7e3a5cf16873a4a1f8efbfca"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/guygrip/5c52659f7e3a5cf16873a4a1f8efbfca" class="embed-card">https://gist.github.com/guygrip/5c52659f7e3a5cf16873a4a1f8efbfca</a></div><p>Since this isn’t really a “Getting started with RBAC” kind of post. If you’re still not sure about the terminology, Check out <a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">Kubernetes RBAC docs</a> for further reading.</p>
<h2 id="heading-eks-rbac-integration-with-iam">🔑 EKS RBAC Integration with IAM</h2>
<p>Now that we’ve finished our overview of Kubernetes RBAC, let’s dive right in and see how it integrates with AWS IAM. The general idea is:</p>
<ol>
<li><p><code>aws-iam-authenticator</code> authenticates an IAM identity.</p>
<ul>
<li>This is achieved by <a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication">token authentication webhook</a>.</li>
</ul>
</li>
<li><p><code>aws-iam-authenticator</code> translates the authenticated identity to a Kubernetes RBAC identity.</p>
</li>
<li>Each IAM identity is mapped to a Kubernetes username or added to a group.</li>
<li>Kubernetes RBAC uses these translated identities to enforce permissions to different resources.<ul>
<li>Kubernetes RBAC is not aware of IAM identities.</li>
</ul>
</li>
</ol>
<p>AWS EKS uses a specific <code>ConfigMap</code> named <code>aws-auth</code> to manage the <strong>AWS</strong> Roles and <strong>AWS</strong> Users who are allowed to connect and manage the cluster (or namespace).</p>
<p>This is an example of <code>aws-auth.yaml</code> file:</p>
<div class="gist-block embed-wrapper" data-gist-show-loading="false" data-id="a20001c8a3eade08f49a34605a303a37"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a href="https://gist.github.com/guygrip/a20001c8a3eade08f49a34605a303a37" class="embed-card">https://gist.github.com/guygrip/a20001c8a3eade08f49a34605a303a37</a></div><h3 id="heading-authentication-and-authorization-flow">Authentication and Authorization Flow</h3>
<div class="hn-embed-widget" id="eks-rbac-image-auth-diagram"></div><h2 id="heading-adding-an-iam-group-to-eks-rbac">👨‍👩‍👦 Adding an IAM group to EKS RBAC</h2>
<p>According to AWS official documentation, <code>aws-auth.yaml</code> doesn’t support IAM groups translation to RBAC.</p>
<p>We conducted comprehensive research to verify this fact, and we found that there is no official documentation on the full format specification of <code>aws-auth.yaml</code>. So we read the implementation of <code>aws-iam-authenticator</code> to find alternative solutions.</p>
<p>We have built a full format specification based on the implementation - <a target="_blank" href="https://gist.github.com/guygrip/dc64629887d39d4fa7696936be07f71f"><code>aws-auth-specification.yaml</code></a></p>
<p>As you can see, <code>aws-auth</code> supports three IAM entities <code>mapAccounts</code>, <code>mapUsers</code> and, <code>mapRoles</code> but none of them allow us to map an IAM group.</p>
<p><strong>Therefore, it is not possible to define an IAM group for RBAC using standard methods.</strong></p>
<p>Furthermore, we found multiple open issues on GitHub asking for help with the same issue:</p>
<ul>
<li><a target="_blank" href="https://github.com/kubernetes-sigs/aws-iam-authenticator/issues/176">GitHub[aws-iam-authenticator] - Can I not add an IAM group to my ConfigMap?</a></li>
<li><a target="_blank" href="https://github.com/aws/containers-roadmap/issues/150">Github[containers-roadmap] - [EKS] [request]: MapGroups in ConfigMap</a></li>
<li><a target="_blank" href="https://github.com/kubernetes-sigs/aws-iam-authenticator/issues/262">GitHub[aws-iam-authenticator] - How to use IAM Groups?</a></li>
</ul>
<h2 id="heading-our-chosen-solution">✅ Our Chosen Solution</h2>
<p>After comparing the <a class="post-section-overview" href="#header-other-possible-solutions">different options</a>, we decided to use the most robust solution we found, which is the “assume role” method.</p>
<h3 id="heading-general-idea">General Idea</h3>
<ol>
<li>Create an IAM role - <code>dev.eks-access.role</code></li>
<li>Create a policy that allows it to manage EKS and attach it to the role - <code>dev.eks-access.policy</code></li>
<li>Add the role ARN under the <code>mapRoles</code> section of <code>aws-auth.yaml</code>.</li>
<li>Create a policy that allows assuming the role and attach it to your R&amp;D group - <code>dev.assume-eks-access-role.policy</code></li>
</ol>
<div class="hn-embed-widget" id="eks-rbac-image-aws-iam-relation"></div><h4 id="heading-solution-downside">Solution Downside</h4>
<p>The main downside of this solution is that R&amp;D team members must assume the <code>dev.eks-access.role</code> every time they want to interact with the cluster. A common workaround is to use AWS profiles, but this also requires changing the profile before accessing the cluster.</p>
<p>We wanted to get rid of this constraint. After some brainstorming, we assumed that the way <code>aws-iam-authenticator</code> works on our dev machines allows it to assume a role on the fly, and only for Kubernetes-related operations.</p>
<h4 id="heading-solving-the-downside-kubeconfig-authentication-configuration">Solving the Downside - <code>.kube/config</code> Authentication Configuration</h4>
<p>The way that <code>kubectl</code> (and other Kubernetes operations) authenticate to the cluster is using the configuration described in <code>.kube/config</code>.</p>
<p>This way, the <code>kubectl</code> command knows how to authenticate to EKS using the <code>aws-iam-authenticator</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">users:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">arn:aws:eks:us-east-2:123456789123:cluster/dev.my-eks-cluster</span>
  <span class="hljs-attr">user:</span>
    <span class="hljs-attr">exec:</span>
      <span class="hljs-attr">apiVersion:</span> <span class="hljs-string">client.authentication.Kubernetes.io/v1alpha1</span>
      <span class="hljs-attr">args:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--region</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">us-east-2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">eks</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">get-token</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">--cluster-name</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">dev.my-eks-cluster</span>
      <span class="hljs-attr">command:</span> <span class="hljs-string">aws</span>
      <span class="hljs-attr">env:</span> <span class="hljs-literal">null</span>
</code></pre>
<p>This authentication configuration is equivalent to the following command</p>
<pre><code class="lang-bash">aws --region us-east-2 eks get-token --cluster-name dev.my-eks-cluster
</code></pre>
<p>So, if we could assume the role only in this context, all the Kubernetes operations will be done with the assumed role, but other AWS operations will be done with the configured R&amp;D user.</p>
<p>And as we expected, both <code>get-token</code> and <code>update-kubeconfig</code> commands support <code>--role-arn</code> flag</p>
<pre><code class="lang-bash">OPTIONS
     . . .
    --role-arn (string)  
        Assume this role <span class="hljs-keyword">for</span> credentials when signing the token.
    . . .
</code></pre>
<p>Finally! we can just use <code>--role-arn</code> and assume the role on the fly <strong>only for Kubernetes operations</strong>! This eliminated our previous friction points 🥳</p>
<div class="hn-embed-widget" id="eks-rbac-image-auth-diagram-role-arn"></div><h3 id="heading-step-by-step-guide">Step-by-Step Guide</h3>
<p>This guide gives an example of how this should be done; however, it's suggested that a more strict permission model be implemented for production environments.</p>
<h4 id="heading-create-the-necessary-iam-entities">Create the Necessary IAM Entities</h4>
<p>First, we need to create all the needed IAM entities.</p>
<p> <strong>Create <code>dev.eks-access.role</code> Role</strong></p>
<table>
<tr>
<td>
ℹ️
</td>
<td>
We give the <code>root</code> account trust to assume the role. This means that the role will allow everyone in the account to assume the role <strong>from the role perspective</strong>.<br />
This doesn’t mean that everyone in the account can assume the role, because only users with a policy that allows them to assume the role will be able to do so.
</td>
</tr>
</table>


<p>Create the role and give it the following trust relationship:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Principal"</span>: {
                <span class="hljs-attr">"AWS"</span>: <span class="hljs-string">"arn:aws:iam::123456789123:root"</span>
            },
            <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>,
            <span class="hljs-attr">"Condition"</span>: {}
        }
    ]
}
</code></pre>
<p> <strong>Create <code>dev.assume-eks-access-role.policy</code> Policy</strong></p>
<p>This policy allows an IAM entity to assume the <code>dev.eks-access.role</code>.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor0"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"sts:AssumeRole"</span>,
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:iam::123456789123:role/dev.eks-access.role"</span>
        }
    ]
}
</code></pre>
<p> <strong>Create <code>dev.eks-access.policy</code> Policy</strong></p>
<p>This is an example of a policy that effectively allows full access to access EKS</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Version"</span>: <span class="hljs-string">"2012-10-17"</span>,
    <span class="hljs-attr">"Statement"</span>: [
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor0"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: [
                <span class="hljs-string">"eks:ListClusters"</span>,
                <span class="hljs-string">"eks:DescribeAddonVersions"</span>,
                <span class="hljs-string">"eks:CreateCluster"</span>
            ],
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"*"</span>
        },
        {
            <span class="hljs-attr">"Sid"</span>: <span class="hljs-string">"VisualEditor1"</span>,
            <span class="hljs-attr">"Effect"</span>: <span class="hljs-string">"Allow"</span>,
            <span class="hljs-attr">"Action"</span>: <span class="hljs-string">"eks:*"</span>,
            <span class="hljs-attr">"Resource"</span>: <span class="hljs-string">"arn:aws:eks:*:123456789123:cluster/*"</span>
        }
    ]
}
</code></pre>
<p><strong>Connect the Dots</strong></p>
<p>Inside AWS IAM management console do the following:</p>
<ul>
<li>Attach <code>dev.eks-access.policy</code> to <code>dev.eks-access.role</code></li>
<li>Attach <code>dev.assume-eks-access-role.policy</code> to your desired group (e.g. <code>dev.rnd.group</code>)</li>
</ul>
<h4 id="heading-configure-eks-to-work-with-the-role">Configure EKS to Work With the Role</h4>
<p> <strong>Add the role to the cluster <code>aws-auth</code></strong></p>
<pre><code class="lang-bash">eksctl create iamidentitymapping --cluster {CLUSTER_NAME} --arn arn:aws:iam::123456789123:role/dev.eks-access.role --group system:masters --username aws_eks_access_role
</code></pre>
<p></p><table><p></p>
<p><tr></tr></p>
<p><td>
⚠️
</td></p>
<p><td>
We give the role <code>system:masters</code> permissions, meaning the role can modify <strong>everything</strong> in the cluster. Be cautious!
</td>

</p></table>
 <strong>Add the Role to the <code>ClusterRoleBinding</code></strong><p></p>
<p>Create a <code>cluster-role-binding.yaml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">rbac.authorization.Kubernetes.io/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRoleBinding</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">creationTimestamp:</span> <span class="hljs-literal">null</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">entire-cluster-admin-access</span>
<span class="hljs-attr">roleRef:</span>
  <span class="hljs-attr">apiGroup:</span> <span class="hljs-string">rbac.authorization.Kubernetes.io</span>
  <span class="hljs-attr">kind:</span> <span class="hljs-string">ClusterRole</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">cluster-admin</span>
<span class="hljs-attr">subjects:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">apiGroup:</span> <span class="hljs-string">rbac.authorization.Kubernetes.io</span>
    <span class="hljs-attr">kind:</span> <span class="hljs-string">User</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">aws_eks_access_role</span>
</code></pre>
<p></p><table><p></p>
<p><tr></tr></p>
<p><td>
⚠️
</td></p>
<p><td>
We give the role <code>cluster-admin</code> permissions, meaning the role can modify <strong>everything</strong> in the cluster. Be cautious!
</td>

</p></table>
Run this command to apply the new <code>ClusterRoleBinding</code> on the Kubernetes cluster:<p></p>
<pre><code class="lang-bash">kubectl apply -f cluster-role-binding.yaml
</code></pre>
<h4 id="heading-update-the-kubernetes-config-file-kubeconfig">Update the Kubernetes config file (<code>.kube/config</code>)</h4>
<p>After all the entities exist and all  configurations are set, all you need to do is to configure <code>.kube/config</code> to be able to access the EKS cluster using this command:</p>
<pre><code class="lang-bash">aws eks --region us-east-2 update-kubeconfig --name {CLUSTER_NAME} --role-arn arn:aws:iam::123456789123:role/dev.eks-access.role
</code></pre>
<h2 id="heading-other-possible-solutions">🗺️ Other Possible Solutions</h2>
<p>These are some other solutions that solve this issue from a different perspective:</p>
<ul>
<li><strong>Use Terraform to “manually” give access to all  users:</strong><ul>
<li>This solution isn’t comprehensive, as Terraform isn’t used by all organizations. Source - <a target="_blank" href="https://github.com/kubernetes-sigs/aws-iam-authenticator/issues/176#issuecomment-508562203">Can I not add an IAM group to my ConfigMap? · Issue #176 · kubernetes-sigs/aws-iam-authenticator · GitHub</a></li>
</ul>
</li>
<li><strong>Deploy <a target="_blank" href="https://github.com/ygrene/iam-eks-user-mapper">iam-eks-user-mapper</a></strong><ul>
<li>This project runs as a container in the cluster and modifies the configurations on the fly.</li>
</ul>
</li>
<li><strong>Use AWS Lambda to periodically update all cluster permissions.</strong></li>
</ul>
<h2 id="heading-wrap-up">🌯 Wrap Up</h2>
<p>This blog post was a comprehensive overview intended to solve a seemingly very simple problem.</p>
<p>By acquiring the relevant knowledge and understanding of some internal implementations, we managed to find a good workaround that provides a great solution to this problem. We hope that other organizations find value in this information, and use it to simplify and streamline their workloads using Kubernetes.</p>
<p>If you are interested in becoming a member of our team, we are hiring!</p>
<h2 id="heading-related-documentation">📖 Related documentation</h2>
<ul>
<li><a target="_blank" href="https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html">Enabling IAM user and role access to your cluster - Amazon EKS</a></li>
<li><a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/rbac/">Using RBAC Authorization | Kubernetes</a></li>
<li><a target="_blank" href="https://kubernetes.io/docs/reference/access-authn-authz/authorization/">Authorization Overview | Kubernetes</a></li>
<li><a target="_blank" href="https://medium.com/globant/rbac-and-eks-aws-step-by-step-e2f9c38f1aeb">EKS (AWS) AND RBAC, step by step. Introduction | by David De Juan Calvo | Globant | Medium</a></li>
<li><a target="_blank" href="https://github.com/kubernetes-sigs/aws-iam-authenticator#full-configuration-format">GitHub - kubernetes-sigs/aws-iam-authenticator</a></li>
<li><a target="_blank" href="https://ygrene.tech/mapping-iam-groups-to-eks-user-access-66fd745a6b77">Mapping IAM Groups to EKS User Access | by Zach Arnold | Ygrene Tech</a></li>
<li><a target="_blank" href="https://docs.aws.amazon.com/eks/latest/userguide/troubleshooting_iam.html#security-iam-troubleshoot-ConfigMap">Troubleshooting IAM - Amazon EKS</a></li>
<li><a target="_blank" href="https://aws.amazon.com/blogs/containers/kubernetes-rbac-and-iam-integration-in-amazon-eks-using-a-java-based-kubernetes-operator/">Kubernetes RBAC and IAM Integration in Amazon EKS using a Java-based Kubernetes Operator | Containers</a></li>
<li><a target="_blank" href="https://aws.amazon.com/premiumsupport/knowledge-center/eks-kubernetes-object-access-error/">https://aws.amazon.com/premiumsupport/knowledge-center/eks-kubernetes-object-access-error</a></li>
<li><a target="_blank" href="https://aws.github.io/aws-eks-best-practices/security/docs/iam/">Identity and Access Management - EKS Best Practices Guides</a></li>
<li><a target="_blank" href="https://github.com/keikoproj/aws-auth">GitHub - keikoproj/aws-auth: Manage the aws-auth config map for EKS Kubernetes clusters</a></li>
</ul>
]]></content:encoded></item></channel></rss>