Week 2: Networking Basics & Git for Ops

Last week you got comfortable at a Linux shell prompt and connected to a remote box over SSH — that connection worked because of networking fundamentals you took on faith: an IP address, a resolved hostname, an open port. This week makes those fundamentals explicit, from raw addressing and DNS resolution through the TLS handshake that protects HTTPS traffic, and finishes with the Git workflow habits that let a team change infrastructure safely. The request/response model you trace with curl -v here is the same model every container port mapping in Week 3, load balancer in Week 7, and Kubernetes Service in Week 11 is built on top of.

Module 2 of 22 Week 2 of 26 ~3–4 Hours Hands-on Exercise Included

By the end of this week, you'll be able to

  • Explain IP addressing, DNS resolution and the request/response model, and identify common ports on sight
  • Describe what TLS/HTTPS actually protects, and configure a basic firewall rule set
  • Read a curl -v trace end to end, and use Git branching and history as an audit trail for infrastructure changes

1. IP Addressing, DNS & Ports

Every device on a network gets an IP address — a numeric identifier like 203.0.113.10 (IPv4, four 8-bit octets). Addresses starting with 10., 172.16.172.31., or 192.168. are private: they only mean something inside your own network and get translated to a public address by NAT before reaching the internet. You'll see this exact split again in Week 8 as an AWS VPC's public and private subnets.

terminal
10.0.0.0/8       # private -- 10.0.0.0 to 10.255.255.255
172.16.0.0/12    # private -- 172.16.0.0 to 172.31.255.255
192.168.0.0/16   # private -- 192.168.0.0 to 192.168.255.255
203.0.113.10/32  # a single public address (the /32 means "just this host")

The /N suffix is CIDR notation: it says how many leading bits of the address are the fixed "network" part, leaving the rest for individual hosts. A /24 fixes the first 24 bits (the first three octets), leaving 8 bits — 256 addresses — for hosts on that network. Smaller numbers mean bigger networks; you'll size VPC subnets with exactly this math in Week 8.

Humans don't remember IP addresses, so DNS (Domain Name System) maps names to them. Resolving api.example.com walks a chain: your OS checks its local cache, then asks a configured resolver (often your ISP's or a public one like 1.1.1.1), which — if it doesn't already know the answer — asks a root server, then a .com TLD server, then the authoritative nameserver for example.com, which finally returns the real IP.

terminal
$ dig api.example.com +short
203.0.113.10

$ dig api.example.com
;; ANSWER SECTION:
api.example.com.   300   IN   A   203.0.113.10
# 300 = TTL in seconds -- how long resolvers may cache this answer

$ nslookup api.example.com

Once you have an IP, a connection still needs a port — a 16-bit number that tells the receiving machine which service to hand the traffic to. A handful of ports show up constantly in this course:

terminal — well-known ports
22    SSH        # remote shell access (Week 1)
53    DNS        # name resolution
80    HTTP       # unencrypted web traffic
443   HTTPS      # encrypted web traffic (TLS)
3306  MySQL
5432  PostgreSQL
6379  Redis

Communication over these ports follows the request/response model: a client opens a connection and sends a request; the server processes it and sends back a response with a status code (200 OK, 404 Not Found, 500 Internal Server Error) and, usually, a body. Every HTTP API you'll call from a CI pipeline or a Terraform provider follows this exact shape.

DNS TTL is a real operational gotcha

That 300 in the dig output means resolvers may keep serving the old answer for up to five minutes after you change a record. If you're cutting a domain over to a new server, lower the TTL a day in advance so the change propagates fast when you actually flip it — this trips up almost every team the first time they migrate a production hostname.

2. TLS/HTTPS Basics & Firewalls

Plain HTTP sends everything — headers, cookies, request bodies — in cleartext; anyone on the network path can read or modify it. TLS (Transport Layer Security) wraps that traffic in encryption, and HTTPS is just "HTTP over TLS." The handshake happens before a single byte of your actual request is sent: the client and server agree on a cipher, the server proves its identity with a certificate signed by a trusted certificate authority, and both sides derive a shared encryption key for the rest of the connection.

terminal — inspect a live certificate
$ openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates
subject=CN=example.com
issuer=C=US, O=Let's Encrypt, CN=R3
notBefore=Jan  2 00:00:00 2026 GMT
notAfter=Apr  2 23:59:59 2026 GMT

A firewall is a set of allow/deny rules applied to incoming and outgoing traffic, usually by port and source address. On a single Linux host, ufw (Uncomplicated Firewall) is a friendly front end for the kernel's packet filter:

terminal
sudo ufw default deny incoming     # deny everything unless explicitly allowed
sudo ufw default allow outgoing
sudo ufw allow 22/tcp               # SSH
sudo ufw allow 80/tcp                # HTTP
sudo ufw allow 443/tcp               # HTTPS
sudo ufw enable
sudo ufw status verbose

Cloud providers add a second layer on top of the host firewall — AWS calls it a security group, and you'll configure one for real in Week 8. The principle is identical: deny by default, allow only the specific ports and sources a service actually needs.

Never forget your own SSH rule

The single most common self-inflicted outage in this space is enabling ufw or tightening a security group without first allowing your own SSH port — you lock yourself out of a box you can now only reach through a cloud console. Always add the allow rule for the port you're connected on before flipping default-deny on.

3. Reading a curl -v Trace End to End

curl is the single most useful debugging tool for anything HTTP-shaped, and -v (verbose) shows every stage of the request/response model in order — DNS, connection, TLS, headers, body. Learning to read this output on sight will save you from guessing when an API call fails in a pipeline.

terminal
$ curl -v https://api.example.com/health
*   Trying 203.0.113.10:443...
* Connected to api.example.com (203.0.113.10) port 443
* TLS handshake, Client hello (1):
* TLS handshake, Server hello (2):
* TLSv1.3 connection using TLS_AES_256_GCM_SHA384
* Server certificate: CN=api.example.com
> GET /health HTTP/1.1
> Host: api.example.com
> User-Agent: curl/8.5.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< content-type: application/json
< content-length: 15
<
{"status":"ok"}
* Connection #0 to host api.example.com left intact

Lines starting with * are curl's own status messages: DNS resolution, the TCP connection, and the TLS handshake, all happening before your request is sent. Lines starting with > are the exact bytes curl sent — the request line and headers. Lines starting with < are what the server sent back — the status line and response headers — followed by the response body with no prefix at all. If a trace stops after the * lines, the problem is networking or TLS, not your application; if it stops after the > lines with no < response, the server accepted the connection but never answered — often a slow backend or a security group blocking the return path.

terminal — other curl flags worth knowing
curl -I https://api.example.com          # headers only, no body (HEAD request)
curl -o /dev/null -s -w "%{http_code} %{time_total}s\n" https://api.example.com
curl --resolve api.example.com:443:203.0.113.99 https://api.example.com
                                          # force a hostname to resolve to a specific IP,
                                          # useful for testing a new server before the DNS cutover
--resolve beats editing /etc/hosts

When you need to test that a new server is actually serving the right content before you flip DNS over to it, curl --resolve is scoped to that single command and leaves no cleanup behind — unlike editing /etc/hosts, which is easy to forget and later debug an unrelated "why is this URL going to the wrong place" mystery.

4. Git Branching Workflows for Ops

Treating infrastructure config the same way you treat application code — version-controlled, reviewed, and reversible — is a core DevOps habit. The pattern is a feature branch per change, a pull request for review, and a protected main branch that only changes through a merge:

terminal
git checkout -b add-nginx-rate-limit
# ...edit nginx.conf...
git add nginx.conf
git commit -m "Add rate limiting to /api routes to stop the scraper traffic we saw Tuesday"
git push -u origin add-nginx-rate-limit
# open a pull request in the GitHub UI, request a review, merge once approved

This matters more for infrastructure than for application code: a bad application deploy usually breaks a feature, but a bad infrastructure change can take down everything at once. A required review before merge is a second set of eyes on exactly the kind of change — a firewall rule, a DNS record, a Terraform resource — that's easy to get subtly wrong and expensive to get wrong in production.

Once changes are merged, Git history itself becomes an audit trail: every infrastructure change has an author, a timestamp, a message explaining why, and a full diff of exactly what changed.

terminal
git log --oneline --graph -- nginx.conf
# a94f2c1 Add rate limiting to /api routes to stop the scraper traffic we saw Tuesday
# 7b1e0aa Increase client_max_body_size for file uploads
# 3d40912 Initial nginx reverse proxy config

git blame nginx.conf              # who last touched each line, and in which commit
git show a94f2c1                  # the full diff and commit message for one change
git tag -a v1.4.0 -m "Nginx config as deployed to prod, 2026-08-03"

When something breaks in production and you need to know "what changed and who approved it," git log and git blame answer that in seconds — no separate change-management system required. If a merged change turns out to be wrong, git revert creates a new commit that undoes it, which is safer in a team setting than rewriting history with git reset.

Write commit messages for the 3am on-call engineer

"Fix bug" tells a future debugger nothing. "Add rate limiting to /api routes to stop the scraper traffic we saw Tuesday" tells them what changed, why, and gives them a starting point if the same symptom reappears. You'll see this same discipline pay off directly when you write GitHub Actions workflows in Week 6 that key off commit messages and PR metadata.

5. Hands-on Exercise

Hands-on

Trace a live request and commit it as a runbook

Combine DNS lookups, a curl trace, and a proper Git ops workflow into one reviewable artifact.

Requirements:

  1. Pick any public HTTPS site and run dig <domain> +short to resolve it, then openssl s_client -connect <domain>:443 -servername <domain> piped into openssl x509 -noout -subject -issuer -dates to inspect its certificate.
  2. Run curl -v https://<domain> and save the full output.
  3. In a local git repo, create a branch named add-request-runbook.
  4. Write runbook.md documenting, in your own words, what each stage of the curl -v output means (DNS/connect, TLS handshake, request headers, response headers, body) and the certificate's issuer and expiry date.
  5. Commit with a message explaining what the runbook is for, push the branch, and open a pull request against main (a repo you own is fine — the goal is practicing the workflow).
  6. Merge the PR, then run git log --oneline and confirm the commit message alone explains what changed and why, with no need to reread the diff.
Hint

If openssl s_client hangs waiting for input, add </dev/null to the end of the command — it stops the tool from waiting on stdin and lets the pipe to openssl x509 complete immediately.

6. Knowledge Check

Four quick questions. Expand each to check your answer.

Q1

Why can't a device with the private address 10.0.0.5 reach the public internet on its own?

Private ranges like 10.0.0.0/8 aren't routable on the public internet — routers outside your network don't know how to send traffic back to them, and duplicate use across countless private networks means a public reply couldn't know which network to return to. A NAT gateway rewrites the source address to a public one on the way out and reverses the mapping on the way back, which is exactly what happens at the edge of an AWS VPC's private subnet in Week 8.

Q2

In a curl -v trace, what does it mean if the output stops right after the last > line with no < response following it?

> lines are what curl sent to the server, and < lines are what the server sent back. If the trace stops after your request was fully sent but before any response arrives, the connection and TLS handshake succeeded, so the server accepted the request — the problem is on the server side: it's hanging, overloaded, or a security group/load balancer is silently dropping the return traffic rather than rejecting the connection outright.

Q3

Why might a DNS record change not take effect immediately, even right after you update it?

DNS answers are cached by resolvers for as long as the record's TTL (time-to-live) says, so anyone who already resolved the old value keeps using it until that cached entry expires — regardless of when the authoritative record actually changed. That's why a planned cutover should lower the TTL well in advance, so the old cached answers expire quickly once the record is actually flipped.

Q4

Why is Git history described as an "audit trail" for infrastructure changes — what does it give you that a shared doc or chat log doesn't?

Every commit is immutably tied to an author, a timestamp, and an exact diff of what changed, and git blame/git log let you jump straight from "this line is wrong" to "who changed it, when, and why they said they did" without hunting through separate chat history or hoping someone remembered to update a doc. A pull request adds a recorded approval on top of that, so the trail covers both what changed and who signed off on it.