11 KiB
Task 41
The Nautilus DevOps team is focusing on improving their data security by using AWS KMS. Your task is to create a KMS key and manage the encryption and decryption of a pre-existing sensitive file using the KMS key.
Specific Requirements:
Create a symmetric KMS key named nautilus-KMS-Key to manage encryption and decryption. Encrypt the provided SensitiveData.txt file (located in /root/), base64 decode the ciphertext, and save the encrypted version as EncryptedData.bin in the /root/ directory. Try to decrypt the same and verify that the decrypted data matches the original file. Make sure that the KMS key is correctly configured. The validation script will test your configuration by decrypting the EncryptedData.bin file using the KMS key you created.
Solution
AWS KMS Encryption/Decryption Task (nautilus-KMS-Key)
The concept that matters: KMS naming works differently. A KMS key has no "name" attribute at all — what you set is an alias (alias/nautilus-KMS-Key), a friendly pointer to the key's real ID. And there's a hard 4KB size limit on direct kms encrypt, which is why this task is fine for a small file but you'd need envelope encryption for anything bigger. The base64-decode step exists because the CLI returns ciphertext base64-encoded in JSON — you decode it back to raw bytes for the .bin.
Run on aws-client:
REGION=us-east-1
# 1. Create the symmetric KMS key (symmetric + ENCRYPT_DECRYPT are the defaults)
KEY_ID=$(aws kms create-key \
--description "nautilus KMS key for sensitive data" \
--key-usage ENCRYPT_DECRYPT \
--key-spec SYMMETRIC_DEFAULT \
--region $REGION \
--query 'KeyMetadata.KeyId' --output text)
# Give it the "name" — which in KMS means an alias
aws kms create-alias \
--alias-name alias/nautilus-KMS-Key \
--target-key-id $KEY_ID \
--region $REGION
# 2. Encrypt SensitiveData.txt -> base64-decode -> EncryptedData.bin
aws kms encrypt \
--key-id alias/nautilus-KMS-Key \
--plaintext fileb:///root/SensitiveData.txt \
--output text \
--query CiphertextBlob \
--region $REGION \
| base64 --decode > /root/EncryptedData.bin
# 3. Decrypt EncryptedData.bin and compare to the original
aws kms decrypt \
--ciphertext-blob fileb:///root/EncryptedData.bin \
--output text \
--query Plaintext \
--region $REGION \
| base64 --decode > /root/DecryptedData.txt
# Verify round-trip integrity
diff /root/SensitiveData.txt /root/DecryptedData.txt && echo "MATCH ✔" || echo "MISMATCH <20>’"
Every non-obvious piece explained
- Alias, not name.
create-keyreturns only aKeyId(a UUID) and ARN — no name field exists.create-aliaswith thealias/prefix (mandatory prefix) is how you give it the human labelnautilus-KMS-Key. Everywhere else you can then referencealias/nautilus-KMS-Keyinstead of the UUID. The validation script decrypting "using the KMS key you created" works because the ciphertext blob has the key ID embedded in it — decrypt doesn't even need--key-id. SYMMETRIC_DEFAULT+ENCRYPT_DECRYPTare both the defaults, so you could omit those flags — set explicitly since the task says "symmetric." Symmetric = same key encrypts and decrypts, AES-256-GCM under the hood, key material never leaves KMS.fileb://notfile://for--plaintext— theb= binary. This reads the file's raw bytes.file://would try to interpret it as text/UTF-8 and can mangle binary or trailing content. Critical for a clean round-trip.- The base64 dance.
kms encryptreturnsCiphertextBlobas base64 text inside JSON.--query CiphertextBlob --output textextracts just that base64 string;base64 --decodeconverts it to the raw binary ciphertext that becomesEncryptedData.bin. This is exactly the task's "base64 decode the ciphertext" step — the.binmust be raw bytes, not base64 text, or the validation script's decrypt fails. - Decrypt needs no
--key-id. This surprises people:kms decryptfor a symmetric key figures out which key to use because the key ID is baked into the ciphertext blob itself. You just hand it the blob. (Asymmetric keys would require--key-id.) Same base64-decode on the way out to recover the original plaintext bytes. fileb://on decrypt's--ciphertext-blobtoo — reading the raw.binbytes.
Verify the key + alias config (what the validation script cares about)
# Alias points at an enabled key
aws kms list-aliases --region $REGION \
--query "Aliases[?AliasName=='alias/nautilus-KMS-Key']"
aws kms describe-key --key-id alias/nautilus-KMS-Key --region $REGION \
--query 'KeyMetadata.{Id:KeyId,State:KeyState,Usage:KeyUsage,Spec:KeySpec,Enabled:Enabled}'
# The round-trip already proved it, but confirm the .bin exists and is binary
file /root/EncryptedData.bin
ls -l /root/EncryptedData.bin
Want: the alias resolving to your key; describe-key showing State: Enabled, Usage: ENCRYPT_DECRYPT, Spec: SYMMETRIC_DEFAULT; the diff printing MATCH; and EncryptedData.bin existing as binary data. The successful decrypt + matching diff is the task's proof — it means the key encrypts and decrypts correctly and the .bin is in the raw format the validator expects.
Debug order if something's off
diffshows MISMATCH → almost always afile://vsfileb://mix-up mangling bytes, or you skipped abase64 --decode. Both the write and read paths need the binary treatment.- Validation script can't decrypt the
.bin→ the.binis probably still base64-encoded text instead of raw bytes (missing thebase64 --decodeon encrypt).file /root/EncryptedData.binshould say "data," not "ASCII text." AccessDeniedon encrypt/decrypt → your CLI identity lacks kms permissions, or (rare) the key policy is restrictive — a freshly created key grants the creating account root full access by default, so this is unusual in a lab.
Task 42
The Nautilus DevOps team is developing a simple 'To-Do' application using DynamoDB to store and manage tasks efficiently. The team needs to create a DynamoDB table to hold tasks, each identified by a unique task ID. Each task will have a description and a status, which indicates the progress of the task (e.g., 'completed' or 'in-progress').
Your task is to:
Create a DynamoDB table named nautilus-tasks with a primary key called taskId (string). Insert the following tasks into the table: Task 1: taskId: '1', description: 'Learn DynamoDB', status: 'completed' Task 2: taskId: '2', description: 'Build To-Do App', status: 'in-progress' Verify that Task 1 has a status of 'completed' and Task 2 has a status of 'in-progress'. Ensure the DynamoDB table is created successfully and that both tasks are inserted correctly with the appropriate statuses.
Solution
DynamoDB To-Do Table Task (nautilus-tasks)
DynamoDB — the concepts that matter here: DynamoDB is schemaless except for the key. You only declare taskId at table creation; description and status are not defined up front — they're just attributes you attach per-item at insert time. And every value carries a type descriptor (S for string, N for number, etc.) in the API, which is why the item JSON looks verbose. Also on-demand billing (PAY_PER_REQUEST) is the right pick for a lab — no capacity planning, no idle cost.
Run on aws-client:
1. Create the table
REGION=us-east-1
aws dynamodb create-table \
--table-name nautilus-tasks \
--attribute-definitions AttributeName=taskId,AttributeType=S \
--key-schema AttributeName=taskId,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region $REGION
# Table creation is async — wait until ACTIVE
aws dynamodb wait table-exists --table-name nautilus-tasks --region $REGION
2. Insert the two tasks
aws dynamodb put-item \
--table-name nautilus-tasks \
--item '{
"taskId": {"S": "1"},
"description": {"S": "Learn DynamoDB"},
"status": {"S": "completed"}
}' \
--region $REGION
aws dynamodb put-item \
--table-name nautilus-tasks \
--item '{
"taskId": {"S": "2"},
"description": {"S": "Build To-Do App"},
"status": {"S": "in-progress"}
}' \
--region $REGION
Every non-obvious piece explained
- Only the key attribute is declared at creation.
--attribute-definitionslists onlytaskId— you do NOT declaredescriptionorstatus. DynamoDB is schemaless beyond the primary key; non-key attributes materialize when you write items. Declaring extra attributes here would actually error unless they're part of a key or index. This trips people coming from relational DBs. KeyType=HASH= partition key.taskIdasHASHmakes it the partition (primary) key — the single-attribute simple primary key the task wants. (ARANGEkey would add a sort key for a composite key; not needed here.)- The
{"S": "..."}type descriptors are mandatory in the low-level API. Every attribute value is wrapped with its type:Sstring,Nnumber,BOOL,Mmap,Llist, etc. Even thoughtaskId"looks" numeric ("1"), the task says it's a string, soS— and the values are quoted strings. Get this wrong (e.g.Nfor taskId) and it mismatches the key schema. PAY_PER_REQUEST= on-demand mode: no read/write capacity units to provision, you pay per request. For a lab with 2 items this is free-tier-friendly and zero-config. The alternativePROVISIONEDmode needs--provisioned-throughputnumbers.wait table-exists— table creation goesCREATING → ACTIVEasynchronously. You can'tput-itemuntil it'sACTIVE, so the waiter prevents a race where inserts fire against a still-creating table.
3. Verify both items and their statuses
# Fetch each item by key, checking the status
aws dynamodb get-item \
--table-name nautilus-tasks \
--key '{"taskId": {"S": "1"}}' \
--region $REGION \
--query 'Item.status.S' # expect: "completed"
aws dynamodb get-item \
--table-name nautilus-tasks \
--key '{"taskId": {"S": "2"}}' \
--region $REGION \
--query 'Item.status.S' # expect: "in-progress"
# Or dump both items at once to eyeball everything
aws dynamodb scan --table-name nautilus-tasks --region $REGION \
--query 'Items[].{ID:taskId.S, Desc:description.S, Status:status.S}' \
--output table
Want the two get-item calls returning "completed" and "in-progress" respectively, and the scan table showing both rows with correct descriptions and statuses. That confirms the task: table ACTIVE, both items inserted, statuses correct.
get-item vs scan: get-item is a direct key lookup (fast, cheap, single item) — the right way to verify a specific task by its taskId. scan reads the whole table (fine for 2 items, but avoid on large tables — it's a full sweep). Used both here: get-item for the targeted status checks, scan for the at-a-glance dump.
Debug order if verification fails
get-itemreturns nothing / null → wrong key type or value. The key inget-itemmust match exactly:{"taskId": {"S": "1"}}—Stype, string"1". AnNtype or unquoted value won't match.put-itemerrored on insert → usually a JSON quoting issue in the--itemblob, or a type mismatch against the key schema (taskId declaredSbut sent asN).create-tableerrored → likely declared a non-key attribute in--attribute-definitions. OnlytaskIdbelongs there.