<?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: Julia Salem</title>
    <description>The latest articles on DEV Community by Julia Salem (@jsal570).</description>
    <link>https://dev.to/jsal570</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F597029%2Fc1012de9-e00c-4002-a7a4-eb8984e38bbc.jpeg</url>
      <title>DEV Community: Julia Salem</title>
      <link>https://dev.to/jsal570</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jsal570"/>
    <language>en</language>
    <item>
      <title>Least Privilege Is a Losing Race for Agents</title>
      <dc:creator>Julia Salem</dc:creator>
      <pubDate>Wed, 30 Sep 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/jsal570/least-privilege-is-a-losing-race-for-agents-2glm</link>
      <guid>https://dev.to/jsal570/least-privilege-is-a-losing-race-for-agents-2glm</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work at Eon, on the content side, and we sell what control 5 describes. The other five need no vendor, and control 5 carries a build-it-yourself path. The sources are OWASP, Microsoft, Anthropic's own numbers, Hugging Face's incident timeline and a founder's postmortem of losing his own database.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Restricting what an AI agent can reach is a losing race
&lt;/h2&gt;

&lt;p&gt;You strip a permission on Monday, and an engineer with a blocked ticket grants it back by Thursday. You put a human approval in front of the dangerous actions, and by Anthropic's own count, that human says yes 97% of the time. And the one control that catches &lt;code&gt;rm -rf&lt;/code&gt; on sight cannot see the API call that wipes a customer table, because to anything reading the traffic, the two look nothing alike.&lt;/p&gt;

&lt;p&gt;Every published guide to agent security says the opposite. OWASP, Microsoft, GitHub, and both companies at the center of the July intrusion landed on some version of scope it tighter and watch it closely. All of it is worth doing, as long as it is not where you stop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery is the control everyone left out
&lt;/h2&gt;

&lt;p&gt;OWASP calls the problem Excessive Agency: an agent with more functionality, permissions, or autonomy than its task needs. In the 2026 edition of its Top 10 for LLM Applications, published August 4, 2026, Excessive Agency jumped from sixth place to third, the biggest move on the list. The reason is blunt. OWASP checked its practitioner survey against 7,714 real incidents this year, and agentic deployments are where the damage is landing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Published&lt;/th&gt;
&lt;th&gt;What it recommends&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OWASP GenAI, Top 10 for LLM Apps 2026&lt;/td&gt;
&lt;td&gt;Aug 4, 2026&lt;/td&gt;
&lt;td&gt;Minimize tools and permissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Security, least privilege for agents&lt;/td&gt;
&lt;td&gt;Jul 16, 2026&lt;/td&gt;
&lt;td&gt;Dedicated identity per agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Well-Architected, governing agents&lt;/td&gt;
&lt;td&gt;Apr 13, updated Sep 9, 2026&lt;/td&gt;
&lt;td&gt;Enterprise agent and MCP policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugging Face, July intrusion timeline&lt;/td&gt;
&lt;td&gt;Jul 27, 2026&lt;/td&gt;
&lt;td&gt;Stricter isolation, short-lived credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenAI, Hugging Face incident report&lt;/td&gt;
&lt;td&gt;Aug 26, 2026&lt;/td&gt;
&lt;td&gt;More isolated sandboxes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The fuller versions run longer. OWASP also says to avoid open-ended tools like a shell and to require human approval for high-impact actions. Microsoft adds task-scoped roles, tool allowlists, just-in-time elevation and end-to-end audit logs. GitHub adds audit log streaming to a SIEM. Hugging Face adds narrower trust boundaries, blocked metadata access and faster detection. OpenAI adds restricted internet access and more compute on chain-of-thought monitoring.&lt;/p&gt;

&lt;p&gt;Read the right-hand column. It is one idea in five vocabularies: keep the agent away from things, and notice fast when it gets near them. Recovery does not appear in any row.&lt;/p&gt;

&lt;p&gt;OWASP would fairly answer that resilience lives in its other documents and that the Top 10 is scoped to application risk. The claim here is about emphasis, not error. Five documents on what to do about agents, and not one of them budgets a line for the day the controls fail.&lt;/p&gt;

&lt;p&gt;Every control in that column runs before the write, and when the last of them fails, the only question left is whether you can put the data back.&lt;/p&gt;

&lt;p&gt;Not one of these documents tells you how to put the data back, and from what we hear in customer conversations, that gap is now the biggest risk in the room.&lt;/p&gt;

&lt;h2&gt;
  
  
  Least privilege only works for so long
&lt;/h2&gt;

&lt;p&gt;To be fair, everything in that table shrinks the surface an agent can damage. A dedicated identity per agent means you revoke one thing instead of rotating a shared secret, and task-scoped roles cut the blast radius of an honest mistake. Allowlisted tools keep an agent from discovering a capability nobody meant to hand it.&lt;/p&gt;

&lt;p&gt;Nobody should stop doing this work. The argument is narrower: restriction cannot be the finish line, and the guidance published so far treats it as one. The open question is what happens once you finish scoping and the agents are still writing to production.&lt;/p&gt;

&lt;p&gt;Every permission you strip makes the agent worse at the job you hired it for, so the pressure to loosen comes from inside the team. Microsoft's guidance describes the pattern without flinching. A team grants a broad Reader role because the use case looks read-only, the workflow expands to fixing what it finds, and instead of redesigning the role, the team grants something broader and moves on.&lt;/p&gt;

&lt;p&gt;Anthropic measured the same erosion in Claude Code. As of June 2026, 49.5% of active Claude Code CLI users had written a rule telling the tool to stop asking before it runs shell commands. The share was growing about 5 percentage points every 5 weeks. Beyond those rules, 62% had switched Bash prompts off entirely or clicked "don't ask again," and 25% of interactive sessions started with permissions bypassed.&lt;/p&gt;

&lt;p&gt;Those are developers on their laptops, where a bad command usually costs a git reset. Now apply the same erosion to the service account an agent uses against a production database. The person deciding whether to widen that role is usually the engineer the agent is blocking, and the role does not get narrower after that.&lt;/p&gt;

&lt;p&gt;Bash is also the one channel the built-in undo in these tools does not cover, which we took apart in &lt;a href="https://www.eon.io/blog/claude-code-rewind-bash" rel="noopener noreferrer"&gt;Claude Code's /rewind Won't Bring Back What Bash Deleted&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So the permissions come back, and the last line of defense is the approval prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Human approval decays to 5% inside one session
&lt;/h2&gt;

&lt;p&gt;Claude Code users reject 3% of individual permission prompts and 39% of the plans put in front of them, per Anthropic's August 7, 2026 data. People do say no when they &lt;em&gt;actually read&lt;/em&gt; the prompt, but when dozens of prompts come up a day, they may start clicking "Approve" without reading.&lt;/p&gt;

&lt;p&gt;Anthropic measured the decline in a controlled study of 1,053 paid professional testers. A research vendor swapped one permission prompt in each session for a clearly dangerous command. Early in a session, testers caught about 17%. After 50 or more prior prompts, the catch rate fell to about 5%. Nobody reads the fortieth prompt of the hour.&lt;/p&gt;

&lt;h3&gt;
  
  
  Swapping the human for a model does not close the gap
&lt;/h3&gt;

&lt;p&gt;The obvious fix is to replace the tired human with a model, and Anthropic made its classifier the default on August 14. The classifier is better than the human and, by Anthropic's own account, still not good enough for production.&lt;/p&gt;

&lt;p&gt;The March 25 engineering write-up reports that the classifier let through 17% of the real cases where the agent went further than the user asked. The August post still recommends that people review the agent's actions themselves for high-stakes changes to production infrastructure. The review Anthropic recommends for production is the one that caught 13.6% of dangerous commands in its own study.&lt;/p&gt;

&lt;p&gt;And even a gate that never tires has a built-in blind spot.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dangerous part of an API call is the part no gate reads
&lt;/h2&gt;

&lt;p&gt;A classifier can catch &lt;code&gt;rm -rf&lt;/code&gt; because the command announces itself, and that is the whole reason a reviewer or a classifier stands a chance. A reviewer often won't catch a well-formed POST to a documented archive endpoint. Whether it is routine cleanup or a catastrophe depends entirely on the request body, the part of the call that names which records, and nothing in the request itself tells you which.&lt;/p&gt;

&lt;p&gt;Anthropic's classifier authors say as much. Their classifier deliberately never sees the results of the agent's earlier steps. So if the user never named the target, it cannot tell whether the agent pulled that ID from a query or made it up. A row delete in production is a valid SQL statement or API call, identical to the thousands of harmless ones before it.&lt;/p&gt;

&lt;p&gt;Hugging Face saw exactly this in July 2026, when OpenAI's models escaped an evaluation sandbox during an internal cyber-capability test and got into Hugging Face's production systems. Its dataset loader carried a URL allowlist, and the allowlist worked: it rejected every attempt to make the worker fetch a remote resource. So the agent stopped asking the worker to fetch things and instead acted on local ones, through a data file that pointed to a local path and a template field that ran code. In Hugging Face's words, neither is a URL fetch, so the allowlist never saw them.&lt;/p&gt;

&lt;p&gt;A gate can only judge what it can see, and it never sees which records the call names.&lt;/p&gt;

&lt;p&gt;July was a containment and exfiltration story, and recovery does nothing for exfiltration. The data-loss version had already happened in February.&lt;/p&gt;

&lt;h2&gt;
  
  
  One command took the database and its backups together
&lt;/h2&gt;

&lt;p&gt;On February 26, 2026, the founder of DataTalks.Club used a coding agent to fold a static website into the Terraform setup that already ran his production course platform. The Terraform state file was still on his old laptop, so Terraform believed nothing existed and started creating duplicates of resources that were already running.&lt;/p&gt;

&lt;p&gt;He stopped the apply and copied the old machine's Terraform archive over. The archive held the production state, and the agent unpacked it without him noticing. With the full picture in hand, the agent proposed &lt;code&gt;terraform destroy&lt;/code&gt; as the cleaner way to remove the duplicates. Auto-approve was on. He did not stop it.&lt;/p&gt;

&lt;p&gt;The destroy removed the VPC, RDS database, ECS cluster, load balancers, bastion host, and every nightly snapshot. Two and a half years of student submissions were gone: 1,943,200 rows in one table alone. The platform came back about 24 hours later, after he upgraded to AWS Business Support and an engineer found an internal snapshot that was not visible in his console.&lt;/p&gt;

&lt;p&gt;Nothing in that sequence is specific to AI. Someone with the same state file and permissions could have done it, minus the pause a person usually notices.&lt;/p&gt;

&lt;p&gt;His lesson, in his words: automated backups were deleted together with the database. Native snapshots and versioning live in the same account, under the same credentials, in the same lifecycle as the data. They cover an isolated mistake. Against a full-resource removal or an actor holding the account's credentials, the call that deletes the resource deletes them too.&lt;/p&gt;

&lt;h2&gt;
  
  
  6 controls to implement immediately
&lt;/h2&gt;

&lt;p&gt;A VP of architecture at a payments company put it plainly to our team: the scenario that worries them is their own code, ahead of ransomware. Here are some controls to implement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Give the agent its own identity.&lt;/strong&gt; An agent on a developer's credentials inherits everything the developer can do. A dedicated identity forces the decision about what this agent may touch, makes every action attributable, and ends a session with one revoked role.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Put human review outside the agent's reach, and only on irreversible actions.&lt;/strong&gt; Destroy, drop, replace, schema changes. A prompt inside the tool is the first thing a blocked engineer switches off, so put the review somewhere the agent cannot bypass. Gating everything is how you end up at 97%.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Treat state as production data.&lt;/strong&gt; Terraform state and migration history decide what "cleanup" means. State belongs in versioned remote storage with deletion protection, moves between machines through that storage and never by hand, and the agent's identity reads it at most.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Protect the resource at the platform level.&lt;/strong&gt; Deletion protection on the database, prevent_destroy on the module, Object Lock on the bucket. A rule in a config file is advice the model can override; these hold regardless of what the agent decides. They stop removal, do nothing about a valid write to the contents, and are not a backup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep a copy that survives the loss of the thing it protects.&lt;/strong&gt; A separate account with its own identities, so a credential that can destroy the infrastructure cannot reach the copy. Granular to the row or object, so you put back what the agent touched and nothing else. Native snapshots fail this test; DataTalks.Club proved it in one command.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(Eon, where I work, sells this as a managed service. Recovery copies land in isolated vaults, logically air-gapped and immutable, held in a separate account from your production environment, so they sit outside the blast radius of compromised production credentials. A restore comes back at the level of a single object or row, in minutes. You can build the same property without us. Replicate into an account whose credentials your production roles and your agents never hold. Turn on S3 Object Lock in compliance mode, or an AWS Backup vault lock, so the copies stay immutable for the retention period. Keep the state backend and the migration history under the same rule. Then rehearse one restore, so the recovery time is a number you have measured.)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Make every write attributable.&lt;/strong&gt; Identity plus audit logging means every mutating call ties to a specific agent, resource, set of records, and time. When a valid credential makes a valid call against the wrong records, nothing alerts, and the first hour is spent figuring out what changed. The hour is short only if you recorded the answer before the incident.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Controls one through four decide how often a bad change reaches production. Five and six decide what happens when one does, and five is the one almost nobody has. Cloud data protection assessments Eon ran across 78 companies' cloud environments counted 53,709 production resources. 61.2% of them had no recovery copy at all.&lt;/p&gt;

&lt;p&gt;Know, before the agent runs, exactly which records you could put back and how long it would take. DataTalks.Club got theirs back because an AWS support engineer found a copy their console never showed, and nobody should plan around that.&lt;/p&gt;




&lt;p&gt;We're running a hands-on version of this on October 8 in New York, 4 to 7pm at LOFT39. You break an environment with an agent, then recover it, in a sandbox built on Bedrock AgentCore. Details are on &lt;a href="https://www.eon.io/event/break-it-fix-it-prove-it-data-recovery-lab" rel="noopener noreferrer"&gt;the workshop page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll answer what I can in the comments. If you've run &lt;code&gt;terraform destroy&lt;/code&gt; against a stale state file, I'd like to know what you actually got back and how long it took.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>Claude Code's /rewind Won't Bring Back What Bash Deleted</title>
      <dc:creator>Julia Salem</dc:creator>
      <pubDate>Tue, 29 Sep 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/jsal570/claude-codes-rewind-wont-bring-back-what-bash-deleted-465h</link>
      <guid>https://dev.to/jsal570/claude-codes-rewind-wont-bring-back-what-bash-deleted-465h</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work at Eon, on the content side, and we sell cloud data protection, which is what the last two sections are about. Everything before them is Anthropic's and Huawei's own docs, four open-source READMEs and three public GitHub issues, all linked.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Claude Code checkpointing tracks, and the 5 things it skips
&lt;/h2&gt;

&lt;p&gt;As of September 14, 2026, the checkpointing page at code.claude.com says Claude Code captures a checkpoint before each user prompt and tracks changes made through its file editing tools. The Agent SDK page names them: Write, Edit, and NotebookEdit. &lt;code&gt;/rewind&lt;/code&gt; (or &lt;code&gt;Esc&lt;/code&gt; twice on an empty prompt) restores code, conversation, or both, from the 100 most recent checkpoints in a session.&lt;/p&gt;

&lt;p&gt;The limitations section is short and worth reading in full. Its first line: "Checkpointing does not track files modified by bash commands." The docs list these examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;file.txt
&lt;span class="nb"&gt;mv &lt;/span&gt;old.txt new.txt
&lt;span class="nb"&gt;cp &lt;/span&gt;source.txt dest.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK page adds &lt;code&gt;echo &amp;gt; file.txt&lt;/code&gt; and &lt;code&gt;sed -i&lt;/code&gt;. Redirects, in-place edits, moves, and any script the agent wrote 30 seconds earlier all sit outside the checkpoint.&lt;/p&gt;

&lt;p&gt;Four more limits follow. Rewind doesn't restore edits made by subagents, and the docs point you to git for those. A restore skips symlinked and hard-linked files and prints &lt;code&gt;Restored the code, but skipped N files&lt;/code&gt;. It doesn't track edits made outside the session, by hand or by another process. And the snapshots expire: a retention sweep deletes a session's file snapshots about 30 days after the last save, after which rewinding to one of them fails with &lt;code&gt;No files were restored&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The page ends on a heading Anthropic wrote itself: checkpoints are not a replacement for version control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it in 60 seconds
&lt;/h3&gt;

&lt;p&gt;In a scratch repo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask Claude Code to create &lt;code&gt;a.txt&lt;/code&gt; with its editor.&lt;/li&gt;
&lt;li&gt;Ask it to create &lt;code&gt;b.txt&lt;/code&gt; through the shell:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;hi &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; b.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Ask it to add a line to each, the same way it created them.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;/rewind&lt;/code&gt; and restore to the first checkpoint.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Per the docs, &lt;code&gt;a.txt&lt;/code&gt; comes back, and &lt;code&gt;b.txt&lt;/code&gt; stays exactly as the shell left it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where /rewind is the right tool
&lt;/h2&gt;

&lt;p&gt;The limits don't make checkpointing useless. Most of what a coding agent does, most of the time, is edit source files through its own tools, and every one of those edits is restorable in a keystroke. For a refactor that went sideways, or a failing test the agent "fixed" by deleting the assertion, &lt;code&gt;/rewind&lt;/code&gt; is the right tool and a good one.&lt;/p&gt;

&lt;p&gt;"Just use git" doesn't cover it either. Git holds committed state, untracked files vanish under &lt;code&gt;git clean&lt;/code&gt;, and the agent has the same shell you do, so &lt;code&gt;git checkout .&lt;/code&gt; is one tool call away. (&lt;a href="https://pypi.org/project/git-safepoint/" rel="noopener noreferrer"&gt;git-safepoint&lt;/a&gt; exists for exactly that reason.)&lt;/p&gt;

&lt;p&gt;What falls outside the checkpoint is a short list of commands, and most of them delete or overwrite files. An agent that decides a directory is stale runs &lt;code&gt;rm -rf&lt;/code&gt;, and a migration runs through a shell; neither goes anywhere near the Edit tool. Both can remove data that nothing else on the machine recorded.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 open-source tools that cover the bash channel
&lt;/h2&gt;

&lt;p&gt;Engineers hit this often enough that 4 open-source projects now exist to cover it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.rs/bashward" rel="noopener noreferrer"&gt;bashward&lt;/a&gt;&lt;/strong&gt; (Rust, v0.1.1) hooks Claude Code's PreToolUse event for the Bash tool and parses each command. Before it runs, bashward snapshots the paths that &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;, &lt;code&gt;dd&lt;/code&gt;, &lt;code&gt;sed -i&lt;/code&gt; and shell redirects are about to touch, using APFS clones on macOS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pypi.org/project/git-safepoint/" rel="noopener noreferrer"&gt;git-safepoint&lt;/a&gt;&lt;/strong&gt; (Python, v0.0.1, June 23, 2026) captures tracked and untracked files before every destructive command using git plumbing, so you can restore a single file without touching HEAD.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pypi.org/project/stepback/0.1.2/" rel="noopener noreferrer"&gt;stepback&lt;/a&gt;&lt;/strong&gt; (PyPI, v0.1.2) wraps any agent, watches the filesystem instead of one tool's hooks, and can rewind the agent's on-disk conversation transcript along with the files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.rs/crate/doover/latest/source/README.md" rel="noopener noreferrer"&gt;doover&lt;/a&gt;&lt;/strong&gt; (Rust, v0.2.3, September 2026) journals every shell action, classifies commands against a registry of 152 reversibility rules, and snapshots affected paths anywhere on disk, including outside the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All four are pre-1.0, and git-safepoint is at v0.0.1, so try them in a scratch repo before you put one in front of work you can't lose.&lt;/p&gt;

&lt;p&gt;The ask reached Anthropic directly too. On January 9, 2026, a user opened issue #16976 on the Claude Code repository, asking Anthropic to expose checkpoint restore programmatically and calling the current feature "effectively UI-only." Its status: closed as not planned.&lt;/p&gt;

&lt;p&gt;The patch tools are candid about their own ceiling. Doover's README has a section titled "What doover is not" that flags &lt;code&gt;DROP TABLE&lt;/code&gt;, &lt;code&gt;kubectl delete&lt;/code&gt; and force-pushes as unrecoverable, because no local snapshot can bring back remote state. By default, it keeps 7 days and 5 GiB of history on the same disk as the files it protects. The README's advice on that point comes down to 3 words: "Keep real backups."&lt;/p&gt;

&lt;p&gt;Nor is the limit Anthropic's alone. Huawei's CodeArts Agent documentation (updated July 29, 2026) draws the same line. Checkpoints there don't track file changes made through bash commands or external editors, and they expire after 15 days. Two vendors document the same limit, for the same architectural reason. A checkpoint is a copy of the files the agent's own tools touched. It can't see what a shell did.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the checkpoint restore itself deletes files
&lt;/h2&gt;

&lt;p&gt;The sharpest version of the problem is a report where the undo did the damage. On February 25, 2026, a user of GitHub's Copilot CLI filed issue #1675 about what happened when they chose "restore to checkpoint." Per the report, the rollback ran &lt;code&gt;git clean -fd&lt;/code&gt; on the repository root and deleted roughly a gigabyte of untracked evaluation output that no checkpoint had ever covered. It remains open as of September 9, 2026. It is a single user's account, with no confirmed root cause from GitHub in the public thread, and the mechanism is the same. The restore was itself a shell command, and nothing was checkpointing the shell.&lt;/p&gt;

&lt;p&gt;A related bug report on the Claude Code repository (#34368, March 14, 2026) describes the rewind option appearing on a turn where the agent had deleted a file. Selecting it did nothing to restore the file. Read it as a UI question, not a scandal. The docs say the code-restore options appear only when a checkpoint has tracked changes to revert, and a checkpoint can have tracked edits without holding the file you want back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checkpoint vs recovery copy, side by side
&lt;/h2&gt;

&lt;p&gt;A checkpoint answers one question: what did the agent change in my working tree since the last prompt. A recovery copy of a database or bucket (what most teams still call a backup) answers a different one: what did this row, object, or file look like at 02:13, before the agent's &lt;code&gt;UPDATE&lt;/code&gt; ran. The first lives on the agent's machine for a bounded time. The second lives in a separate account under separate credentials, and you restore it to a point in time, regardless of which tool or person made the change.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;File-tool edits&lt;/th&gt;
&lt;th&gt;Shell changes&lt;/th&gt;
&lt;th&gt;Rows and objects&lt;/th&gt;
&lt;th&gt;Undo window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code checkpoints&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Last 100 checkpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Huawei CodeArts checkpoints&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;15 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;git&lt;/td&gt;
&lt;td&gt;Committed only&lt;/td&gt;
&lt;td&gt;Committed only&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Until history is rewritten&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shell-hook tools&lt;/td&gt;
&lt;td&gt;Varies by tool&lt;/td&gt;
&lt;td&gt;Yes, local disk&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Bounded, same disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recovery copies, separate account&lt;/td&gt;
&lt;td&gt;Not the job&lt;/td&gt;
&lt;td&gt;Not the job&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Set by retention policy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two notes on that table. Claude Code's snapshots get swept about 30 days after the last save (docs read September 14, 2026), and Huawei's expire at 15 days (docs updated July 29, 2026). Git protects committed state only, and the agent can run &lt;code&gt;git clean&lt;/code&gt; or &lt;code&gt;git checkout .&lt;/code&gt; itself. Doover flags row and object operations as unrecoverable outright, and defaults to 7 days and 5 GiB on the same disk as the files it protects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same shell reaches your production database
&lt;/h2&gt;

&lt;p&gt;On a developer machine, the worst outcome is a lost afternoon. The same shell channel the checkpoint can't see is also how an agent reaches everything that isn't a source file: &lt;code&gt;psql&lt;/code&gt;, &lt;code&gt;aws s3 rm&lt;/code&gt;, a migration script, &lt;code&gt;kubectl&lt;/code&gt;. Anthropic's March 25, 2026 engineering post on auto mode lists, from its own internal incident log, an agent "attempting migrations against a production database." The only thing that changes is what the command can reach.&lt;/p&gt;

&lt;p&gt;Managed databases add a twist. A nightly snapshot tells you what the table looked like at 02:00. It doesn't tell you which rows the agent touched at 02:14, or which writes since then were legitimate. And if the recovery copies sit in the same account, and the agent's credentials can delete them, they inherit the agent's mistakes. Eon's August 5, 2026 post on agent-driven data loss walks through the public incidents where they did.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a real undo has to do at the data layer
&lt;/h2&gt;

&lt;p&gt;Set the bar for an undo the way you'd set it for a restore, because it is one. It records the data state at the moment of the operation, so nobody has to reconstruct it from logs afterward. Only what changed comes back: a file, an object, or a row, in place (the rest of the system never notices). The copy lives outside the credentials that made the change, and the restore finishes in minutes.&lt;/p&gt;

&lt;p&gt;Checkpoints in coding agents were never built for that, and their docs say so. Before the next agent session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start from a clean commit, ideally in a fresh worktree or container, so &lt;code&gt;rm -rf&lt;/code&gt; has a small blast radius.&lt;/li&gt;
&lt;li&gt;Put a shell hook in front of the Bash tool (bashward or doover), and treat it as local-disk protection only.&lt;/li&gt;
&lt;li&gt;Give the agent its own database credentials, so the database's own logs name the agent and not you when something needs unwinding.&lt;/li&gt;
&lt;li&gt;Time how long it takes to get 1 row back from your managed database's recovery copies. If the answer is "restore the instance to a side environment and export," write that number down.&lt;/li&gt;
&lt;li&gt;Keep recovery copies in a separate account, with credentials the agent never holds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Eon, where I work, sells the last two as a managed service. Recovery copies land in isolated vaults, logically air-gapped and immutable, held in a separate account from your production environment, so they sit outside the blast radius of compromised production credentials. A restore comes back at the level of a single file, object or row, in minutes. You can build the same property without us. Replicate into an account whose credentials your production roles and your agents never hold. Turn on S3 Object Lock in compliance mode, or an AWS Backup vault lock, so the copies stay immutable for the retention period. Give the agent its own database user. Then rehearse one restore, so the recovery time is a number you have measured.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can Claude Code's /rewind undo bash commands?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Anthropic's checkpointing documentation (read September 14, 2026) states that checkpointing does not track files modified by bash commands, with &lt;code&gt;rm&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, and &lt;code&gt;cp&lt;/code&gt; as the listed examples. Rewind restores only edits made through Claude Code's file editing tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does Claude Code checkpointing not track?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Files changed by bash commands, edits made by subagents, and edits made outside the session by hand or by another process. A restore also skips symlinked and hard-linked files, and file snapshots expire about 30 days after the session last saved one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you undo changes an AI agent made outside the editor?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open-source shell-hook tools snapshot files before destructive commands run: bashward and doover hook Claude Code's PreToolUse event, git-safepoint uses git plumbing to capture untracked files, and stepback watches the filesystem for any agent. All of them work on the local disk only.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can you recover data an AI agent deleted from a database?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Only from a recovery copy of the database that lives outside the agent's credentials. Coding-agent checkpoints cover files on the agent's machine, and shell-hook tools such as doover flag &lt;code&gt;DROP TABLE&lt;/code&gt; and other remote operations as unrecoverable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the difference between agent checkpointing and a backup?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A checkpoint is a bounded, same-machine copy of files an agent's own tools edited during a session. A backup, or recovery copy, is a copy of the data itself, held in a separate account under separate credentials. You can restore it to a point in time, regardless of which tool or person made the change.&lt;/p&gt;




&lt;p&gt;We're running a hands-on version of this on October 8 in New York, 4 to 7pm at LOFT39. You break an environment with an agent, then recover it, in a sandbox you don't have to clean up afterwards. Details are on &lt;a href="https://www.eon.io/event/break-it-fix-it-prove-it-data-recovery-lab" rel="noopener noreferrer"&gt;the workshop page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'll answer what I can in the comments. If you're running one of these four on something real, I'd rather hear how the retention defaults held up than hear that they work, and if the 60-second test behaves differently on your setup than the docs describe, say so and I'll correct the post.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>bash</category>
      <category>git</category>
      <category>opensource</category>
    </item>
    <item>
      <title>We checked 53,709 production resources for backups. 61% had none.</title>
      <dc:creator>Julia Salem</dc:creator>
      <pubDate>Mon, 28 Sep 2026 18:00:00 +0000</pubDate>
      <link>https://dev.to/jsal570/we-checked-53709-production-resources-for-backups-61-had-none-4o29</link>
      <guid>https://dev.to/jsal570/we-checked-53709-production-resources-for-backups-61-had-none-4o29</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure: I work at Eon, on the content side. Our team ran these assessments and this is our data. I didn't write the queries, but I sat with the engineer who did, and the methodology below includes what we couldn't see. Argue with it.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Most backup tools produce a coverage report, and most of those reports work from tags. They check the resources a policy already enrolled, which is usually everything tagged &lt;code&gt;environment=production&lt;/code&gt;, and report how many of those have a recent recovery point. The number comes back green.&lt;/p&gt;

&lt;p&gt;That number is real. It's just answering a smaller question than the one you asked.&lt;/p&gt;

&lt;p&gt;We wanted to know how much smaller. Across 78 companies' cloud environments on AWS, Google Cloud, and Azure, our team connected through a read-only role, discovered every resource, classified which ones were production, and checked each one for a recovery copy. 101,340 live resources, 53,709 of them production. Pulled September 15, 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only 14% of AWS production resources had a production tag
&lt;/h2&gt;

&lt;p&gt;This is the finding that explains all the others.&lt;/p&gt;

&lt;p&gt;Of about 47,000 AWS production resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;14% carried a tag saying production&lt;/li&gt;
&lt;li&gt;66% had no environment tag at all&lt;/li&gt;
&lt;li&gt;27% had no tags of any kind&lt;/li&gt;
&lt;li&gt;19% had an environment tag containing something that isn't an environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last group is the one you'll recognize. Real values found in the environment field: &lt;code&gt;base&lt;/code&gt;, &lt;code&gt;stable&lt;/code&gt;, &lt;code&gt;research&lt;/code&gt;, &lt;code&gt;corporate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Somebody typed those. The field was required, it was 6pm, and they put something in it. Then a backup policy read that field to decide what to protect.&lt;/p&gt;

&lt;p&gt;A coverage report keyed to &lt;code&gt;environment=production&lt;/code&gt; would have found about 1 in 7 of these resources. The rest never enter the count, so they can't show up as unprotected. They don't show up at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  61.2% had no recovery copy
&lt;/h2&gt;

&lt;p&gt;Of the 53,709 production resources, 61.2% had no backup policy, no recovery point, and no native cloud snapshot the assessment could detect.&lt;/p&gt;

&lt;p&gt;Three definitions, in case you think we picked the flattering one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Definition&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;No policy, no recovery point, no native snapshot detected&lt;/td&gt;
&lt;td&gt;61.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No backup policy at all&lt;/td&gt;
&lt;td&gt;62.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No policy, or a policy that's attached but blocked or over a limit&lt;/td&gt;
&lt;td&gt;65.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There's no reasonable definition that gets this below 61%.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unprotected resources are the old ones
&lt;/h2&gt;

&lt;p&gt;This is the number that changed how I think about the problem. I'd assumed the unprotected resources would be new ones. Spun up last sprint, policy hasn't caught up, on someone's list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Median age of a production resource with no recovery copy: &lt;strong&gt;502 days&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Median age of one with a recovery copy: &lt;strong&gt;363 days&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Share of unprotected resources older than 12 months: &lt;strong&gt;55%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So they're about four months older on average. Not a backlog. A resident population that nobody is watching.&lt;/p&gt;

&lt;p&gt;It's also the best argument against the obvious objection, that these resources are protected by something the assessment couldn't see. A resource that's been running 16 months with no policy, no tag, and no documentation isn't usually one somebody carefully set up a separate backup plan for.&lt;/p&gt;

&lt;h2&gt;
  
  
  0.9% of the gaps were on purpose
&lt;/h2&gt;

&lt;p&gt;Skipping backups is sometimes correct. A box that Terraform rebuilds on every deploy, a cache that refills from upstream, a low-value system where the cost genuinely isn't worth it. Fine.&lt;/p&gt;

&lt;p&gt;So we counted how often that decision was actually recorded. &lt;strong&gt;0.9%&lt;/strong&gt; of production resources had a documented exclusion. The other 61.2% had no policy and no note.&lt;/p&gt;

&lt;p&gt;The distinction only matters on one day, but it matters a lot that day. "We assessed it and accepted the risk" is an answer. "We didn't know it was there" is a different conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nobody was clean
&lt;/h2&gt;

&lt;p&gt;Among the 51-odd environments with 25 or more production resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typical environment: &lt;strong&gt;64.5%&lt;/strong&gt; of production unprotected&lt;/li&gt;
&lt;li&gt;More than half were above 50%&lt;/li&gt;
&lt;li&gt;Zero reached 100%&lt;/li&gt;
&lt;li&gt;Best in the set: &lt;strong&gt;99.8%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The median tracks the aggregate almost exactly, so this isn't a few disasters dragging an average.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is worse than it was two years ago
&lt;/h2&gt;

&lt;p&gt;For a long time, leaving a low-tier resource unprotected was a defensible cost decision. What broke production was a human mistake or a hardware fault, both rare, both usually fixable by redeploying from code and reloading from upstream.&lt;/p&gt;

&lt;p&gt;An AI agent with valid credentials has a different blast radius. It writes at machine speed to anything those credentials reach, and it has no idea how you tiered your estate. The cache, the queue, the read replica, and the database behind them go together.&lt;/p&gt;

&lt;p&gt;Terraform brings back the table. Not the rows.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to actually do about it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Count from the cloud side, not from the backup tool.&lt;/strong&gt; Pull live inventory from every account and region via read-only APIs, then match each production resource against a recovery point. Starting from your backup tool's inventory guarantees you only count what it already knows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don't classify production from one tag.&lt;/strong&gt; Look at the name, the account, what depends on it, and who gets paged when it breaks. Expect the untagged resources to be the oldest ones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Turn every gap into a written decision.&lt;/strong&gt; Attach a policy or write down why not, with an owner and a review date. A documented exclusion is defensible. An undecided gap isn't.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep recovery copies outside the blast radius.&lt;/strong&gt; Credentials broad enough to modify production data are often broad enough to delete snapshots in the same account. Bucket versioning protects against overwrites, not against a credential that can delete versions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Re-run the count on a schedule.&lt;/strong&gt; New resources arrive weekly. Unless a policy attaches at creation, the coverage you proved Tuesday is stale by Friday, and in our data those gaps persist for about 16 months.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Methodology, including what we couldn't see
&lt;/h2&gt;

&lt;p&gt;The numbers come from cloud data protection assessments run across 78 companies' cloud environments. Each connects through a single read-only IAM role and discovers and classifies every resource. No agents, no appliances, no manual tagging. Results aggregated and anonymized; nothing identifies a company, account, or resource.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cohort:&lt;/strong&gt; 78 environments, 101,340 live resources across AWS, GCP, and Azure, 53,709 production. Excluded: sandboxes, inactive environments, deleted resources, disconnected accounts, and child resources that inherit a parent's protection (an EBS volume attached to an EC2 instance counts once, through the instance). Distribution stats use environments with 25+ production resources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How "production" was determined:&lt;/strong&gt; from resource metadata — name, IDs, tags, the account it runs in, and how the rest of that account was already classified. Tags are one input among several, so a resource with no tags still gets classified.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The honest limitation:&lt;/strong&gt; native snapshot detection is uneven. The assessment sees native snapshots on 98% of RDS instances, 42% of FSx file systems, 24% of EC2 instances, and almost none of the other resource types. So for S3, DynamoDB, EFS, EKS, and anything on GCP or Azure, "no recovery copy" means no policy and no recovery point in our data. A native backup, or a backup made by another tool, could exist outside that view. The age and tagging findings are why I don't think that accounts for much of the 61%, but weigh it yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Also worth saying:&lt;/strong&gt; a classifier is a model of an environment, not the environment, and it has an error rate. And counting unprotected resources says nothing about whether the protected ones would actually restore. That's a separate study, and honestly a harder one.&lt;/p&gt;




&lt;p&gt;Full write-up with the rest of the findings is &lt;a href="https://www.eon.io/blog/cloud-backup-coverage-census" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I'll answer what I can in the comments and pull in the engineer who ran the queries for anything I can't. And if your environment looks nothing like this, I'd like to hear what you're doing differently.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>devops</category>
      <category>cloud</category>
      <category>data</category>
    </item>
    <item>
      <title>Implementing Traffic Policies in Kubernetes</title>
      <dc:creator>Julia Salem</dc:creator>
      <pubDate>Wed, 30 Jun 2021 15:45:26 +0000</pubDate>
      <link>https://dev.to/jsal570/implementing-traffic-policies-in-kubernetes-ie3</link>
      <guid>https://dev.to/jsal570/implementing-traffic-policies-in-kubernetes-ie3</guid>
      <description>&lt;p&gt;When setting up Kubernetes for the first time, one of the networking challenges you might face is how to safely grant outside clients access to your cluster. By default, pods within a cluster can communicate with all other pods and services. You should restrict access to anything outside of that group. &lt;/p&gt;

&lt;p&gt;In this post, we’ll take a closer look at how to introduce a process for monitoring and observing Kubernetes traffic using &lt;a href="https://kuma.io/" rel="noopener noreferrer"&gt;Kuma&lt;/a&gt;, a modern distributed control plane with a bundled Envoy Proxy integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up a Kuma Service Mesh
&lt;/h2&gt;

&lt;p&gt;Application stacks that run as individual containers need to communicate with one another and outside clients. To coordinate between all the requirements necessary to support such platforms—including security, routing and load-balancing—the concept of a &lt;strong&gt;service mesh&lt;/strong&gt; emerged. The goal of a service mesh is to provide seamless management of any service on the network. Thus, while an ingress controller handles the behavior of incoming traffic, a service mesh is responsible for overseeing &lt;em&gt;all&lt;/em&gt; aspects of the network, such as monitoring and configuration of the network.&lt;/p&gt;

&lt;p&gt;Kuma is one example of a service mesh. It’s an open source project that works across various environments, including Kubernetes and virtual machines, and supports multi-zone deployments. Kuma is supported by the same team that built &lt;a href="https://github.com/Kong/kong" rel="noopener noreferrer"&gt;Kong&lt;/a&gt;, a popular API gateway that simplifies network communication. Kong has a vast plugin ecosystem that enables you to easily deploy and manage HTTP requests, responses and routes across your entire fleet. Kuma works hand-in-hand with Kong, but the two projects don’t rely on each other, as we’ll see below. &lt;/p&gt;

&lt;p&gt;In addition to providing fine-grained traffic control capabilities, Kuma also offers rapid metrics and observability analyses. Being able to secure your networking access is only part of the solution. Since Kuma integrates with &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; for native data collection and &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt; for charting and viewing that data, you’ll be able to see precisely how your load balancing and client routing are behaving.&lt;/p&gt;

&lt;p&gt;Installing Kuma is a snap. First, you can download and run the installer like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -L https://kuma.io/installer.sh | sh -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, switch to the installation directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd kuma-1.1.2/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From here, you can run Kuma in multi-zone mode or standalone mode if Kuma is just in a single Kubernetes cluster. The command below will deploy Kuma in a single zone configuration, the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./kumactl install control-plane | kubectl apply -f -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For other environments, check out &lt;a href="https://kuma.io/docs/1.1.2/documentation/deployments/" rel="noopener noreferrer"&gt;the docs on deployment&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are several ways to interact with Kuma. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read-only through its &lt;a href="https://kuma.io/docs/1.1.4/documentation/gui/#getting-started" rel="noopener noreferrer"&gt;GUI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;For write/edit access –&lt;a href="https://kuma.io/docs/1.1.4/policies/introduction/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kuma.io/docs/1.1.4/documentation/http-api/#pagination" rel="noopener noreferrer"&gt;API&lt;/a&gt; (note that in a Kubernetes deployment, the API is also read-only, and interactions via kubectl are the correct process.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To access the GUI, you’ll first need to forward the API service port:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ kubectl port-forward svc/kuma-control-plane -n kuma-system 5681:5681
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you can navigate to &lt;a href="http://127.0.0.1:5681/gui" rel="noopener noreferrer"&gt;http://127.0.0.1:5681/gui&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  CNI Compatibility
&lt;/h2&gt;

&lt;p&gt;Before continuing, it’s important to introduce a minor point about configuration, which has major implications. &lt;/p&gt;

&lt;p&gt;Kubernetes uses &lt;a href="https://github.com/containernetworking/cni" rel="noopener noreferrer"&gt;the Container Network Interface (CNI) standard&lt;/a&gt; to configure networking for containers. This means that no matter how you design a CNI-compatible tool, it ought to be able to rely on the same set of protocols. Kubernetes provides an API that an ingress controller can use to set and manage the network policies. Multiple CNI-based projects have sprung up in response to enterprise-grade security and ease of use requirements. For example, one such project is &lt;a href="https://www.projectcalico.org/" rel="noopener noreferrer"&gt;Calico&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Depending on your needs, opting for a more customizable service mesh, like Kuma, can help you achieve your specific goals. For example, although Calico adheres to the &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" rel="noopener noreferrer"&gt;Network Policies&lt;/a&gt; Kubernetes provides, &lt;a href="https://docs.projectcalico.org/security/service-accounts" rel="noopener noreferrer"&gt;its format for setting up traffic rules&lt;/a&gt; is more opaque than Kuma. Kuma provides a way of configuring &lt;a href="https://kuma.io/policies/" rel="noopener noreferrer"&gt;network&lt;/a&gt; policies that run parallel to the first-class API Kubernetes provides. It should come as no surprise that Kuma is also compatible with CNI. This means you can easily swap out any network policies defined by Calico—or any project that uses a CNI-based protocol for Kuma’s traffic rules. The main differentiator between such projects comes down to features. Kuma, for example, can act as a service mesh, an observability platform &lt;em&gt;and&lt;/em&gt; a network policy manager all in one. Other projects may have different priorities, and it is the developer’s responsibility to make sure they can all interact with one another properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecting Traffic Policies in Kubernetes with Kuma
&lt;/h2&gt;

&lt;p&gt;With Kuma set up and running on Kubernetes, let’s see how to establish traffic rules to manage incoming access.&lt;/p&gt;

&lt;p&gt;Imagine the following scenario: an eCommerce platform that relies on two microservices that communicate to meet the business’s needs—let’s call them services backend1 and backend2. A third microservice acts as a public API, and any incoming request to this service privately queries the other two. We’d like to expose the API to the public but keep the other two microservices isolated from external networks.&lt;/p&gt;

&lt;p&gt;The pure ingress way to do this is to set up a &lt;a href="https://kubernetes.io/docs/concepts/services-networking/network-policies/" rel="noopener noreferrer"&gt;Network Policy&lt;/a&gt;. However, Kuma drastically simplifies this process with an easy-to-understand YAML DSL. You can define &lt;a href="https://kuma.io/docs/1.1.4/policies/traffic-permissions/" rel="noopener noreferrer"&gt;Traffic Permission policies&lt;/a&gt; that explicitly identify which sources the services can communicate specific destination services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kumactl apply -f - 
type: TrafficPermission
name: api-to-backends
mesh: default
sources:
  - match:
      service: 'publicAPI'
destinations:
  - match:
      service: 'backend1'
  - match:
      service: 'backend2'
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this manifest, the Traffic Permission policy gives the frontend permission to send traffic to the backend. The policy will reject any other source.&lt;/p&gt;

&lt;p&gt;Traffic Permission is just one of the policies that Kuma provides. Among other features, you can also set up a &lt;a href="https://kuma.io/docs/1.1.4/policies/health-check/#usage" rel="noopener noreferrer"&gt;Health Check policy&lt;/a&gt; to keep track of the health of every data plane proxy. This, too, makes use of familiar source and destination matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kumactl apply -f -
apiVersion: kuma.io/v1alpha1
kind: HealthCheck
mesh: default
metadata:
  name: web-to-backend-check
spec:
  sources:
  - match:
      service: 'publicAPI'
  destinations:
  - match:
      service: 'backend1'
  - match:
      service: 'backend2'
  conf:
    interval: 10s
    timeout: 2s
    unhealthyThreshold: 3
    healthyThreshold: 1
    tcp:
      send: Zm9v
      receive:
      - YmFy
      - YmF6
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  One Control Plane for Security, Observability and Routing
&lt;/h2&gt;

&lt;p&gt;The goal of any service mesh is to provide a single location to configure how your network behaves across your entire cluster. A service mesh can simplify much of the communication across disparate services. It’s often better to opt for a more restrictive network security rather than one which is open to any connection. Implementing a &lt;a href="https://kuma.io/docs/1.1.6/policies/mutual-tls/" rel="noopener noreferrer"&gt;zero-trust security policy&lt;/a&gt; with Kuma is a first-class feature, not an afterthought.&lt;/p&gt;

&lt;p&gt;If you’d like to learn more about proper access configuration, you can check out &lt;a href="https://kubernetes.io/docs/concepts/security/controlling-access/" rel="noopener noreferrer"&gt;the Kubernetes documentation on controlling access&lt;/a&gt; or &lt;a href="https://kubernetes.io/docs/concepts/security/pod-security-standards/" rel="noopener noreferrer"&gt;their best practices on pod security&lt;/a&gt;. &lt;a href="https://kuma.io/docs/1.1.4/security/certificates/" rel="noopener noreferrer"&gt;Kuma’s secure access patterns&lt;/a&gt; also provide some guidelines on how to define commonly-required networking policies.&lt;/p&gt;

&lt;p&gt;I hope you found this information on traffic policies in Kubernetes helpful. Get in touch via the &lt;a href="https://kuma.io/community/" rel="noopener noreferrer"&gt;Kuma community&lt;/a&gt; or learn more about other ways you can leverage Kuma for your connectivity needs with these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/getting-started-kuma-service-mesh/" rel="noopener noreferrer"&gt;Getting Started With Kuma Service Mesh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/authorize-api-opa-kuma" rel="noopener noreferrer"&gt;Authorizing Microservice APIs With OPA and Kuma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/service-mesh-observability" rel="noopener noreferrer"&gt;Automate Service Mesh Observability With Kuma&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The post &lt;a href="https://konghq.com/blog/traffic-policies-kubernetes/" rel="noopener noreferrer"&gt;Implementing Traffic Policies in Kubernetes&lt;/a&gt; appeared first on &lt;a href="https://konghq.com" rel="noopener noreferrer"&gt;KongHQ&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>opensource</category>
      <category>servicemesh</category>
    </item>
    <item>
      <title>Protecting Services With API Gateway Rate Limiting</title>
      <dc:creator>Julia Salem</dc:creator>
      <pubDate>Tue, 18 May 2021 13:00:19 +0000</pubDate>
      <link>https://dev.to/kong/protecting-services-with-api-gateway-rate-limiting-413</link>
      <guid>https://dev.to/kong/protecting-services-with-api-gateway-rate-limiting-413</guid>
      <description>&lt;p&gt;The Kong Gateway Rate Limiting &lt;a href="https://docs.konghq.com/hub/kong-inc/rate-limiting/" rel="noopener noreferrer"&gt;plugin&lt;/a&gt; is one of our most popular traffic control add-ons. You can configure the plugin with a policy for what constitutes “similar requests” (requests coming from the same IP address, for example), and you can set your limits (limit to 10 requests per minute, for example). This tutorial will walk through how simple it is to enable rate limiting in your &lt;a href="https://konghq.com/kong/" rel="noopener noreferrer"&gt;Kong Gateway&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limiting: Protecting Your Server 101
&lt;/h2&gt;

&lt;p&gt;Let’s take a step back and go over the concept of rate limiting for those who aren’t familiar.&lt;/p&gt;

&lt;p&gt;Rate limiting is remarkably effective and ridiculously simple. It’s also regularly forgotten. Rate limiting is a defensive measure you can use to prevent your server or application from being paralyzed. By restricting the number of similar requests that can hit your server within a window of time, you ensure your server won’t be overwhelmed and debilitated.&lt;/p&gt;

&lt;p&gt;You’re not only guarding against malicious requests. Yes, you want to shut down a bot that’s trying to discover login credentials with a brute force attack. You want to stop scrapers from slurping up your content. You want to safeguard your server from DDOS attacks.&lt;/p&gt;

&lt;p&gt;But it’s also vital to limit non-malicious requests. Sometimes, it’s somebody else’s buggy code that hits your API endpoint 10 times a second rather than one time every 10 minutes. Or, perhaps only your premium users get unlimited API requests, while your free-tier users only get a hundred requests an hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mini-Project for Kong Gateway Rate Limiting
&lt;/h2&gt;

&lt;p&gt;In our mini-project for this article, we’re going to walk through a basic use case: Kong Gateway with the Rate Limiting plugin protecting a simple API server. Here are our steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create Node.js Express API server with a single “hello world” endpoint.&lt;/li&gt;
&lt;li&gt;Install and set up Kong Gateway.&lt;/li&gt;
&lt;li&gt;Configure Kong Gateway to sit in front of our API server.&lt;/li&gt;
&lt;li&gt;Add and configure the Rate Limiting plugin.&lt;/li&gt;
&lt;li&gt;Test our rate limiting policies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After walking through these steps together, you’ll have what you need to tailor the Rate Limiting plugin for your unique business needs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to set up rate limiting for your API gateway with clicks instead of code? &lt;a href="https://konghq.com/kong-konnect/" rel="noopener noreferrer"&gt;Try Konnect for free &amp;gt;&amp;gt;&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Node.js Express API Server
&lt;/h2&gt;

&lt;p&gt;To get started, we’ll create a simple API server with a single endpoint that listens for a &lt;code&gt;GET&lt;/code&gt; request and responds with “hello world.” At the command line, create a project folder and initialize a Node.js application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/$ mkdir project
~/$ cd project
~/project$ yarn init
// Accept all defaults
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we’ll add Express, which is the only package we’ll need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ yarn add express
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, let’s create a simple server with our “hello world” endpoint. In your project folder, create an index.js file with these contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* PATH: ~/project/index.js
*/
const express = require('express')
const server = express()
const port = 3000
server.get('/', (req, res) =&amp;gt; {
 console.log(req.headers)
 res.status(200).send('Hello world!')
})
server.listen(port, () =&amp;gt; {
 console.log(`Server is listening on http://localhost:${port}`)
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, spin up your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your browser, you can visit &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. Here’s what you should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Flocal-host-hello-world.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Flocal-host-hello-world.png" alt="local host hello world" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s also use &lt;a href="https://insomnia.rest/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt;, a desktop client for API testing. In Insomnia, we send a &lt;code&gt;GET&lt;/code&gt; request to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Flocal-host-hello-world-insomnia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Flocal-host-hello-world-insomnia.png" alt="local host hello world in Insomnia" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our Node.js API server with its single endpoint is up and running. We have our &lt;code&gt;200 OK&lt;/code&gt; response.&lt;/p&gt;

&lt;p&gt;Keep that terminal window with the node running. We’ll do the rest of our work in a separate terminal window.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and Set Up Kong Gateway
&lt;/h2&gt;

&lt;p&gt;Next, we’ll &lt;a href="https://konghq.com/install/" rel="noopener noreferrer"&gt;install Kong Gateway&lt;/a&gt; to sit in front of our API server. These steps will vary depending on your local environment.&lt;/p&gt;

&lt;p&gt;Simple usage of the Rate Limiting plugin supports configuring Kong in DB-less mode with a declarative configuration. That means, instead of sending configurations to Kong Gateway one step at a time, with those configurations stored in a database, we can use a single declarative &lt;code&gt;.yml&lt;/code&gt; file for specifying our entire Kong configuration.&lt;/p&gt;

&lt;p&gt;After installing Kong, we generate a starter .yml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ kong config init
~/project$ tree -L 1
.
├── index.js
├── kong.yml
├── node_modules
├── package.json
└── yarn.lock
1 directory, 4 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll come back to that kong.yml file in a moment.&lt;/p&gt;

&lt;p&gt;Now, let’s tell Kong where to look for that declarative configuration file upon startup. In /etc/kong, there is a &lt;br&gt;
&lt;code&gt;kong.conf.default&lt;/code&gt; file that we’ll need to copy as &lt;code&gt;kong.conf&lt;/code&gt; and then edit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ cd /etc/kong
/etc/kong$ sudo su
root:/etc/kong$ cp kong.conf.default kong.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we edit the &lt;code&gt;kong.conf&lt;/code&gt; file. You’ll likely need root privileges to do this. There are two edits that we need to make:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PATH: /etc/kong/kong.conf

# line ~839: Uncomment this line and set to off
database = off

# line ~1023, Uncomment this line. Set to absolute path to kong.yml
declarative_config = /PATH/TO/YOUR/project/kong.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure Kong Gateway With Our Service and Routes
&lt;/h2&gt;

&lt;p&gt;Before we start up Kong, let’s edit the declarative configuration file generated in our project folder. It should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PATH: ~/project/kong.yml

_format_version: "2.1"

services:
- name: my-api-server
  url: http://localhost:3000/
  routes:
  - name: api-routes
    paths:
    - /api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s walk through what this configuration does. After setting the syntax version (2.1), we configure a new upstream service for which Kong will serve as an API gateway. Our service, which we name my-api-server, listens for requests at the URL &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;We associate a route (arbitrarily named &lt;br&gt;
&lt;code&gt;api-routes&lt;/code&gt;) for our service with the path &lt;code&gt;/api&lt;/code&gt;. Kong Gateway will listen for requests to &lt;code&gt;/api&lt;/code&gt;, and then route those requests to our API server at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With our declarative configuration file in place, we start Kong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in Insomnia, we send a &lt;code&gt;GET&lt;/code&gt; request through Kong Gateway, which listens at &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;http://localhost:8000&lt;/a&gt;, to the &lt;code&gt;/api&lt;/code&gt; path:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Finsomnia-get-request.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Finsomnia-get-request.png" alt="Insomnia get request" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that, by sending our request to port &lt;code&gt;8000&lt;/code&gt;, we are going &lt;em&gt;through&lt;/em&gt; Kong Gateway to get to our API server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add the Kong Gateway Rate Limiting Plugin
&lt;/h2&gt;

&lt;p&gt;Now that we have Kong Gateway sitting in front of our API server, we’ll add in the Rate Limiting Plugin and test it out. We will need to add a few lines to our &lt;code&gt;kong.yml&lt;/code&gt; declarative configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PATH: ~/project/kong.yml

_format_version: "2.1"

services:
- name: my-api-server
  url: http://localhost:3000/
  routes:
  - name: api-routes
    paths:
    - /api
  plugins:
  - name: rate-limiting
    config:
      minute: 5
      hour: 12
      policy: local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ve added the entire plugins section underneath our my-api-server service. We specify the name of the plugin, &lt;code&gt;rate-limiting&lt;/code&gt;. This name is &lt;em&gt;not&lt;/em&gt; arbitrary but refers to the actual &lt;code&gt;rate-limiting&lt;/code&gt; plugin in the Kong package.&lt;/p&gt;

&lt;p&gt;In this first run, we’ve configured the plugin with &lt;code&gt;minute: 5&lt;/code&gt;, which allows for up to five requests per minute. We’ve also added hour &lt;code&gt;: 12&lt;/code&gt;, which limits the requests per hour to 12. We’re using the local policy, which has to do with how Kong will store and increment request counts. We’ll talk about this policy configuration more below.&lt;/p&gt;

&lt;p&gt;Let’s restart Kong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, back in Insomnia, we test out sending some requests to our API endpoint. If you send a request every few seconds, you’ll see that the first five requests received a &lt;code&gt;200 OK&lt;/code&gt; response. However, if you exceed five requests within a minute:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Finsomnia-test-kong-rate-limiting.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Finsomnia-test-kong-rate-limiting.png" alt="Insomnia test Kong's rate limiting plugin" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Rate Limiting plugin detects that requests have exceeded the five-per-minute rule. It doesn’t let the subsequent request through to the API server. Instead, we get a &lt;code&gt;429 Too Many Requests&lt;/code&gt; response.&lt;/p&gt;

&lt;p&gt;Our plugin works!&lt;/p&gt;

&lt;p&gt;If you wait a minute and then try your requests again, you’ll see that you can get another five successful requests until Kong blocks you again with another &lt;code&gt;429&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At this point, we’ve sent 10 requests over several minutes. But you’ll recall that we configured our plugin with &lt;code&gt;hour: 12&lt;/code&gt;. That means our 11th and 12th requests will be successful. However, the system will reject our 13th request  even though we haven’t exceeded the five-per-minute rule . That’s because we’ll have exceeded the 12-per-hour rule.&lt;/p&gt;

&lt;p&gt;The Rate Limiting plugin allows you to limit the number of requests per second, minute, hour, day, month and year.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate Limiting “Similar” Requests
&lt;/h3&gt;

&lt;p&gt;We’ve configured Kong to count (and limit) requests to our server in our simple use case so far. More likely, we’ll want to apply our rate limits to &lt;em&gt;similar&lt;/em&gt; requests. By “similar,” we might mean “coming from the same IP address” or “using the same API key.”&lt;/p&gt;

&lt;p&gt;For example, let’s say that all users of our API server need to send requests with a unique API key set in headers as x-api-key. We can configure Kong to apply its rate limits on a per-API-key basis as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# PATH: ~/project/kong.yml

_format_version: "2.1"

services:
- name: my-api-server
  url: http://localhost:3000/
  routes:
  - name: api-routes
    paths:
    - /api
  plugins:
  - name: rate-limiting
    config:
      minute: 5
      hour: 12
      policy: local
      limit_by: header
      header_name: x-api-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Rate Limiting plugin will apply its limits by grouping requests according to a value found in the &lt;code&gt;x-api-key&lt;/code&gt; in the header. Requests with the same &lt;code&gt;x-api-key&lt;/code&gt; will be considered “similar” requests.&lt;/p&gt;

&lt;p&gt;Let’s restart Kong and see this in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ sudo kong restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back in Insomnia, we’ll adjust our &lt;code&gt;GET&lt;/code&gt; request slightly by adding a new &lt;code&gt;x-api-key&lt;/code&gt; header value. Let’s first set the value to user1. We send the request multiple times. Our first five requests return a &lt;code&gt;200 OK&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Fx-api-key-header-insomnia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Fx-api-key-header-insomnia.png" alt="x-api-key header in Insomnia to test rate limiting" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we exceed the five-per-minute rule, however, this is what we see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2FInsomnia-api-rate-limit-exceeded.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2FInsomnia-api-rate-limit-exceeded.png" alt="Insomnia API Rate Limit Exceeded" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we change the &lt;code&gt;x-api-key&lt;/code&gt; to a different value (for example, user2), we immediately get &lt;code&gt;200 OK&lt;/code&gt; responses:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Finsomnia-test-kong-rate-limiting-2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fuploads%2F2021%2F05%2Finsomnia-test-kong-rate-limiting-2.png" alt="Insomnia test Kong rate limiting" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Requests from &lt;code&gt;user2&lt;/code&gt; are counted separately from requests from &lt;code&gt;user1&lt;/code&gt;. Each unique &lt;code&gt;x-api-key&lt;/code&gt; value gets (according to our rate limiting rules) up to five requests per minute and up to 12 requests per hour.&lt;/p&gt;

&lt;p&gt;You can configure the plugin to &lt;code&gt;limit_by&lt;/code&gt; IP address, a header value, request path or even credentials (like &lt;a href="https://konghq.com/blog/kong-gateway-oauth2/" rel="noopener noreferrer"&gt;OAuth2&lt;/a&gt; or &lt;a href="https://konghq.com/blog/jwt-kong-gateway" rel="noopener noreferrer"&gt;JWT&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Counter Storage “Policy”
&lt;/h3&gt;

&lt;p&gt;In our example above, we set the &lt;code&gt;policy&lt;/code&gt; of the Rate Limiting plugin to &lt;code&gt;local&lt;/code&gt;. This policy configuration controls &lt;a href="https://docs.konghq.com/hub/kong-inc/rate-limiting/#implementation-considerations" rel="noopener noreferrer"&gt;how Kong will store request counters&lt;/a&gt; to apply its rate limits. The &lt;code&gt;local&lt;/code&gt; policy stores in-memory counters. It’s the simplest strategy to implement (there’s nothing else you need to do!). &lt;/p&gt;

&lt;p&gt;However, request counters with this strategy are only &lt;em&gt;mostly&lt;/em&gt; accurate. If you want basic protection for your server, and it’s acceptable if you miscount a request here and there, then the local policy is sufficient. &lt;/p&gt;

&lt;p&gt;The documentation for the Rate Limiting plugin instructs those with needs where “every transaction counts” to use the &lt;code&gt;cluster&lt;/code&gt; (writes to the database) or &lt;code&gt;redis&lt;/code&gt; (writes to Redis key-value store) policy instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Rate Limiting
&lt;/h2&gt;

&lt;p&gt;For many common business cases, the open source Rate Limiting plugin has enough configuration options to meet your needs. For those with more complex needs (multiple limits or time windows, integration with Redis Sentinel), Kong also offers their &lt;a href="https://docs.konghq.com/hub/kong-inc/rate-limiting-advanced/" rel="noopener noreferrer"&gt;Rate Limiting Advanced plugin&lt;/a&gt; for &lt;a href="https://konghq.com/kong-konnect/" rel="noopener noreferrer"&gt;Kong Konnect&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s briefly recap what we did in our mini-project. We spun up a simple API server. Then, we installed and configured Kong Gateway to sit in front of our API server. Next, we added the Rate Limiting plugin to count and limit requests to our server, showing how Kong blocks requests once we exceed certain limits within a window of time. &lt;/p&gt;

&lt;p&gt;Lastly, we demonstrated how to group (and count) requests as “similar” with the example of a header value. Doing so enabled our plugin to differentiate counts for requests coming from two different users so that you can apply that rate limit to each user separately.&lt;/p&gt;

&lt;p&gt;That’s all there is to it. Kong’s Rate Limiting plugin makes protecting your server ridiculously simple. As a basic traffic control defense measure, rate limiting can bring incredible power and peace of mind. You can find more details in the &lt;a href="https://docs.konghq.com/hub/kong-inc/rate-limiting/" rel="noopener noreferrer"&gt;rate limiting plugin doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any additional questions, post them on &lt;a href="https://discuss.konghq.com/" rel="noopener noreferrer"&gt;Kong Nation&lt;/a&gt;. To stay in touch, join the &lt;a href="https://konghq.com/community/" rel="noopener noreferrer"&gt;Kong Community&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now that you’re successfully protecting services with the Kong Gateway Rate Limiting plugin, you may find these other tutorials helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/custom-lua-plugin-kong-gateway" rel="noopener noreferrer"&gt;Creating Your First Custom Lua Plugin for Kong Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/openid-connect-api-gateway" rel="noopener noreferrer"&gt;Getting Started With Kong’s OpenID Connect Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/kong-gateway-oauth2/" rel="noopener noreferrer"&gt;4 Steps to Authorizing Services With the Kong Gateway OAuth2 Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://konghq.com/blog/jwt-kong-gateway" rel="noopener noreferrer"&gt;How to Use the Kong Gateway JWT Plugin for Service Authentication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="/kong-konnect"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fkonghq.com%2Fwp-content%2Fthemes%2Fkonghq%2Fassets%2Fimg%2Fcta-shortcode%2Fkonnect.jpg" alt="Get Started with Kong Konnect - The only full stack connectivity platform for cloud native architectures" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://konghq.com/blog/kong-gateway-rate-limiting/" rel="noopener noreferrer"&gt;Protecting Services With Kong Gateway Rate Limiting&lt;/a&gt; appeared first on &lt;a href="https://konghq.com" rel="noopener noreferrer"&gt;KongHQ&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>apigateway</category>
      <category>tutorial</category>
      <category>opensource</category>
      <category>ratelimiting</category>
    </item>
    <item>
      <title>Authentication with Kong's JWT Plugin</title>
      <dc:creator>Julia Salem</dc:creator>
      <pubDate>Mon, 15 Mar 2021 22:38:31 +0000</pubDate>
      <link>https://dev.to/kong/service-authentication-with-kong-s-jwt-plugin-1knm</link>
      <guid>https://dev.to/kong/service-authentication-with-kong-s-jwt-plugin-1knm</guid>
      <description>&lt;p&gt;As you build and maintain more applications, your authentication strategy becomes increasingly important. It may also be top of mind for your boss since technology leaders cited “improve application security” as one of their top priorities in this year’s &lt;a href="https://konghq.com/resources/digital-innovation-benchmark-2021/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community" rel="noopener noreferrer"&gt;Digital Innovation Benchmark&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Kong Gateway &lt;a href="https://docs.konghq.com/hub/kong-inc/jwt/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community" rel="noopener noreferrer"&gt;JWT plugin&lt;/a&gt; is one strategy for &lt;a href="https://konghq.com/learning-center/api-gateway/api-gateway-authentication/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community" rel="noopener noreferrer"&gt;API gateway authentication&lt;/a&gt;. JWT simplifies authentication setup, allowing you to focus more on coding and less on security.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication Is Tough
&lt;/h2&gt;

&lt;p&gt;You know you need a secure front door to your system. If requests don’t have the right credentials, the door should remain locked. If they do have the proper credentials, the entry should be smooth. &lt;/p&gt;

&lt;p&gt;But how do you verify that credentials are authentic? And how do you make sure there aren’t other ways for those without the right credentials to get into your system?&lt;/p&gt;

&lt;p&gt;Let’s walk through those scenarios as I demonstrate how to secure a service (in this case, an API server) with &lt;a href="https://konghq.com/kong/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community" rel="noopener noreferrer"&gt;Kong Gateway&lt;/a&gt; and its JWT plugin. I’ll cover all the steps to set up, configure and test the service — giving you the foundational knowledge needed to implement these tools independently.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/OjF95vVldxY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Concepts
&lt;/h2&gt;

&lt;p&gt;First, let’s cover the core technologies. If you’re already familiar with these and just want to get started, feel free to skip ahead.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is Kong Gateway?
&lt;/h3&gt;

&lt;p&gt;As more companies move from monolithic systems to microservices, a decoupled front-line API gateway to those services — providing authentication, traffic control, request and response transformation — becomes increasingly crucial. Kong Gateway, which is open source, serves as that thin layer between your users and your upstream microservices.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Is JWT?
&lt;/h3&gt;

&lt;p&gt;The JSON Web Token (JWT) format lets two parties exchange secure claims. It’s a way of saying, “I am so-and-so, which should give me access to that resource. Here is my access token to prove it.” &lt;/p&gt;

&lt;p&gt;A JWT has a data payload signed by a trusted party to prevent spoofing. An authorizer verifies that the JWT token is authentic, allowing (or forbidding) access to that resource. Typically, a JWT payload is not encrypted; it’s open for the whole world to read. However, what’s critical is the authenticity of a token, which depends on a trusted party signing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Does Kong's JWT API Gateway Plugin Do?
&lt;/h3&gt;

&lt;p&gt;In this approach, the plugin serves as the JWT authorizer. It authenticates the JWT in the HTTP request by verifying that token’s claims and ensuring a trusted party signed it. Then, depending on whether these steps were successful, Kong Gateway routes the upstream service request.&lt;/p&gt;

&lt;p&gt;Keep in mind that authentication in this context means validating the user’s credentials. That’s the job of the JWT plugin. There’s no way to know how a user got a valid JWT. The system just knows that the user has one and is presenting it for authentication. If the JWT is authentic, you can be confident that the user is who they say.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Basic Use Case
&lt;/h2&gt;

&lt;p&gt;In this basic use case, I have a login server that accepts login attempts with a user’s email and password. If the email/password checks out, the server generates and signs a JWT and hands it back to the user.&lt;/p&gt;

&lt;p&gt;With JWT in hand, the user tries to access our microservice: a simple API server with a single endpoint. Kong Gateway sits in front of your API server, using the JWT plugin for authentication. The user presents his JWT with his request.&lt;/p&gt;

&lt;p&gt;First, the plugin verifies the token’s authenticity. Next, it confirms the installation steps of the claims inside the payload. A common claim used is an expiration timestamp for the access token. It’s essentially saying, “This token is valid until this date and time.” So, the plugin will check the token’s expiration date.&lt;/p&gt;

&lt;p&gt;If the JWT passes all the necessary checks, Kong Gateway grants access to the requested server endpoint. Otherwise, it responds with &lt;code&gt;401 Unauthorized&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The approach is quite simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up a basic Node.js Express server with a single endpoint.&lt;/li&gt;
&lt;li&gt;Set up Kong Gateway as an API gateway to your server.&lt;/li&gt;
&lt;li&gt;Enable the JWT plugin to protect your server endpoint with JWT authentication.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Set Up a Node.js Express Server and Endpoint
&lt;/h3&gt;

&lt;p&gt;On your local machine, create a folder for your project. Then, initialize a new Node.js project. In the following examples, I’ll use yarn, but you could use npm too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;mkdir&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="nx"&gt;cd&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ yarn init  # Use all of the yarn defaults here&lt;/span&gt;&lt;span class="err"&gt;.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add Express to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ yarn add expres&lt;/span&gt;&lt;span class="err"&gt;s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your project folder, create the entry point file, &lt;code&gt;index.js&lt;/code&gt;, which will spin up an Express server with a single endpoint. Allow a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/&lt;/code&gt;, which will respond with the string, “Hello world!”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* PATH: ~/project/index.js
*/&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is listening on http://localhost:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was simple enough! Your single endpoint should log the request headers and then send “Hello world!” back to the client with a &lt;code&gt;200&lt;/code&gt; status.&lt;/p&gt;

&lt;p&gt;Start your server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ node index.j&lt;/span&gt;&lt;span class="err"&gt;s
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use your browser to test this new endpoint by visiting &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your API server endpoint should be working now!&lt;/p&gt;

&lt;p&gt;Next, use &lt;a href="https://insomnia.rest/" rel="noopener noreferrer"&gt;Insomnia&lt;/a&gt; to send the request and inspect the response. Because of its usability, you’re going to want to use Insomnia exclusively once you start sending requests with a JWT.&lt;/p&gt;

&lt;p&gt;In Insomnia, create a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Insomnia, you should get a &lt;code&gt;200 OK&lt;/code&gt; with “Hello world!” in the response body.&lt;/p&gt;

&lt;p&gt;It looks like the API server is up and running. Now, it’s time to put Kong Gateway in front of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Set Up Kong Gateway
&lt;/h3&gt;

&lt;p&gt;I won’t cover the details here, but the &lt;a href="https://konghq.com/blog/set-up-kong-gateway/?utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community" rel="noopener noreferrer"&gt;Kong Gateway installation steps&lt;/a&gt; may look different depending on your system. &lt;/p&gt;

&lt;p&gt;Once you’ve installed Kong, you’ll need to take a few additional steps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DB-Less Declarative Configuration&lt;/strong&gt;&lt;br&gt;
There are &lt;a href="https://docs.konghq.com/gateway-oss/2.3.x/db-less-and-declarative-config/?_ga=2.246640775.229196122.1615774344-852472749.1605808164&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community" rel="noopener noreferrer"&gt;two primary ways to configure Kong&lt;/a&gt;. Imperative configuration issues step-by-step configuration commands to Kong through its admin API. Meanwhile, declarative configuration stores the entire configuration in a single &lt;code&gt;.yml&lt;/code&gt; file then loads it into Kong upon startup. Additionally, you can configure Kong to hook into your database, providing more control over the different nodes it manages.&lt;/p&gt;

&lt;p&gt;For the simple setup example, I’ll use database-less declarative configuration. When you start up Kong, you’ll tell it where to find a &lt;code&gt;.yml&lt;/code&gt; file with all of the configuration declared within.&lt;/p&gt;

&lt;p&gt;In your project folder, run the following command, which generates an initial &lt;code&gt;kong.yml&lt;/code&gt; declarative configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ kong config ini&lt;/span&gt;&lt;span class="err"&gt;t
&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ tree -L &lt;/span&gt;&lt;span class="err"&gt;1
&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yml&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;node_modules&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="kr"&gt;package&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;yarn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lock&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, you’ll need to configure the system’s &lt;code&gt;kong.conf&lt;/code&gt; file before starting up Kong. If you’re working on Ubuntu, you’ll be working in &lt;code&gt;/etc/kong&lt;/code&gt;. Here is a template to copy over and then edit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ cd /&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong&lt;/span&gt;
&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong$&lt;/span&gt; &lt;span class="nx"&gt;sudo&lt;/span&gt; &lt;span class="nx"&gt;su&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong$&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;logrotate&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;directories&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nx"&gt;files&lt;/span&gt;

&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong$&lt;/span&gt; &lt;span class="nx"&gt;cp&lt;/span&gt; &lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are only two edits you need to make in your &lt;code&gt;kong.conf&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;PATH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;conf&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Around&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;839&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uncomment&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;off&lt;/span&gt;
&lt;span class="nx"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;off&lt;/span&gt;

&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Around&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;1023&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uncomment&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;absolute&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yml&lt;/span&gt;
&lt;span class="nx"&gt;declarative_config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/PATH/&lt;/span&gt;&lt;span class="nx"&gt;TO&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;YOUR&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;kong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Kong starts up, it will be in DB-less mode, meaning it will look to your project’s kong.yml file for a configuration.&lt;/p&gt;

&lt;p&gt;Finally, you’ll need to edit your &lt;code&gt;kong.yml&lt;/code&gt; file to set up a gateway in front of your API server “hello world” endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="cm"&gt;/* PATH: ~/project/kong.yml
*/&lt;/span&gt;

&lt;span class="nx"&gt;_format_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:3000/&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sr"&gt;/ap&lt;/span&gt;&lt;span class="err"&gt;i
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s go over this.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;_format_version&lt;/code&gt; metadata specifies the version number of your declarative configuration format.&lt;/p&gt;

&lt;p&gt;Next, you define your service, which Kong describes as “an entity representing an external upstream API or microservice.” You can name your service &lt;code&gt;my-api-service&lt;/code&gt; and specify its URL — you’ll recall that the Express server listens for requests at &lt;code&gt;http://localhost:3000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, define routes, which “determine how (and if) requests are sent to their Services after they reach Kong Gateway.” The (local) URL for Kong is &lt;code&gt;http://localhost:8000&lt;/code&gt;. You should declare your route so that Kong listens for requests at &lt;code&gt;http://localhost:8000/api&lt;/code&gt;, then routes to your service.&lt;/p&gt;

&lt;p&gt;Let’s see this in action. Make sure your Express server is running in a separate terminal. Then, start Kong.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ sudo kong star&lt;/span&gt;&lt;span class="err"&gt;t
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your browser, go to &lt;code&gt;http://localhost:8000/api&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Kong Gateway is up. Finally, add authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Attach JWT Plugin to Kong Gateway
&lt;/h3&gt;

&lt;p&gt;To add the JWT plugin, add a “plugins” definition to your &lt;code&gt;kong.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* PATH: ~/project/kong.yml
*/&lt;/span&gt;

&lt;span class="nx"&gt;_format_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:3000/&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sr"&gt;/ap&lt;/span&gt;&lt;span class="err"&gt;i
&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;key_claim_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;kid&lt;/span&gt;
    &lt;span class="nx"&gt;claims_to_verify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;exp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you can add the plugin named &lt;code&gt;jwt&lt;/code&gt; and attach it to your service called &lt;code&gt;my-api-server&lt;/code&gt;. For its &lt;a href="https://docs.konghq.com/hub/kong-inc/jwt/?_ga=2.207847185.229196122.1615774344-852472749.1605808164&amp;amp;utm_source=devto&amp;amp;utm_medium=syndication&amp;amp;utm_campaign=community#parameters" rel="noopener noreferrer"&gt;configuration options&lt;/a&gt;, tell the plugin to check the &lt;code&gt;exp&lt;/code&gt; value to verify that the access token has not expired.&lt;/p&gt;

&lt;p&gt;At this point, restart Kong and see what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ sudo kong restar&lt;/span&gt;&lt;span class="err"&gt;t
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response is &lt;code&gt;401 Unauthorized&lt;/code&gt;. Excellent! Kong now requires a valid JWT for any requests to your API server. Next, you need to tell Kong what constitutes a valid JWT.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;kong.yml&lt;/code&gt;, you need to add a consumer and a credential. Kong describes consumers as being “associated with individuals using your Service, and can be used for tracking, access management, and more.” In a more elaborate setting, every one of your API users could be a consumer. That’s a use case you can read more about towards the end of this article. In this situation, your login server is your consumer. Your login server will be the entity generating JWTs and handing them out. Users who make a request to Kong will be holding a “login server” JWT.&lt;/p&gt;

&lt;p&gt;Edit your &lt;code&gt;kong.yml&lt;/code&gt; file by adding the “consumers” and “jwt_secrets” definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="mi"&gt;9&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="mi"&gt;12&lt;/span&gt;
&lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="mi"&gt;14&lt;/span&gt;
&lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="mi"&gt;16&lt;/span&gt;
&lt;span class="mi"&gt;17&lt;/span&gt;
&lt;span class="mi"&gt;18&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;
&lt;span class="mi"&gt;20&lt;/span&gt;
&lt;span class="mi"&gt;21&lt;/span&gt;
&lt;span class="mi"&gt;22&lt;/span&gt;
&lt;span class="mi"&gt;23&lt;/span&gt;
&lt;span class="mi"&gt;24&lt;/span&gt;
&lt;span class="mi"&gt;25&lt;/span&gt;
&lt;span class="mi"&gt;26&lt;/span&gt;
&lt;span class="cm"&gt;/* PATH: ~/project/kong.yml
*/&lt;/span&gt;

&lt;span class="nx"&gt;_format_version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//localhost:3000/&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sr"&gt;/ap&lt;/span&gt;&lt;span class="err"&gt;i
&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;my&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;
  &lt;span class="nx"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;key_claim_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;kid&lt;/span&gt;
    &lt;span class="nx"&gt;claims_to_verify&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;exp&lt;/span&gt;
&lt;span class="nx"&gt;consumers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login_server_issuer&lt;/span&gt;
&lt;span class="nx"&gt;jwt_secrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;consumer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;login_server_issuer&lt;/span&gt;
    &lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secret-hash-brown-bear-market-rate-limit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ve added a new consumer, named &lt;code&gt;login_server_issuer&lt;/code&gt;. Then, you added a JWT API gateway credential for that consumer, which contains the secret used to sign JWTs for this consumer. Authentication requires two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;kid&lt;/code&gt; (key identifier) value in the JWT header, which is a unique identifier that lets the plugin determine which consumer allegedly issued this JWT&lt;/li&gt;
&lt;li&gt;Verification of the consumer’s secret – Was this the secret used to sign this JWT API gateway? If so, then this JWT is authentic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Before continuing, remember to restart Kong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;/project$ sudo kong restar&lt;/span&gt;&lt;span class="err"&gt;t
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to generate a JWT for testing, you need the secret (which you have) and the key to use for the kid value. Kong gives us access to that value through its admin API at &lt;code&gt;http://localhost:8001&lt;/code&gt;. You send a &lt;code&gt;GET&lt;/code&gt; request to the admin API’s endpoint &lt;code&gt;/consumers/CONSUMER-USERNAME/jwt&lt;/code&gt;. This gives us information about this consumer’s JWT credential.&lt;/p&gt;

&lt;p&gt;As you inspect this credential’s information, you should see the JWT &lt;code&gt;secret&lt;/code&gt; and signing &lt;code&gt;algorithm&lt;/code&gt;. What you’re looking for, though, is the key. In the above example, that’s &lt;code&gt;1nzcMG9Xg7n1lLgmltHnkAmkt7yp4fjZ&lt;/code&gt;. This is what you use as the &lt;code&gt;kid&lt;/code&gt; value in the JWT header. The Kong plugin will see this &lt;code&gt;kid&lt;/code&gt; value, track down the associated consumer and secret, then make sure the JWT was signed with that secret.&lt;/p&gt;

&lt;p&gt;To test this, let’s start with the happy path. You need a JWT with a header that includes the correct kid value, signed with the right secret. For simplicity, let’s do this at jwt.io. Here, you can craft your payload, set the signing secret and then copy/paste the resulting JWT.&lt;/p&gt;

&lt;p&gt;In the payload, the &lt;code&gt;kid&lt;/code&gt; must match the &lt;code&gt;key&lt;/code&gt; value from above. Also, because you configured the plugin to check JWT token expiration, you should set the &lt;code&gt;exp&lt;/code&gt; (Unix timestamp) far into the future. The &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt; are inconsequential; they just demonstrate that you can put other helpful data in the JWT payload.&lt;/p&gt;

&lt;p&gt;Lastly, include your JWT secret at the bottom for proper signing. The result is an encoded JWT.&lt;/p&gt;

&lt;p&gt;Back in Insomnia, you have your original request that resulted in &lt;code&gt;401&lt;/code&gt;. You need to add “Authorization” to that request. Choose “Auth – Bearer Token,” then paste in your encoded JWT from above.&lt;/p&gt;

&lt;p&gt;Now, with a valid JWT attached, resend the request.&lt;/p&gt;

&lt;p&gt;Your JWT should have been validated, and Kong routed us to the API server!&lt;/p&gt;

&lt;p&gt;If you look back at the terminal running the Express server, you’ll recall that you’re logging the request headers to the console. When the JWT plugin authenticates an access token, it writes some additional values to the upstream headers, namely the consumer id, username and credential identifier (the &lt;code&gt;key&lt;/code&gt; value).&lt;/p&gt;

&lt;p&gt;But what happens if your JWT is not valid? Let’s test and see.&lt;/p&gt;

&lt;p&gt;First, sign the JWT with a different secret. Back at jwt.io, keep the payload, but change the signing secret. Copy the resulting JWT to Insomnia, and send your request again. You’ll get a &lt;code&gt;401&lt;/code&gt; with “Invalid Signature.”&lt;/p&gt;

&lt;p&gt;If your secret is correct, but the &lt;code&gt;kid&lt;/code&gt; is incorrect, Kong won’t find an associated credential. Without that credential, there’s no way to find the secret for authenticating the JWT.&lt;/p&gt;

&lt;p&gt;Lastly, if the &lt;code&gt;exp&lt;/code&gt; value is in the past, then your JWT has expired. Just as you expected, you get the following response.&lt;/p&gt;

&lt;p&gt;And that’s it! You should be up and running with Kong Gateway and the JWT Plugin acting as an authentication layer in front of an API server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set It and Forget It
&lt;/h2&gt;

&lt;p&gt;Implementing authentication —and getting it right —is hard work. As microservices become the norm, delegating authentication makes more and more sense. “Rolling your own” implementation for JWT authentication can muddy a code base and still leave you wondering if you got it right. By using well-tested and community-adopted services that handle concerns like routing, logging or authentication, developers can shift their focus back to their projects’ unique needs.&lt;/p&gt;

&lt;p&gt;With that, you now have a solid foundation for getting started with Kong Gateway and the JWT plugin. It’s time to get to work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for walking through this tutorial with us. It was originally published on our blog: &lt;a href="https://bit.ly/3eJGhCS" rel="noopener noreferrer"&gt;https://bit.ly/3eJGhCS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>apigateway</category>
      <category>authorization</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
