It’s 3am. The pager fires: the CI build box can’t reach the artifact cache, and every pipeline is failing at the “restore dependencies” step. You SSH in and find a line in /etc/fstab like //nas01/builds /mnt/cache cifs ... — and the mount is dead. To fix it you need to know what SMB actually does on the wire: how it negotiates, how it authenticates, and how it fails. This post builds that knowledge by making you run it, break it, and watch the packets.

This is a lab, not a reference. Every step runs in Docker containers on your laptop. By the end you’ll have stood up a file server, mounted it from a Linux client, dissected the protocol in Wireshark, broken authentication on purpose, killed off the dangerous old dialect, and rehearsed the 3am debug. Budget about 60–90 minutes.

Why SMB exists (the problem)

Before network file sharing, sharing a file meant copying it — FTP it over, email it, put it on a USB stick. The moment two people needed to edit the same file, or a program needed to read a file that lived on another machine, copying fell apart: whose copy is authoritative? What if it changes while you’re reading it?

SMB (Server Message Block) solves this: it lets a client open, read, write, and lock files that physically live on a remote host, as if they were on a local disk. One authoritative copy on a server; many clients reading and writing it concurrently, with permissions and locking enforced centrally.

Where you’ll actually meet SMB as a platform engineer:

  • A NAS or Windows file server holding shared assets, and a fleet of Linux boxes mounting it.
  • CI/CD: a shared artifact or dependency cache mounted on build runners (exactly the 3am scenario).
  • Kubernetes: the SMB CSI driver backing PersistentVolumes for pods that need shared ReadWriteMany storage.
  • Backups: a backup agent writing to an SMB target.

It’s the protocol behind Windows’ “map network drive” (Z:) and behind every mount -t cifs on Linux. Knowing it is table stakes for anyone running mixed Windows/Linux infrastructure.

The mental model (and where it breaks)

The model: SMB is a network hard drive. You “attach” a remote folder and then use it with normal file operations — ls, open, read, write, rename.

Where the analogy breaks — and this matters: a local disk is a block device you talk to over a fast bus. SMB is a stateful, chatty request/response protocol over TCP. Every open, every read, every stat is a separate message with a round trip to the server. On a LAN (sub-millisecond), you never notice. Over a WAN (50–100ms each way), a git status that touches thousands of files turns thousands of round trips into minutes of hanging. The “it’s just a folder” mental model is exactly what makes engineers deploy SMB across regions and then page themselves at 3am about “slow storage.” It isn’t slow storage — it’s latency multiplied by chattiness.

Hold onto that: SMB is a conversation, not a disk. The whole lab is about hearing that conversation.

How SMB actually works (the wire protocol)

Below the “network drive” abstraction, here’s the ground truth.

Ports. Modern SMB runs directly over TCP on port 445. The legacy path is 139 (SMB tunneled inside the NetBIOS session service), with UDP 137/138 for old NetBIOS name resolution. For anything built this decade, think 445.

Dialects (versions). SMB has evolved, and the version in use is negotiated per connection:

  • SMB 1.0 / CIFS (1990s) — chatty, no real integrity protection. This is the one that got the world ransomwared (EternalBlue → WannaCry/NotPetya, 2017). Disabled by default everywhere now.
  • SMB 2.0 / 2.1 (Windows Vista/7 era) — drastically fewer commands, far less chatty, the modern baseline.
  • SMB 3.0 → 3.1.1 (Windows 8/10+) — adds encryption, multichannel, and stronger integrity. 3.1.1 is the current default.

CIFS vs SMB — the confusing bit. “CIFS” (Common Internet File System) is just Microsoft’s old name for SMB 1.0. The word stubbornly survives in Linux: the kernel module and mount type are still called cifs (mount -t cifs) even though a modern mount negotiates SMB 2 or 3. So “CIFS” in mount -t cifs means “the SMB client,” not “use the ancient SMB1 dialect.” Don’t confuse the tool name with the dialect.

The message flow. Once TCP connects on 445, an SMB session is a defined sequence. This is the single most important diagram in the post — memorize it, because you’ll watch it happen in Lab 2:

  CLIENT                                                 SERVER
    │                                                       │
    │  ── TCP 3-way handshake on :445 ──────────────────►   │
    │                                                       │
    │  ── NEGOTIATE ────────────────────────────────────►  │  "I speak dialects 2.0.2 … 3.1.1"
    │  ◄──────────────────────────────────── NEGOTIATE ──   │  "Let's use 3.1.1"
    │                                                       │
    │  ── SESSION_SETUP (auth: NTLM / Kerberos) ─────────►  │  challenge / response
    │  ◄──────────────────────────────────  SESSION_SETUP   │  → returns a SessionId
    │                                                       │
    │  ── TREE_CONNECT  \\server\share ─────────────────►   │  → returns a TreeId
    │                                                       │
    │  ── CREATE (open file/dir) ───────────────────────►   │  → returns a FileId (handle)
    │  ── READ / WRITE / QUERY_INFO ────────────────────►   │  the actual data
    │  ── CLOSE (release handle) ───────────────────────►   │
    │                                                       │
    │  ── TREE_DISCONNECT ──────────────────────────────►   │
    │  ── LOGOFF ───────────────────────────────────────►   │

The shape to remember: negotiate a version → prove who you are → attach to a share → open a handle → move bytes → tidy up. Each later message carries the IDs returned earlier (SessionId, TreeId, FileId) — that’s what makes SMB stateful.

Authentication happens at SESSION_SETUP, two main ways:

  • NTLM — a challenge/response using your password hash. Works standalone (no domain). The protocol it runs inside is NTLMSSP, which is why you’ll see SESSION_SETUP happen several times in a row (negotiate → challenge → authenticate).
  • Kerberos — ticket-based, issued by a KDC (Active Directory). The default inside a domain; it avoids sending reusable secrets and gives single sign-on. (Beyond this lab, but know the word.)

Integrity vs confidentiality — two separate knobs people conflate:

  • SMB signing adds a MAC to every message so tampering (and NTLM relay attacks) is detected. The data is still readable on the wire.
  • SMB encryption (SMB 3.x) makes the payload unreadable. You’ll prove the difference in Lab 4.

Now let’s hear the conversation.

Before you start (lab prerequisites)

You need Docker (Desktop on macOS/Windows, or Engine on Linux) and, for the packet-dissection labs, Wireshark on your host. Everything else runs in containers.

The topology you’re about to build:

            docker network: smblab
   ┌───────────────────────┐        ┌───────────────────────┐
   │   smb-server          │  :445  │   smb-client          │
   │   Samba (smbd)        │◄───────│   smbclient / mount   │
   │   shares: public,     │  SMB   │   tcpdump (capture)   │
   │           secure      │        │                       │
   └───────────────────────┘        └───────────────────────┘

macOS/Docker gotcha (read this now). On Docker Desktop the containers run inside a lightweight Linux VM, so you can’t tcpdump the SMB traffic from your Mac’s host interfaces — you’ll see nothing. The fix used throughout: capture inside the client container, then docker cp the .pcap out to your Mac and open it in Wireshark. This is a real, recurring lesson about where your packets actually are.

Lab 0 — Build the Samba server

Samba is the open-source implementation of the SMB protocol for Unix; smbd is its server daemon. We’ll bake a tiny server with two shares so we can contrast guest vs authenticated access.

Create a working directory with two files.

smb.conf — the server config. Every line is commented because you should be able to explain each one:

[global]
   workgroup = WORKGROUP
   server string = SMB Lab Server
   security = user            # authenticate against real Samba users
   map to guest = Bad User    # unknown USERNAME → guest; bad PASSWORD still fails
   # Deliberately permissive so we can see SMB1 on the wire in Lab 4.
   # In production you would set: server min protocol = SMB2
   server min protocol = NT1
   server max protocol = SMB3

[public]                      # a guest-readable share
   path = /srv/public
   browseable = yes
   read only = no
   guest ok = yes
   force user = nobody

[secure]                      # requires a valid user + password
   path = /srv/secure
   browseable = yes
   read only = no
   guest ok = no
   valid users = labuser

Dockerfile — installs Samba, creates the shares, and adds one user labuser (password labpass):

FROM alpine:3.20
RUN apk add --no-cache samba samba-common-tools
COPY smb.conf /etc/samba/smb.conf
RUN mkdir -p /srv/public /srv/secure \
    && chmod 0777 /srv/public \
    && chmod 0770 /srv/secure \
    && adduser -D -H labuser \
    && printf 'labpass\nlabpass\n' | smbpasswd -a -s labuser \
    && chown labuser /srv/secure \
    && echo "hello from the secure share" > /srv/secure/welcome.txt \
    && echo "hello from the public share" > /srv/public/readme.txt
EXPOSE 445
CMD ["smbd", "--foreground", "--no-process-group", "--debuglevel=1"]

Build it, create a private network, and start the server:

docker build -t smb-lab .
docker network create smblab
docker run -d --name smb-server --network smblab smb-lab

# Confirm smbd is listening on 445 inside the container
docker exec smb-server netstat -lntp | grep 445
# Expect: tcp  0  0 0.0.0.0:445  0.0.0.0:*  LISTEN  1/smbd

If you see smbd listening on 445, your file server is up.

Check your understanding: why did we put the server on a user-defined Docker network (smblab) instead of the default bridge? (Hint: name resolution — containers on a user-defined network can reach each other by container name, so the client can say //smb-server.)

Lab 1 — Connect (the happy path)

Now a Linux client — the same way a build runner or a Kubernetes node talks to SMB. Start a client container on the same network, with cifs-utils (for mounting), samba-client (for smbclient), and tcpdump (for later):

docker run -d --name smb-client --network smblab --privileged smb-lab sleep infinity
docker exec smb-client apk add --no-cache samba-client cifs-utils tcpdump tshark

Why --privileged? Two later steps need elevated capabilities: mount -t cifs needs CAP_SYS_ADMIN, and tcpdump needs CAP_NET_RAW. --privileged grants both so the lab is frictionless. You would never run a production container privileged — it’s a near-root hole. In the real world, the host/node already has these, and in Kubernetes the CSI driver does the mounting in a controlled component, not your app pod.

First, enumerate the shares — like a client asking “what do you offer?”:

docker exec smb-client smbclient -L //smb-server -N
        Sharename       Type      Comment
        ---------       ----      -------
        public          Disk
        secure          Disk
        IPC$            IPC       IPC Service (SMB Lab Server)
SMB1 disabled -- no workgroup available

You see both shares plus IPC$ (a special hidden share for control messages — every SMB server has it). The -N means “no password.” The SMB1 disabled line at the bottom is expected and harmless: it’s the old NetBIOS workgroup browse (an SMB1-era feature) failing — the actual share listing came over SMB2.

Now read the guest share and the authenticated share. smbclient gives you an FTP-like prompt; -c runs commands non-interactively:

# Guest access to the public share (no creds)
docker exec smb-client smbclient //smb-server/public -N \
  -c 'ls; get readme.txt /tmp/r.txt; quit'

# Authenticated access to the secure share (user%password)
docker exec smb-client smbclient //smb-server/secure -U 'labuser%labpass' \
  -c 'ls; get welcome.txt /tmp/w.txt; quit'

Both should list the directory and pull the file. You just exercised the whole flow from the diagram — negotiate, authenticate, tree-connect, create, read, close — in one line.

Finally, the real-world form: mount the share into the filesystem so any program can use it, exactly like an fstab entry on a build box. Never put passwords on the command line (they land in shell history and ps); use a credentials file:

# Credentials file (mode 600 so other users can't read it)
docker exec smb-client sh -c 'printf "username=labuser\npassword=labpass\n" > /root/.smbcreds && chmod 600 /root/.smbcreds'

# Mount, pinning the dialect explicitly
docker exec smb-client sh -c 'mkdir -p /mnt/secure && \
  mount -t cifs //smb-server/secure /mnt/secure -o credentials=/root/.smbcreds,vers=3.1.1'

# It's now a normal directory
docker exec smb-client cat /mnt/secure/welcome.txt   # → hello from the secure share
docker exec smb-client mount | grep cifs             # see the mount + negotiated options

That vers=3.1.1 is the dialect knob from the theory section. Remember it — it’s the cause of half your future mount failures.

Lab 2 — Watch the protocol on the wire

This is the payoff. Capture a session and see the message flow you memorized.

# Capture port 445 INSIDE the client while we do a connect + read
docker exec smb-client sh -c '
  tcpdump -i any -w /tmp/smb.pcap port 445 >/dev/null 2>&1 & TCPID=$!
  sleep 1
  smbclient //smb-server/secure -U "labuser%labpass" -c "ls; get welcome.txt /tmp/x; quit" >/dev/null 2>&1
  sleep 1
  kill $TCPID'

You can dissect it right in the container with tshark (Wireshark’s CLI):

docker exec smb-client sh -c \
  "tshark -r /tmp/smb.pcap -Y smb2 -O smb2 2>/dev/null | grep -E 'Command:|Dialect:'"

The real output — this is your diagram, live:

Command: Negotiate Protocol (0)
Dialect count: 5
Dialect: SMB 2.0.2 (0x0202)
Dialect: SMB 2.1 (0x0210)
Dialect: SMB 3.0 (0x0300)
Dialect: SMB 3.0.2 (0x0302)
Dialect: SMB 3.1.1 (0x0311)      ← client offers five dialects
Command: Negotiate Protocol (0)
Dialect: SMB 3.1.1 (0x0311)      ← server picks the highest: 3.1.1
Command: Session Setup (1)        ┐
Command: Session Setup (1)        │ NTLMSSP challenge/response
Command: Session Setup (1)        │ (several round trips)
Command: Session Setup (1)        ┘
Command: Tree Connect (3)         ← attach to a share (IPC$ first, then the real one)
Command: Create (5)               ← open the directory
Command: Find (14)                ← the 'ls'
Command: Close (6)
Command: Create (5)               ← open welcome.txt
Command: Read (8)                 ← the 'get'  ← your file's bytes, in cleartext
Command: Close (6)

There it is: NEGOTIATE → SESSION_SETUP → TREE_CONNECT → CREATE → FIND/READ → CLOSE. Note the dialect negotiation in action (offer five, server picks 3.1.1) and that Read carries your file content in the clear — anyone on this network path can read welcome.txt. Hold that thought for Lab 4.

To explore it in the GUI, copy the capture to your host and open it in Wireshark:

docker cp smb-client:/tmp/smb.pcap ./smb.pcap
open smb.pcap            # macOS; or just open Wireshark and File→Open

In Wireshark, type smb2 in the display filter to see only SMB messages. Click a Session Setup and expand the tree to find the NTLMSSP fields; click the Read Response to see your file’s bytes in the packet detail.

Check your understanding: in the capture, which message returned the TreeId, and which returned the FileId? Why does the protocol need both instead of just one handle?

Lab 3 — Break authentication

Failure teaches the protocol better than success. Make auth fail two different ways and note that they fail at different stages.

# 1) Right user, WRONG password
docker exec smb-client smbclient //smb-server/secure -U 'labuser%wrongpass' -c 'ls; quit'
#   → session setup failed: NT_STATUS_LOGON_FAILURE

# 2) Guest (no creds) trying the SECURE share
docker exec smb-client smbclient //smb-server/secure -N -c 'ls; quit'
#   → tree connect failed: NT_STATUS_ACCESS_DENIED

Read those two errors carefully — they’re a debugging map:

  • NT_STATUS_LOGON_FAILURE at session setup → the credentials themselves are wrong. Authentication never completed. (In Lab 5’s terms, this becomes mount error(13).)
  • NT_STATUS_ACCESS_DENIED at tree connect → you authenticated fine (as guest), but you’re not allowed onto this share. Auth succeeded; authorization failed.

That distinction — who are you? (authentication, SESSION_SETUP) vs are you allowed here? (authorization, TREE_CONNECT) — is the single most useful thing to internalize about access control, and SMB makes it visible as two separate protocol steps. When a teammate says “it says access denied,” your first question is now: at which step?

Lab 4 — Dialects and security (the blast radius)

Modern clients refuse SMB1 — by default

Try to force the ancient SMB1 dialect:

docker exec smb-client smbclient //smb-server/public -N -m NT1 -c 'ls; quit'
lp_load_ex: Max protocol NT1 is less than min protocol SMB2_02.
... NT_STATUS_INVALID_PARAMETER_MIX

The client itself refuses — its own floor is SMB2. This is post-WannaCry hardening baked into the tools: you have to go out of your way to speak SMB1. To actually do it you must also lower the client’s minimum:

docker exec smb-client smbclient //smb-server/public -N -m NT1 \
  --option='client min protocol=NT1' -c 'ls; quit'
#   → now it works, because OUR server still allows NT1 (we set min protocol = NT1)

Kill SMB1 on the server (the production posture)

The lab server is deliberately permissive. Lock it down the way production should be — edit smb.conf:

   server min protocol = SMB2   # was NT1

Rebuild and restart the server, then the same forced-SMB1 client attempt fails at negotiation — the two sides share no common dialect:

Protocol negotiation failed: NT_STATUS_... (no dialect in common)

Why this matters more than almost anything else here. SMB1’s EternalBlue vulnerability (a pre-authentication remote code execution bug) is what WannaCry and NotPetya used to spread across flat networks in 2017, causing billions in damage. The lesson for you: server min protocol = SMB2 is non-negotiable, and TCP 445 must never face the public internet. A junior asks “does the share work?”; a senior asks “if this host is compromised, how far does 445 let the blast radius reach?” Put 445 behind a firewall/VPN and disable SMB1 everywhere — that’s the whole ballgame.

Signing leaves data readable; encryption hides it

Recall Lab 2: your file crossed the wire in cleartext. Now force SMB3 encryption and capture again:

docker exec smb-client sh -c '
  tcpdump -i any -w /tmp/enc.pcap port 445 >/dev/null 2>&1 & TCPID=$!
  sleep 1
  smbclient //smb-server/secure -U "labuser%labpass" \
    --option="client smb encrypt=required" -c "get welcome.txt /tmp/z; quit" >/dev/null 2>&1
  sleep 1
  kill $TCPID'

# How many SMB2 commands are still readable?
docker exec smb-client sh -c "tshark -r /tmp/enc.pcap -Y smb2.cmd 2>/dev/null | wc -l"
#   → only a handful (Negotiate + Session Setup happen BEFORE keys exist)

# How many encrypted 'transform header' frames? (protocol id 0xfd534d42 = "\xfdSMB")
docker exec smb-client sh -c "tshark -r /tmp/enc.pcap -Y 'smb2.protocol_id==0xfd534d42' 2>/dev/null | wc -l"
#   → ~18 encrypted frames

# Can we still see the filename? (we could, in Lab 2)
docker exec smb-client sh -c "tshark -r /tmp/enc.pcap -T fields -e smb2.filename 2>/dev/null | grep -i welcome || echo 'hidden (encrypted)'"
#   → hidden (encrypted)

The contrast is the lesson: only NEGOTIATE and SESSION_SETUP stay in cleartext (they have to — that’s where the session key is derived). Everything after — tree connect, the filename, the file’s bytes — becomes opaque encrypted transform frames. Compare to Lab 2 where all 38 SMB2 messages and the filename were readable. Open enc.pcap in Wireshark and you’ll see rows labelled Encrypted SMB3 instead of Read/Create.

Signing ≠ encryption. Signing would let an eavesdropper still read welcome.txt (it only proves the message wasn’t altered). Encryption is what gives you confidentiality. For sensitive data over an untrusted network, require encryption (smb encrypt = required).

Lab 5 — The 3am failure drill

Back to the pager. The mount is dead and pipelines are red. Don’t guess — walk the protocol from the bottom up. Here’s the ladder, in order, mapped to where each one sits in the flow:

# RUNG 1 — is the server even reachable on 445? (TCP layer, before any SMB)
docker exec smb-client nc -zv smb-server 445
#   open    → TCP fine, go to rung 2
#   refused/timeout → firewall, wrong host, server down, or 445 not exposed

# RUNG 2 — do my credentials work at all? (SESSION_SETUP)
docker exec smb-client smbclient -L //smb-server -U 'labuser%labpass'
#   NT_STATUS_LOGON_FAILURE → wrong creds / expired password / account locked

# RUNG 3 — does the dialect match? (NEGOTIATE)
docker exec smb-client sh -c 'mount -t cifs //smb-server/secure /mnt/secure \
  -o credentials=/root/.smbcreds,vers=3.1.1'
#   fails on an old server? try vers=2.1 — a dialect mismatch is a classic cause

# RUNG 4 — what does the kernel say? (the CIFS client logs the real reason)
docker exec smb-client dmesg | grep -i CIFS | tail

Decode the mount error(N) numbers — they’re Linux errnos and they tell you which rung you’re on:

ErrorerrnoMeaningWhere to look
mount error(13)EACCESPermission deniedCredentials or share ACL (rung 2) — like Lab 3’s LOGON_FAILURE / ACCESS_DENIED
mount error(112)EHOSTDOWNHost is downOften a dialect mismatch or name resolving to the wrong host (rung 3)
mount error(115)EINPROGRESSConnection timing outCan’t reach 445 — firewall / port closed / wrong host (rung 1)

The habit to build: don’t jump to “SMB is broken.” Reachability → credentials → dialect → kernel log. Most “SMB” pages are actually a firewall rule, an expired service account, or a vers= mismatch after a server upgrade dropped SMB1.

Bonus — feel the chattiness. Want to viscerally understand why SMB hates the WAN? Inject latency on the client and time a directory walk: docker exec smb-client tc qdisc add dev eth0 root netem delay 100ms, then time docker exec smb-client smbclient //smb-server/secure -U 'labuser%labpass' -c 'ls'. Each round trip now costs 200ms, and a multi-file operation crawls. Remove it with tc qdisc del dev eth0 root. That’s the cost of treating a chatty protocol like a local disk.

SMB in Kubernetes (the platform angle)

You rarely run mount -t cifs by hand in production — you let the platform do it. The SMB CSI driver lets pods consume an SMB share as a PersistentVolume, which is the usual way to get ReadWriteMany storage (many pods writing the same volume — something most block storage can’t do).

The shape, with the exact concepts from this lab in bold:

apiVersion: v1
kind: Secret
metadata:
  name: smb-creds
stringData:
  username: labuser          # ← the credentials file, now a k8s Secret
  password: labpass
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: smb-pv
spec:
  capacity: { storage: 10Gi }
  accessModes: ["ReadWriteMany"]   # ← the reason you reach for SMB/NFS over block storage
  csi:
    driver: smb.csi.k8s.io
    volumeHandle: smb-pv
    volumeAttributes:
      source: "//nas01/builds"     # ← \\server\share, the TREE_CONNECT target
    nodeStageSecretRef:
      name: smb-creds
  mountOptions:
    - vers=3.1.1                    # ← the dialect knob, exactly like Lab 1
    - dir_mode=0777
    - file_mode=0777

Everything you learned maps straight across: source is the TREE_CONNECT target, nodeStageSecretRef is the credentials file, vers= is the dialect. When a pod can’t mount this PV, you debug it with the same ladder from Lab 5 — except now you run it from the node, and the errors show up in the CSI driver/kubelet logs.

When to choose SMB vs the alternatives (a senior makes this call deliberately):

  • SMB — Windows interop, existing NAS, need ReadWriteMany. Chatty; keep it on the LAN.
  • NFS — the Unix-native equivalent; usually simpler and a bit less chatty for Linux-only fleets.
  • Object storage (S3) — for write-once/read-many blobs and anything cross-region. Not a filesystem; no POSIX semantics, but no chattiness tax either. For a build cache across regions, this is usually the right answer — not SMB.

Cleanup

docker exec smb-client sh -c 'umount /mnt/secure 2>/dev/null'
docker rm -f smb-server smb-client
docker network rm smblab
# optionally: docker rmi smb-lab

Common traps

  • “CIFS means SMB1.” No — mount -t cifs is the modern client; it negotiates SMB2/3. The dialect is set by vers=, not the mount type.
  • “Signing protects my data.” Signing protects integrity, not confidentiality. Use smb encrypt for secrecy (Lab 4).
  • “It’s just a network folder, region doesn’t matter.” SMB is chatty; latency multiplies. Cross-region SMB is a self-inflicted incident. Use object storage instead.
  • “The mount is broken” (without checking which rung). 90% of the time it’s reachability (445 blocked), credentials (expired service account), or a vers= mismatch — not SMB itself.
  • Exposing 445. Never to the internet. It’s a worm and credential-relay magnet. LAN/VPN only, SMB1 off.

The mental model

SMB is a stateful conversation, not a disk: negotiate a dialect, prove who you are, attach to a share, open a handle, move bytes, tidy up — each step carrying the IDs from the last. Every file operation is a round trip, which is why it’s fast on a LAN and miserable over a WAN. Security is two separate dials: signing (integrity) and encryption (confidentiality). And the one rule that prevents the worst outcomes: disable SMB1, never expose 445.

Next steps

  1. Add SMB signing and watch a relay get blocked. Set server signing = mandatory, capture, and find the signature field in Wireshark.
  2. Wire up the SMB CSI driver on a local k3s/minikube cluster, point a PV at your lab server, and mount it into a pod. Then break it (wrong vers=) and debug from the node.
  3. Compare with NFS. Stand up an NFS server the same way, capture both, and feel the difference in chattiness for the same ls.
  4. Read the source when it bites you. When a CIFS mount misbehaves, the kernel fs/smb/client/ code and dmesg tell you why — docs say what, source says why.

Connects to: the OSI model (where SMB sits — application data riding TCP at L4), the TCP 3-way handshake (which completes before NEGOTIATE), firewalls (why 445 stays on the LAN), and Kubernetes Secrets (how the CSI driver holds your credentials).

After the lab: capture your lesson

This post is the Active Experimentation stage of a learning cycle — you’ve now run the experiment. Don’t let it evaporate. Run /kolb:cycle again and, in your own words, answer:

  • Reflective Observation: what surprised you? (For me it was that the modern client refuses SMB1 by default, and that the file’s bytes were plainly readable until I forced encryption.)
  • Abstract Conceptualization: state the one principle you’ll keep — crisp enough to stand alone as a flashcard. Write your version; don’t copy the “mental model” box above.

That self-authored principle is what spaced repetition will quiz you on later — and writing it yourself is what makes it stick.