t0ul

Ship, Securely, Ch. 1: Getting Started

Part of Ship, Securely, a hands-on DevSecOps book.

This chapter does two things. First it explains the idea the rest of the book runs on: security built into the pipeline, not bolted on at the end. Then it gets your machine ready to practice it, with Go, Make, an editor, Docker, and a local Kubernetes cluster to deploy into.

Security in the pipeline, not after it

DevOps is a simple idea with a lot of machinery behind it. Development and operations work as one team, and software ships in small, frequent changes that flow through an automated pipeline: build, test, release, repeat. The pipeline is the product. Code only counts once it is running somewhere, so everything that gets it there is scripted, versioned, and repeatable.

Two habits make that work. Continuous integration is merging small changes often, with a build-and-test step that runs on every push and answers one question: does it still work? Continuous delivery extends that automation past the merge, so a change that passes its checks is always ready to ship, or ships on its own.

DevSecOps is that same pipeline with security built into it. Security stops being a gate someone waves you through at the end and becomes a set of checks that run alongside your tests, on the same push. That is the whole move, and it pays for itself the way every control in this book does: a problem caught the moment it is introduced is the cheapest it will ever be to fix.

In CI: feedback a developer can act on

Where code enters the system, security belongs next to compilation and unit tests, fast enough to run on every push:

  • SAST reads your source for insecure patterns.
  • SCA checks your dependencies against known CVEs and licenses.
  • Secret scanning catches a key or token before it ever leaves the laptop.

Run the fast versions on every push and keep the deep scans on a schedule. The point is to get the feedback while the change is still in your hands.

In CD: guard what you are about to ship

Where artifacts and environments move toward production, the checks shift to the thing being released:

  • Scan the container image before you promote it.
  • Check the Terraform, Helm, and Kubernetes manifests for risky defaults, like a public bucket or a privileged pod.
  • Enforce least privilege, TLS, and only the ports you actually need.
  • Keep a canary or blue-green rollout and an automated rollback for when something degrades.

Run it on your own machine first

The thread through the whole book is local-first. Whatever CI and CD will do, do it on your laptop before you push. A Makefile runs the same make test, make lint, and make scan the server will, and a local cluster like Minikube lets you apply manifests, run a smoke test, and rehearse a rollback without touching shared infrastructure. The feedback loop shrinks to seconds, and you learn by doing instead of waiting on a pipeline. The rest of this chapter installs the tools that make that possible.

Pre-requisites

You’ll need the following to work along to the book:

  • Docker
  • Minikube
  • GO
  • JQuery
  • Make
  • VSCODE

I’ve included steps for each one in case you don’t already have it.

Otherwise, feel free to go to the next section.

How to Install Docker (Mac • Windows • Linux)

macOS

Docker Desktop (recommended)

brew install --cask docker   # or download Docker Desktop DMG
open -a Docker               # start it once to initialize
docker --version

Sanity test (first pull is ~tens of MB):

docker run --rm hello-world

Windows

Docker Desktop (WSL 2 backend)

  1. Ensure WSL 2 is on:
wsl --install
  1. Install Docker Desktop:
winget install -e --id Docker.DockerDesktop
  1. Launch Docker Desktop → Settings → Use WSL 2 based engine (default).
  2. Test:
docker --version
docker run --rm hello-world

If corporate policy blocks Docker Desktop, alternatives: Rancher Desktop or Podman Desktop (works with this repo; commands are nearly identical).


Linux

Docker Engine (quick path)

  • Debian/Ubuntu

    sudo apt update
    sudo apt install -y docker.io
    sudo systemctl enable --now docker
    sudo usermod -aG docker $USER   # then log out/in or run: newgrp docker
    docker --version
    docker run --rm hello-world
    
  • Fedora

    sudo dnf install -y docker
    sudo systemctl enable --now docker
    sudo usermod -aG docker $USER
    docker --version
    docker run --rm hello-world
    

How to Install GO (Mac • Windows • Linux)

macOS

Option A — Official pkg (recommended)

  1. Download “macOS pkg installer” from go.dev/dl
  2. Run the installer (works on Intel & Apple Silicon).
  3. Verify:
go version

Option B — Homebrew

brew install go
go version

(Optional) Set up your Go workspace

# Add to ~/.zshrc (or ~/.bashrc)
echo 'export GOPATH="$HOME/go"' >> ~/.zshrc
echo 'export GOBIN="$GOPATH/bin"' >> ~/.zshrc
echo 'export PATH="$PATH:$GOBIN"' >> ~/.zshrc
source ~/.zshrc

Windows

Option A — Winget (fastest)

winget install -e --id GoLang.Go
go version

Option B — Chocolatey

choco install golang -y
go version

Option C — Official MSI

  • Download the Windows installer from go.dev/dl and run it.
  • Open a new PowerShell or Command Prompt:
go version

(Optional) GOPATH

  • By default on Windows, GOPATH is C:\Users\<You>\go.
  • To use GOBIN on your PATH:
setx GOPATH "$env:USERPROFILE\go"
setx GOBIN "$env:GOPATH\bin"
# Add to PATH for the current session:
$env:Path += ";$env:GOBIN"

Linux

Debian/Ubuntu (apt)

sudo apt update
sudo apt install -y golang
go version

Fedora

sudo dnf install -y golang
go version

Arch

sudo pacman -S --needed go
go version

(Optional) Official tarball (all distros)

# Replace X.Y.Z with the latest version for your architecture
sudo rm -rf /usr/local/go
curl -L https://go.dev/dl/goX.Y.Z.linux-amd64.tar.gz | sudo tar -C /usr/local -xz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
go version

Quick sanity test

mkdir -p ~/tmp/hello && cd ~/tmp/hello
cat > main.go << 'EOF'
package main
import "fmt"
func main(){ fmt.Println("hello, ship-securely") }
EOF
go run .

Common hiccups

  • Open a new shell after install so your PATH updates.

  • If go: command not found, ensure Go’s bin directory is on PATH:

    • macOS pkg: /usr/local/go/bin (Intel) or /opt/homebrew/opt/go/libexec/bin (Brew)
    • Linux tarball: /usr/local/go/bin
    • Windows MSI/winget sets PATH automatically; reopen the terminal.

How to Install JQuery (JQ) (Mac • Windows • Linux)

Installing JQuery

These scripts use jq if present: macOS: brew install jq Windows: winget install -e –id jqlang.jq (or choco install jq) Ubuntu: sudo apt install -y jq Fedora: sudo dnf install -y jq The scripts still run without jq—they’ll just show zeros/“n/a” for a couple metrics.

How to Install Make (Mac • Windows • Linux)

macOS

Option A — Fastest (Xcode Command Line Tools)

xcode-select --install
make --version

This installs Apple’s make (BSD). It’s fine for our Makefile.

Option B — GNU make via Homebrew (optional)

brew install make
gmake --version

Homebrew installs it as gmake. If you want make to point to GNU make:

echo 'alias make=gmake' >> ~/.zshrc && source ~/.zshrc

Windows

Pick one of these:

Option A — WSL (recommended)

  1. Enable WSL and install Ubuntu:
wsl --install
# reboot when prompted
  1. In the Ubuntu terminal:
sudo apt update && sudo apt install -y build-essential
make --version

Option B — Chocolatey (native)

Set-ExecutionPolicy Bypass -Scope Process -Force
choco install make -y
make --version

Option C — Scoop (native)

iwr -useb get.scoop.sh | iex
scoop install make
make --version

Option D — MSYS2 (native)

  1. Install MSYS2, then in “MSYS2 MSYS” terminal:
pacman -Syu
pacman -S make
make --version

If you use Git Bash, prefer MSYS2 or WSL—Git Bash alone does not include make.


Linux

  • Debian/Ubuntu

    sudo apt update && sudo apt install -y build-essential
    make --version
    
  • Fedora

    sudo dnf install -y make gcc
    make --version
    
  • Arch

    sudo pacman -S --needed base-devel
    make --version
    

How to Install Minikube (Mac • Windows • Linux)

Minikube Setup (for make ci + make cd)

0) Prereqs

  • Docker Desktop (Mac/Windows) or Docker Engine (Linux)
  • Minikube
  • Make (Windows: use Git Bash or install via Chocolatey/Scoop)
  • (Optional) cosign if you want to sign/verify images

Verify:

docker --version
minikube version
kubectl version --client
make -v

1) Install Minikube

macOS

brew install minikube

Windows (PowerShell as Admin)

choco install minikube
# or: winget install minikube

Linux (Debian/Ubuntu example)

sudo apt-get update && sudo apt-get install -y curl conntrack
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

2) Start a local cluster

We use a dedicated profile called ship so it doesn’t collide with other setups.

Recommended driver: Docker (works on Mac/Windows/Linux)

minikube start -p ship --driver=docker --cpus=2 --memory=4096
minikube -p ship status

Windows (Hyper-V alternative, if Docker Desktop isn’t available):

minikube start -p ship --driver=hyperv --cpus=2 --memory=4096

Tip: If you’ve got 16GB+ RAM, bump to --cpus=4 --memory=6144 for a smoother experience.


3) Point kubectl at Minikube & create the namespace

kubectl config use-context minikube
kubectl get nodes
kubectl create namespace ship || true

Pre-pull common tool images so the class is fast even offline:

docker pull returntocorp/semgrep:latest
docker pull zricethezav/gitleaks:latest
docker pull bridgecrew/checkov:latest
docker pull hadolint/hadolint:latest
docker pull aquasec/trivy:latest
docker pull anchore/syft:latest
docker pull zaproxy/zap-stable:latest

(Or use the warm-cache target below.)


5) Run it

From the repo root:

make ci          # builds & scans locally -> artifacts/
make cd          # starts Minikube (if needed), loads image, applies K8s, verifies
make k8s-port    # prints a URL to hit the app via Minikube

Open the printed URL (e.g., http://127.0.0.1:xxxxx) and try:

  • /
  • /healthz
  • /echo?input=<script>alert(1)</script> (intentional XSS for the lab)

6) Useful Make targets (paste into your Makefile)

Doctor / dashboard / cache warm-up

mk-doctor:
	@echo "== Minikube doctor =="
	minikube -p $(PROFILE) status || true
	kubectl get nodes
	kubectl -n $(NAMESPACE) get deploy,svc || true

mk-dashboard:
	@echo "== Minikube dashboard (opens browser) =="
	minikube -p $(PROFILE) dashboard --url &

warm-cache:
	@echo "== Pre-pulling common tool images =="
	docker pull returntocorp/semgrep:latest
	docker pull zricethezav/gitleaks:latest
	docker pull bridgecrew/checkov:latest
	docker pull hadolint/hadolint:latest
	docker pull aquasec/trivy:latest
	docker pull anchore/syft:latest
	docker pull zaproxy/zap-stable:latest

Then run: make warm-cache and make mk-doctor.


7) Troubleshooting quick hits

A) Can’t get a service URL (make k8s-port prints nothing)

  • Ensure the app was deployed:

    kubectl -n ship get deploy app
    kubectl -n ship get svc app
    
  • Re-apply:

    make k8s-apply && make k8s-verify
    make k8s-port
    

B) ZAP image not found / old name

  • We use zaproxy/zap-stable:latest. If you still have owasp/zap2docker-stable, update the Makefile (already fixed in the starter).

C) Syft CLI changed flags

  • Use the new syntax in the Makefile:

    docker run --rm -v $(PWD):/work anchore/syft:latest \
        scan dir:/work/app -o json > artifacts/sbom.json
    

D) Very slow image loads

  • Confirm Docker driver: minikube start -p ship --driver=docker
  • We use minikube image load (no registry needed). Avoid docker system prune during the course.

E) Windows path/volume issues

  • Run the repo from your user directory (e.g., C:\Users\<you>\...) so Docker Desktop can mount volumes.
  • Use Git Bash or PowerShell. If you see permission or mount errors, restart Docker Desktop and Minikube.

F) DNS/network hiccups inside Minikube

  • Restart Docker Desktop, then:

    minikube -p ship stop && minikube -p ship start
    
  • As a last resort:

    make mk-delete && make mk-up
    

G) Apple Silicon (M1/M2/M3)

  • All images referenced are multi-arch. Docker Desktop handles arm64 transparently.

8) Nice-to-have Minikube add-ons (optional)

  • Dashboard (already wrapped by make mk-dashboard)

  • Ingress if you want nicer hostnames locally:

    minikube -p ship addons enable ingress
    

    (Not required for this starter; minikube service ... --url is simpler and cross-platform.)


9) Clean up

make mk-down     # stop cluster
make mk-delete   # delete the 'ship' profile entirely

How to Install VSCODE (Mac • Windows • Linux)

macOS

Homebrew (recommended)

brew install --cask visual-studio-code
code --version

If code isn’t on PATH: open VS Code → Command Palette → “Shell Command: Install ‘code’ command in PATH”.


Windows

Winget (recommended)

winget install -e --id Microsoft.VisualStudioCode
code --version

Chocolatey

choco install vscode -y
code --version

Linux

Debian/Ubuntu (Microsoft repo)

sudo apt update
sudo apt install -y wget gpg
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /usr/share/keyrings/ms_vscode.gpg > /dev/null
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ms_vscode.gpg] https://packages.microsoft.com/repos/code stable main" | sudo tee /etc/apt/sources.list.d/vscode.list
sudo apt update
sudo apt install -y code
code --version

Fedora

sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
sudo sh -c 'echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com/yumrepos/vscode\nenabled=chapter-1\ngpgcheck=chapter-1\ngpgkey=https://packages.microsoft.com/keys/microsoft.asc" > /etc/yum.repos.d/vscode.repo'
sudo dnf check-update
sudo dnf install -y code
code --version

Arch (AUR)

# Use your AUR helper
paru -S visual-studio-code-bin
code --version

Install from a terminal:

code --install-extension golang.Go
code --install-extension ms-azuretools.vscode-docker
code --install-extension redhat.vscode-yaml
code --install-extension ms-kubernetes-tools.vscode-kubernetes-tools
code --install-extension ms-vscode.makefile-tools
code --install-extension eamodio.gitlens
code --install-extension yzhang.markdown-all-in-one

Why these

  • golang.Go: language server (gopls), debugging, tests, linters
  • Docker/YAML/Kubernetes: editing manifests & seeing cluster state
  • Makefile Tools: task runner UI for your Make targets
  • GitLens: easy code ownership & history
  • Markdown All in One: better docs authoring

Go extension first-run

  • On first open of a Go file, VS Code prompts to install tools. Accept it.
  • Or install manually in a Go-enabled shell:
go install golang.org/x/tools/gopls@latest
go install github.com/go-delve/delve/cmd/dlv@latest

Optional nice-to-haves

code --install-extension streetsidesoftware.code-spell-checker
code --install-extension esbenp.prettier-vscode

Quick VS Code sanity check

  1. Open the starter repo folder in VS Code.
  2. Open app/main.go → you should see “Go” in the status bar (gopls active).
  3. Press Run > Start Debugging (or use the Run icon) to debug the app locally.
  4. Try Terminal > Run Task… and pick a Make target (e.g., ci).

Chapter 1. Checklist

Use this to align your local-first setup with how your org builds and ships software.

Platform & Architecture

  • Delivery model: What do you ship today? (SPA / REST / GraphQL / gRPC / monolith / microservices)
  • Hosting posture: Cloud (which?) / on-prem / air-gapped / hybrid — any internet egress limits for scanners?
  • Runtime target: Containers on K8s? Which cluster(s) will mirror your Minikube layout (namespaces, ingress)?
  • Language mix: Mostly Go, or polyglot? Which languages need SAST/Secrets coverage day 1?

Source Control & CI

  • Repo strategy: Monorepo vs multi-repo — where will the Make targets live (root or per-service)?
  • CI system: What hosted CI will call make later (Actions / GitLab / Jenkins / other)? Who owns the runner images?
  • Branch gates: Do you already enforce PR checks (required status, code owners, signed commits)?

Data & Compliance

  • Data classes: What data does the app touch? (PII/PHI/PCI) Any regulatory constraints that affect tooling choice?
  • License stance: Any license allow/deny policy already? (e.g., GPL, AGPL)
  • Audit evidence: Who needs artifacts (JSON/SBOM/reports), and in what format/cadence?

Local Constraints

  • Developer machines: Are Docker/Minikube feasible for everyone (CPU/RAM policy, Windows/macOS/Linux mix)?
  • Proxy/mirrors: Do dev networks require HTTP(S) proxies or internal package mirrors for builds?

← Back to the blog