<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Kms on Geoff Baskwill</title>
    <link>https://geoffbaskwill.ca/tags/kms/</link>
    <description>Geoff Baskwill (Kms)</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-ca</language>
    <lastBuildDate>Mon, 06 Mar 2023 00:00:00 +0000</lastBuildDate>
    
    <atom:link href="https://geoffbaskwill.ca/tags/kms/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>SQS, DLQs, and KMS, oh my</title>
      <link>https://geoffbaskwill.ca/posts/sqs-dlqs-and-kms-keys-oh-my/</link>
      <pubDate>Mon, 06 Mar 2023 00:00:00 +0000</pubDate>
      
      <guid>https://geoffbaskwill.ca/posts/sqs-dlqs-and-kms-keys-oh-my/</guid>
      <description>&lt;p&gt;Recently one of the teams I work with had a &lt;em&gt;fun time&lt;/em&gt; (note: it was not fun in the moment) with Amazon Simple Queue
Service (SQS), dead-letter queues (DLQs), and AWS Key Management Service (KMS).&lt;/p&gt;
&lt;p&gt;I thought I&amp;rsquo;d share because we learned something pretty important.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re new to SQS, you may be kind of aware that it&amp;rsquo;s a generalized queue
service that lets you post messages onto the queue and then process queued
messages in a handler, kind of like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/sqs-simplified.png&#34; alt=&#34;Source posting messages onto a queue that are then sent to a handler&#34; title=&#34;Source posting messages onto a queue that are then sent to a handler&#34;&gt;&lt;/p&gt;
&lt;p&gt;There is an
&lt;a href=&#34;https://docs.aws.amazon.com/lambda/latest/dg/invocation-eventsourcemapping.html&#34;&gt;event source mapping&lt;/a&gt;
in between the queue and the handler; we&amp;rsquo;re going to gloss over that detail for
a minute.&lt;/p&gt;
&lt;p&gt;SQS is super-handy, reliable, and amazingly scalable; AWS guides us to use it
anywhere that we need to buffer communications between components of a solution.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s even a mechanism to deal with failures. You can configure a policy on
your queue so that if SQS fails to deliver&lt;sup&gt;1&lt;/sup&gt; a message to the handler
enough times, the message gets moved to another queue for special handling. This
second queue is called a &amp;ldquo;dead-letter&amp;rdquo; queue, named after the
&lt;a href=&#34;https://en.wikipedia.org/wiki/Dead_letter_mail&#34;&gt;post office concept&lt;/a&gt; where
messages that were undeliverable got sent to a special place for handling.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;sup&gt;1&lt;/sup&gt; &lt;em&gt;&amp;ldquo;SQS fails to deliver&amp;rdquo; is not technically accurate, as SQS does
not deliver messages, rather the event source mapping retrieves the message,
attempts to deliver it to the Lambda function, and deletes the message if the
message was handled successfully. If there was an error retrieving or
processing the message, SQS will make the message available again after the
visibility period expires. Eventually the message will expire, either after a
specified time period or after a specified number of retrievals from the
queue.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You may have a mental model of this setup that looks a bit like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/dlqs-simplified.png&#34; alt=&#34;Source posting messages onto a queue that go to a handler and a DLQ for handling&#34; title=&#34;Source posting messages onto a queue that go to a handler and a DLQ for handling&#34;&gt;&lt;/p&gt;
&lt;p&gt;SQS also lets you encrypt messages in the queue; you can use an AWS-managed key
or a customer-managed key. Our teams always use customer-managed keys, so we
have an SQS queue with a KMS CMK and DLQs (say that three times fast!). The
updated model looks a bit like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/dlqs-more-complete.png&#34; alt=&#34;Source posting messages onto a queue with a KMS customer-managed key for encryption and a DLQ with a separate KMS key&#34; title=&#34;Source posting messages onto a queue with a KMS customer-managed key for encryption and a DLQ with a separate KMS key&#34;&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re an SQS expert, someone who has read the documentation carefully, or
someone who read &lt;em&gt;very carefully&lt;/em&gt; through the paragraphs above, you will
&lt;em&gt;instantly&lt;/em&gt; see the problem with this picture. If you didn&amp;rsquo;t catch it, you are
not alone.&lt;/p&gt;
&lt;p&gt;The key phrasing above is that undeliverable messages are &lt;em&gt;moved&lt;/em&gt; from the
primary queue to the dead-letter queue. They&amp;rsquo;re not retrieved from the primary
queue and then sent onto the dead-letter queue, they&amp;rsquo;re &lt;em&gt;moved&lt;/em&gt;. This isn&amp;rsquo;t
explicitly stated in the documentation we have been able to find, but it&amp;rsquo;s clear
from the behaviour we experienced and a couple of other hints in the
documentation.&lt;/p&gt;
&lt;p&gt;Our team discovered that the DLQ handler wasn&amp;rsquo;t processing messages, and in fact
its event source mapping (the invisible thing we agreed to gloss over) was
entirely disabled. An extremely-helpful AWS support person was able to tell us
why.&lt;/p&gt;
&lt;p&gt;When messages are put onto a primary queue that has KMS encryption enabled, they
are encrypted using that queue&amp;rsquo;s KMS key. The handler must also have permissions
to decrypt messages using the key, and we had that all working perfectly.&lt;/p&gt;
&lt;p&gt;The Lambda function does not use the decryption permission itself, but rather
&amp;ldquo;loans&amp;rdquo; that permission to the event source mapping. In our case, the Lambda
function&amp;rsquo;s execution role and therefore event source mapping did not have
sufficient permissions to decrypt the message, so the mapping failed to decrypt
the message after retrieving it from the DLQ and did not invoke the Lambda
function.&lt;/p&gt;
&lt;p&gt;If you have a flawed understanding of the DLQ like we did, you might expect that
messages would be decrypted and then re-encrypted when they are sent on the DLQ.
However, they&amp;rsquo;re not decrypted and re-encrypted. They&amp;rsquo;re not even &lt;em&gt;sent&lt;/em&gt; on the
DLQ. They&amp;rsquo;re &lt;em&gt;moved&lt;/em&gt; from the primary queue to the DLQ.&lt;/p&gt;
&lt;p&gt;We had our DLQ set up with a separate key, and the DLQ handler had permissions
to use the key, and in tests where we sent messages to the DLQ the handler
worked perfectly. However, in a full end-to-end scenario where messages fell off
the primary queue and were moved to the DLQ, the event source mapping failed to
decrypt the message (it was set up with permissions on the DLQ key, not the
primary queue key) and did not invoke the handler.&lt;/p&gt;
&lt;p&gt;AWS is super-smart, and they know not to try things forever when there&amp;rsquo;s
persistent failure. The event source mapping eventually disables itself and
emits a CloudTrail event when it does so. If you&amp;rsquo;re vigilant or, preferably, if
you set up a CloudWatch alarm based on the &lt;code&gt;ApproximateAgeOfOldestMessage&lt;/code&gt;
metric on the DLQ, you&amp;rsquo;ll be able to detect that messages are queuing up in the
DLQ and not getting handled.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;images/dlqs-fixed.png&#34; alt=&#34;Source posting messages onto a queue with a KMS customer-managed key for encryption and a DLQ that uses the same KMS key&#34; title=&#34;Source posting messages onto a queue with a KMS customer-managed key for encryption and a DLQ that uses the same KMS key&#34;&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s important to remember that because messages are &lt;em&gt;moved&lt;/em&gt; and not re-sent on
the DLQ, your &lt;code&gt;ApproximateAgeOfOldestMessage&lt;/code&gt; does not reflect the time that the
message is in the DLQ but rather the time since the message was originally sent
on the primary queue. This is also an important thing to remember when you are
configuring your DLQ: if you use the same policy to age messages out of your DLQ
as you have in your primary queue, you may find that messages disappear without
getting handled the way you expected.&lt;/p&gt;
&lt;p&gt;I did mention that AWS
&lt;a href=&#34;https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-configure-dead-letter-queue-redrive.html&#34;&gt;documents&lt;/a&gt;
the requirement to use the same key:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Your source queue and its corresponding dead-letter queue need to share the
same KMS key.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oops. 😳&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re looking into creating a
&lt;a href=&#34;https://github.com/aws-cloudformation/cfn-lint&#34;&gt;cfn-lint&lt;/a&gt; custom rule to
prevent us from tripping over this issue again.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
