diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..050fe8744 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +## Goal + + +## Changes +- + +## Testing + + +## Checklist +- [ ] Title is a clear sentence (≤ 70 chars) +- [ ] Commits are signed (git log --show-signature) +- [ ] submissions/labN.md updated diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..decaf8654 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,84 @@ +name: quicknotes-ci + +on: + push: + branches: [main] + paths: + - "app/**" + - ".github/workflows/ci.yml" + - "submissions/**" + pull_request: + branches: [main] + paths: + - "app/**" + - ".github/workflows/ci.yml" + - "submissions/**" + +permissions: + contents: read + +jobs: + vet: + name: vet (${{ matrix.go-version }}) + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + go-version: ["1.23", "1.24"] + defaults: + run: + working-directory: app + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: ${{ matrix.go-version }} + cache: true + cache-dependency-path: app/go.mod + - run: go vet ./... + + test: + name: test (${{ matrix.go-version }}) + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + go-version: ["1.23", "1.24"] + defaults: + run: + working-directory: app + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: ${{ matrix.go-version }} + cache: true + cache-dependency-path: app/go.mod + - run: go test -race -count=1 ./... + + lint: + name: lint + runs-on: ubuntu-24.04 + defaults: + run: + working-directory: app + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 + with: + go-version: "1.24" + cache: true + cache-dependency-path: app/go.mod + - uses: golangci/golangci-lint-action@db582008a42febd596419635a5abc9d9815daa9c # v9.2.1 + with: + version: v2.5.0 + working-directory: app + + ci-ok: + name: ci-ok + if: always() + needs: [vet, test, lint] + runs-on: ubuntu-24.04 + steps: + - run: | + test "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" = "false" \ No newline at end of file diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 000000000..7663a9533 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,58 @@ +Vagrant.configure("2") do |config| + config.vm.box = "ubuntu/jammy64" + config.vm.hostname = "quicknotes-vm" + + config.vm.network "forwarded_port", + guest: 8080, + host: 18080, + host_ip: "127.0.0.1" + + config.vm.synced_folder "./app", "/opt/quicknotes/app", type: "virtualbox" + + config.vm.provider "virtualbox" do |vb| + vb.name = "quicknotes-lab5" + vb.cpus = 2 + vb.memory = 1024 + end + + config.vm.provision "shell", inline: <<-SHELL + set -eux + + GO_VERSION="1.24.5" + GO_TARBALL="go${GO_VERSION}.linux-amd64.tar.gz" + + apt-get update + apt-get install -y curl ca-certificates build-essential + + if ! /usr/local/go/bin/go version 2>/dev/null | grep -q "go${GO_VERSION}"; then + rm -rf /usr/local/go + curl -fsSLo "/tmp/${GO_TARBALL}" "https://go.dev/dl/${GO_TARBALL}" + tar -C /usr/local -xzf "/tmp/${GO_TARBALL}" + ln -sf /usr/local/go/bin/go /usr/local/bin/go + ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt + fi + + cd /opt/quicknotes/app + /usr/local/go/bin/go build -o /usr/local/bin/quicknotes . + + cat >/etc/systemd/system/quicknotes.service <<'EOF' +[Unit] +Description=QuickNotes service +After=network.target + +[Service] +WorkingDirectory=/opt/quicknotes/app +Environment=ADDR=:8080 +ExecStart=/usr/local/bin/quicknotes +Restart=always +RestartSec=2 + +[Install] +WantedBy=multi-user.target +EOF + + systemctl daemon-reload + systemctl enable quicknotes + systemctl restart quicknotes + SHELL +end \ No newline at end of file diff --git a/ansible/files/quicknotes b/ansible/files/quicknotes new file mode 100644 index 000000000..868b4a14e Binary files /dev/null and b/ansible/files/quicknotes differ diff --git a/ansible/inventory.ini b/ansible/inventory.ini new file mode 100644 index 000000000..fa153fecf --- /dev/null +++ b/ansible/inventory.ini @@ -0,0 +1,2 @@ +[quicknotes] +lab5-vm ansible_host=172.22.0.1 ansible_port=2223 ansible_user=vagrant ansible_ssh_private_key_file=~/.ssh/vagrant-lab7/private_key ansible_python_interpreter=/usr/bin/python3 ansible_ssh_common_args='-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' \ No newline at end of file diff --git a/ansible/playbook.yaml b/ansible/playbook.yaml new file mode 100644 index 000000000..1aeb3b370 --- /dev/null +++ b/ansible/playbook.yaml @@ -0,0 +1,74 @@ +--- +- name: Deploy QuickNotes + hosts: quicknotes + become: true + gather_facts: false + + vars: + quicknotes_user: quicknotes + quicknotes_data_dir: /var/lib/quicknotes + listen_addr: ":8080" + data_path: /var/lib/quicknotes/notes.json + seed_path: /var/lib/quicknotes/seed.json + restart_sec: 2 + + tasks: + - name: Ensure quicknotes system user exists + ansible.builtin.user: + name: "{{ quicknotes_user }}" + system: true + shell: /usr/sbin/nologin + create_home: false + + - name: Ensure data directory exists + ansible.builtin.file: + path: "{{ quicknotes_data_dir }}" + state: directory + owner: "{{ quicknotes_user }}" + group: "{{ quicknotes_user }}" + mode: "0750" + + - name: Copy seed file + ansible.builtin.copy: + src: ../app/seed.json + dest: "{{ seed_path }}" + owner: "{{ quicknotes_user }}" + group: "{{ quicknotes_user }}" + mode: "0640" + + - name: Copy QuickNotes binary + ansible.builtin.copy: + src: files/quicknotes + dest: /usr/local/bin/quicknotes + owner: root + group: root + mode: "0755" + notify: restart quicknotes + + - name: Render systemd unit + ansible.builtin.template: + src: templates/quicknotes.service.j2 + dest: /etc/systemd/system/quicknotes.service + owner: root + group: root + mode: "0644" + notify: + - reload systemd + - restart quicknotes + + - name: Enable and start QuickNotes service + ansible.builtin.systemd: + name: quicknotes + enabled: true + state: started + daemon_reload: true + + handlers: + - name: reload systemd + ansible.builtin.systemd: + daemon_reload: true + + - name: restart quicknotes + ansible.builtin.systemd: + name: quicknotes + state: restarted diff --git a/ansible/templates/quicknotes.service.j2 b/ansible/templates/quicknotes.service.j2 new file mode 100644 index 000000000..157dca30c --- /dev/null +++ b/ansible/templates/quicknotes.service.j2 @@ -0,0 +1,18 @@ +[Unit] +Description=QuickNotes service +After=network-online.target +Wants=network-online.target + +[Service] +User={{ quicknotes_user }} +Group={{ quicknotes_user }} +WorkingDirectory={{ quicknotes_data_dir }} +Environment=ADDR={{ listen_addr }} +Environment=DATA_PATH={{ data_path }} +Environment=SEED_PATH={{ seed_path }} +ExecStart=/usr/local/bin/quicknotes +Restart=on-failure +RestartSec={{ restart_sec }} + +[Install] +WantedBy=multi-user.target diff --git a/app/Dockerfile b/app/Dockerfile new file mode 100644 index 000000000..1cac8a00d --- /dev/null +++ b/app/Dockerfile @@ -0,0 +1,26 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.24-alpine AS builder + +WORKDIR /src + +COPY go.mod ./ +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" -o /out/quicknotes . + +FROM gcr.io/distroless/static-debian12:nonroot + +WORKDIR / + +COPY --from=builder /out/quicknotes /quicknotes +COPY --from=builder /src/seed.json /seed.json + +EXPOSE 8080 + +USER nonroot:nonroot + +ENTRYPOINT ["/quicknotes"] \ No newline at end of file diff --git a/app/handlers.go b/app/handlers.go index c534979c5..187c418b0 100644 --- a/app/handlers.go +++ b/app/handlers.go @@ -26,7 +26,7 @@ func NewServer(store *Store) *Server { return &Server{store: store, requestsByCode: by} } -func (s *Server) Routes() *http.ServeMux { +func (s *Server) Routes() http.Handler { mux := http.NewServeMux() mux.HandleFunc("GET /health", s.wrap(s.handleHealth)) mux.HandleFunc("GET /metrics", s.wrap(s.handleMetrics)) @@ -34,7 +34,7 @@ func (s *Server) Routes() *http.ServeMux { mux.HandleFunc("POST /notes", s.wrap(s.handleCreateNote)) mux.HandleFunc("GET /notes/{id}", s.wrap(s.handleGetNote)) mux.HandleFunc("DELETE /notes/{id}", s.wrap(s.handleDeleteNote)) - return mux + return securityHeaders(mux) } type statusWriter struct { diff --git a/app/handlers_test.go b/app/handlers_test.go index 9dff2e3e5..7bc799d66 100644 --- a/app/handlers_test.go +++ b/app/handlers_test.go @@ -1,6 +1,7 @@ package main import ( + "os" "bytes" "encoding/json" "net/http" @@ -131,3 +132,36 @@ func TestMetrics_ExposesPrometheusFormat(t *testing.T) { } } +func TestSecurityHeadersPresent(t *testing.T) { + path := t.TempDir() + "/notes.json" + if err := os.WriteFile(path, []byte("[]"), 0o644); err != nil { + t.Fatalf("write test store: %v", err) + } + + store, err := NewStore(path) + if err != nil { + t.Fatalf("new store: %v", err) + } + + server := NewServer(store) + + req := httptest.NewRequest(http.MethodGet, "/health", nil) + rr := httptest.NewRecorder() + + server.Routes().ServeHTTP(rr, req) + + tests := map[string]string{ + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "DENY", + "Referrer-Policy": "no-referrer", + "Content-Security-Policy": "default-src 'none'", + "Permissions-Policy": "camera=(), microphone=(), geolocation=()", + "Cache-Control": "no-store", + } + + for header, want := range tests { + if got := rr.Header().Get(header); got != want { + t.Fatalf("%s = %q, want %q", header, got, want) + } + } +} diff --git a/app/security.go b/app/security.go new file mode 100644 index 000000000..cf4e53471 --- /dev/null +++ b/app/security.go @@ -0,0 +1,15 @@ +package main + +import "net/http" + +func securityHeaders(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("X-Frame-Options", "DENY") + w.Header().Set("Referrer-Policy", "no-referrer") + w.Header().Set("Content-Security-Policy", "default-src 'none'") + w.Header().Set("Permissions-Policy", "camera=(), microphone=(), geolocation=()") + w.Header().Set("Cache-Control", "no-store") + next.ServeHTTP(w, r) + }) +} diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..c1e9aa0c4 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,72 @@ +services: + quicknotes-init: + image: busybox:1.36 + command: ["sh", "-c", "mkdir -p /data && chown -R 65532:65532 /data"] + volumes: + - quicknotes-data:/data + + quicknotes: + build: + context: ./app + image: quicknotes:lab6 + depends_on: + quicknotes-init: + condition: service_completed_successfully + ports: + - "8080:8080" + environment: + ADDR: ":8080" + DATA_PATH: "/data/notes.json" + SEED_PATH: "/seed.json" + volumes: + - quicknotes-data:/data + user: "65532:65532" + restart: unless-stopped + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + read_only: true + tmpfs: + - /tmp + + quicknotes-health: + image: curlimages/curl:8.11.1 + depends_on: + - quicknotes + command: + - sh + - -c + - | + while true; do + curl -fsS http://quicknotes:8080/health && sleep 30 || sleep 2 + done + restart: unless-stopped + + prometheus: + image: prom/prometheus:v3.6.0 + depends_on: + - quicknotes + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - ./monitoring/prometheus/rules:/etc/prometheus/rules:ro + restart: unless-stopped + + grafana: + image: grafana/grafana:12.2.0 + depends_on: + - prometheus + ports: + - "3000:3000" + environment: + GF_SECURITY_ADMIN_USER: "lab8admin" + GF_SECURITY_ADMIN_PASSWORD: "lab8StrongPassword123!" + volumes: + - ./monitoring/grafana/provisioning:/etc/grafana/provisioning:ro + - ./monitoring/grafana/dashboards:/var/lib/grafana/dashboards:ro + restart: unless-stopped + +volumes: + quicknotes-data: \ No newline at end of file diff --git a/docs/runbook/high-error-rate.md b/docs/runbook/high-error-rate.md new file mode 100644 index 000000000..ee7cb799a --- /dev/null +++ b/docs/runbook/high-error-rate.md @@ -0,0 +1,46 @@ +\# QuickNotes High Error Rate Runbook + + + +\## What this alert means + + + +More than 5% of QuickNotes HTTP responses are 4xx or 5xx for at least 5 minutes. + + + +\## Triage steps + + + +1\. Open the Grafana QuickNotes Golden Signals dashboard and check whether errors increased together with traffic. + +2\. Open Prometheus and run the error-ratio query from the alert rule to confirm the alert condition. + +3\. Check QuickNotes logs with `docker compose logs quicknotes --tail 100`. + +4\. Check whether recent traffic includes malformed `POST /notes` requests. + +5\. Verify that `/health` still returns HTTP 200. + + + +\## Mitigations + + + +1\. If malformed client traffic is causing the issue, temporarily block or throttle the bad client. + +2\. If the service is unstable, restart QuickNotes with `docker compose restart quicknotes`. + +3\. If a recent change caused the issue, roll back to the previous known-good version. + + + +\## Post-incident + + + +After the incident, write a short blameless postmortem using the Lecture 1 postmortem template. Include impact, timeline, root cause, detection gap, and action items. + diff --git a/monitoring/grafana/dashboards/golden-signals.json b/monitoring/grafana/dashboards/golden-signals.json new file mode 100644 index 000000000..ada9f7cee --- /dev/null +++ b/monitoring/grafana/dashboards/golden-signals.json @@ -0,0 +1,73 @@ +{ + "id": null, + "uid": "quicknotes-golden-signals", + "title": "QuickNotes Golden Signals", + "tags": ["quicknotes", "sre", "golden-signals"], + "timezone": "browser", + "schemaVersion": 39, + "version": 1, + "refresh": "15s", + "time": { + "from": "now-15m", + "to": "now" + }, + "panels": [ + { + "id": 1, + "type": "timeseries", + "title": "Latency Proxy - Request Rate", + "description": "QuickNotes does not expose a request duration histogram, so request rate is used as a latency proxy for this lab.", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }, + "targets": [ + { + "refId": "A", + "expr": "sum(rate(quicknotes_http_requests_total[1m]))", + "legendFormat": "requests/sec" + } + ], + "datasource": { "type": "prometheus", "uid": "prometheus" } + }, + { + "id": 2, + "type": "timeseries", + "title": "Traffic - HTTP Requests per Second", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }, + "targets": [ + { + "refId": "A", + "expr": "sum(rate(quicknotes_http_requests_total[1m]))", + "legendFormat": "total rps" + } + ], + "datasource": { "type": "prometheus", "uid": "prometheus" } + }, + { + "id": 3, + "type": "timeseries", + "title": "Errors - 4xx/5xx Error Ratio", + "gridPos": { "h": 8, "w": 12, "x": 0, "y": 8 }, + "targets": [ + { + "refId": "A", + "expr": "sum(rate(quicknotes_http_responses_by_code_total{code=~\"4..|5..\"}[1m])) / sum(rate(quicknotes_http_responses_by_code_total[1m]))", + "legendFormat": "error ratio" + } + ], + "datasource": { "type": "prometheus", "uid": "prometheus" } + }, + { + "id": 4, + "type": "timeseries", + "title": "Saturation - Notes Stored", + "gridPos": { "h": 8, "w": 12, "x": 12, "y": 8 }, + "targets": [ + { + "refId": "A", + "expr": "quicknotes_notes_total", + "legendFormat": "notes" + } + ], + "datasource": { "type": "prometheus", "uid": "prometheus" } + } + ] +} diff --git a/monitoring/grafana/provisioning/dashboards/dashboard.yml b/monitoring/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 000000000..8415c574c --- /dev/null +++ b/monitoring/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,12 @@ +apiVersion: 1 + +providers: + - name: "QuickNotes" + orgId: 1 + folder: "QuickNotes" + type: file + disableDeletion: false + editable: true + updateIntervalSeconds: 30 + options: + path: /var/lib/grafana/dashboards \ No newline at end of file diff --git a/monitoring/grafana/provisioning/datasources/datasource.yml b/monitoring/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 000000000..fe7fa2053 --- /dev/null +++ b/monitoring/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,10 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + uid: prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true \ No newline at end of file diff --git a/monitoring/prometheus/prometheus.yml b/monitoring/prometheus/prometheus.yml new file mode 100644 index 000000000..713b176d6 --- /dev/null +++ b/monitoring/prometheus/prometheus.yml @@ -0,0 +1,10 @@ +global: + scrape_interval: 15s + +rule_files: + - /etc/prometheus/rules/*.yml + +scrape_configs: + - job_name: "quicknotes" + static_configs: + - targets: ["quicknotes:8080"] \ No newline at end of file diff --git a/monitoring/prometheus/rules/alerts.yml b/monitoring/prometheus/rules/alerts.yml new file mode 100644 index 000000000..c3c130f13 --- /dev/null +++ b/monitoring/prometheus/rules/alerts.yml @@ -0,0 +1,17 @@ +groups: + - name: quicknotes-alerts + rules: + - alert: QuickNotesHighErrorRate + expr: | + ( + sum(rate(quicknotes_http_responses_by_code_total{code=~"4..|5.."}[1m])) + / + sum(rate(quicknotes_http_responses_by_code_total[1m])) + ) > 0.05 + for: 5m + labels: + severity: page + annotations: + summary: "QuickNotes high HTTP error rate" + description: "More than 5% of QuickNotes HTTP responses are 4xx or 5xx for at least 5 minutes" + runbook: "docs/runbook/high-error-rate.md" \ No newline at end of file diff --git a/submissions/lab1.md b/submissions/lab1.md new file mode 100644 index 000000000..cc2b52ac8 --- /dev/null +++ b/submissions/lab1.md @@ -0,0 +1,147 @@ +# Lab 1 submission + +## Task 1. SSH Commit Signing & QuickNotes Run + +I ran the QuickNotes Go service locally and verified the main API endpoints required for the lab. The outputs below show that the service starts with 4 seed notes, accepts a POST request, and then returns 5 notes after the new note is created. + +### GET /health + +```json +{ + "notes": 4, + "status": "ok" +} +``` + +### GET /notes + +```json +[ + { + "id": 4, + "title": "Endpoint cheat-sheet", + "body": "GET /notes GET /notes/{id} POST /notes DELETE /notes/{id} GET /health GET /metrics", + "created_at": "2026-01-15T10:15:00Z" + }, + { + "id": 1, + "title": "Welcome to QuickNotes", + "body": "This is the project you'll containerize, deploy, monitor, and harden across all 10 labs.", + "created_at": "2026-01-15T10:00:00Z" + }, + { + "id": 2, + "title": "Read app/main.go first", + "body": "Start by understanding the entry point ??? env vars, signal handling, graceful shutdown.", + "created_at": "2026-01-15T10:05:00Z" + }, + { + "id": 3, + "title": "DevOps mantra", + "body": "If it hurts, do it more often.", + "created_at": "2026-01-15T10:10:00Z" + } +] +``` + +### POST /notes + +```json +{ + "id": 5, + "title": "hello", + "body": "first POST", + "created_at": "2026-06-09T16:15:20.8332538Z" +} +``` + +### GET /notes after POST + +```json +[ + { + "id": 1, + "title": "Welcome to QuickNotes", + "body": "This is the project you'll containerize, deploy, monitor, and harden across all 10 labs.", + "created_at": "2026-01-15T10:00:00Z" + }, + { + "id": 2, + "title": "Read app/main.go first", + "body": "Start by understanding the entry point ??? env vars, signal handling, graceful shutdown.", + "created_at": "2026-01-15T10:05:00Z" + }, + { + "id": 3, + "title": "DevOps mantra", + "body": "If it hurts, do it more often.", + "created_at": "2026-01-15T10:10:00Z" + }, + { + "id": 4, + "title": "Endpoint cheat-sheet", + "body": "GET /notes GET /notes/{id} POST /notes DELETE /notes/{id} GET /health GET /metrics", + "created_at": "2026-01-15T10:15:00Z" + }, + { + "id": 5, + "title": "hello", + "body": "first POST", + "created_at": "2026-06-09T16:15:20.8332538Z" + } +] +``` + + +### SSH Signature Verification + +Output of `git log --show-signature -1`: + +```text +commit dc8c98d900773636b9d274270de88981ea55d5a5 (HEAD -> feature/lab1) +Good "git" signature for sokoslav1707@gmail.com with ED25519 key SHA256:... +Author: Yaroslav Sokolov +Date: Tue Jun 9 19:21:06 2026 +0300 + + docs(lab1): start submission + + Signed-off-by: Yaroslav Sokolov +``` + +### Verified Commit Screenshot + +![Verified Commit](src/screenshots/lab01/task1_verified_commit_screenshot.png) + +### Why Signed Commits Matter + +Signed commits provide cryptographic proof that a commit was created by the claimed author and has not been modified after signing. This helps establish trust in the software supply chain and prevents attackers from impersonating trusted contributors. + +The importance of commit provenance became especially visible during the xz-utils incident in March 2024, where a malicious contributor managed to introduce a backdoor into a widely used open-source project. Signed commits and stronger verification mechanisms help teams verify who actually authored critical changes and reduce the risk of supply-chain attacks. + + +## Task 2. Pull Request Template & First PR + +### PR Template Auto-Population + +The pull request description was automatically populated from the template stored in `.github/pull_request_template.md`. + +![PR Template Auto-Population](src/screenshots/lab01/task2_pr_template.png) + + +## Task 3. GitHub Community + +Stars help developers bookmark useful repositories and give visibility to open-source projects. Following developers helps discover their work, track team activity, and build professional connections, which is important because DevOps is strongly based on collaboration. + +## Bonus Task. Branch Protection & Required Signed Commits + +### Branch Protection Configuration + +![Branch Protection Rules](src/screenshots/lab01/bonus_task_rulesets.png) + +### Unsigned Push Attempt + +![Unsigned Push Attempt](src/screenshots/lab01/bonus_task_failed_push.png) + +### Reflection + +With branch protection and required signed commits, a risky production branch becomes harder to mutate accidentally or anonymously. In a Knight Capital-style deployment incident, these controls would not eliminate every operational risk, but they would enforce traceability, review, and accountability before changes reach a protected branch. Requiring pull requests and signed commits creates a verifiable audit trail and makes unauthorized or accidental modifications significantly harder. diff --git a/submissions/lab2.md b/submissions/lab2.md new file mode 100644 index 000000000..cde5f2824 Binary files /dev/null and b/submissions/lab2.md differ diff --git a/submissions/lab3.md b/submissions/lab3.md new file mode 100644 index 000000000..5219af5f1 --- /dev/null +++ b/submissions/lab3.md @@ -0,0 +1,178 @@ +# Lab 3 Submission + +## Task 1. PR Gate + +### Workflow Overview + +I implemented a GitHub Actions CI pipeline for QuickNotes in `.github/workflows/ci.yml`. + +The workflow runs: + +* `go vet ./...` +* `go test -race -count=1 ./...` +* `golangci-lint run` with pinned `golangci-lint v2.5.0` +* aggregate `ci-ok` status check + +The workflow runs on: + +* Pull Requests targeting `main` +* Pushes to `main` + +The runtime is pinned to `ubuntu-24.04`. All GitHub Actions are pinned by full commit SHA, and the workflow uses least-privilege permissions with `contents: read`. + +### Green CI Run + +Workflow run: + +https://github.com/BuiniyYarik/DevOps-Intro/actions/runs/27637205088 + +Screenshot: + +![Green CI](src/screenshots/lab03/green_ci.png) + +### Failed CI Demonstration + +To prove that the gate works, I deliberately introduced a failing test. + +Failing commit: + +``` +28ffa8d test(lab3): deliberately break CI +``` + +The failing commit caused the `test` jobs and the aggregate `ci-ok` check to fail. + +Screenshot: + +![Failed CI](src/screenshots/lab03/failed_ci.png) + +### Recovery + +I fixed the failed CI by reverting the deliberate breakage. + +Fix commit: + +``` +83a5713 Revert "test(lab3): deliberately break CI" +``` + +After this revert, the CI pipeline became green again. + +### Branch Protection + +The `main` branch in my fork is protected with: + +* Require a pull request before merging +* Require status checks to pass before merging +* Require branches to be up to date before merging + +Screenshot: + +![Branch Protection](src/screenshots/lab03/branch_protection.png) + +### Merge Protection + +When CI checks fail, the PR cannot satisfy the required checks. + +Screenshot: + +![Merge Blocked](src/screenshots/lab03/merge_blocked.png) + +### Design Questions + +#### a) Why pin the runner version (`ubuntu-24.04`) instead of `ubuntu-latest`? + +`ubuntu-latest` is a moving target. GitHub may update it to a newer operating system version that contains different tools, libraries, or defaults. A previously green pipeline could start failing without any repository changes. Pinning `ubuntu-24.04` makes the CI environment reproducible and predictable. + +#### b) Why split vet, test, and lint into separate units? + +Separate jobs improve observability and allow parallel execution. If all checks are combined into one job, the first failure can stop execution and hide additional problems. Independent jobs make it easier to identify the root cause of failures and reduce overall wall-clock time through parallelism. + +#### c) What real attack does SHA pinning prevent? + +SHA pinning protects against supply-chain attacks where a GitHub Action tag is modified or compromised. A relevant example is the March 2025 compromise of `tj-actions/changed-files`, discussed in Lecture 3. Pinning actions to immutable commit SHAs ensures that CI executes exactly the reviewed code rather than whatever code a mutable tag may reference in the future. + +#### d) What is `permissions:` and what principle is behind it? + +`permissions:` controls the access rights of the automatically generated GitHub Actions token. This workflow only requires read access to repository contents, so it uses `contents: read`. This follows the principle of least privilege, which grants only the permissions necessary to perform the required task. + +#### e) GitLab path: what is the difference between a stage and a job? What would `dependencies:` do that `stages:` does not? + +I implemented the GitHub Actions path. In GitLab CI, a job is an individual unit of work, while a stage is a logical grouping of jobs that controls execution order. `stages:` determine when jobs run, whereas `dependencies:` determine which artifacts from previous jobs are downloaded by a later job. + +--- + +## Task 2. Fast and Smart Pipeline + +### Optimizations Applied + +I applied the following optimizations: + +* Enabled Go module and build caching through `actions/setup-go`. +* Added a matrix for Go 1.23 and Go 1.24. +* Set `fail-fast: false` for matrix jobs. +* Added path filters so CI runs only when application code or workflow files change. +* Added a stable `ci-ok` aggregation job for branch protection. + +### Timing Measurements + +The following wall-clock times were measured from GitHub Actions UI. + +| Scenario | Wall-clock | +| ------------------------------------------------------ | ---------- | +| Baseline (no cache, single Go version, no path filter) | 1m 14s | +| With cache | 57s | +| With cache + matrix | 1m 29s | + +Screenshots: + +![Baseline Timing](src/screenshots/lab03/timing_baseline.png) + +![Cache Timing](src/screenshots/lab03/timing_cache.png) + +![Matrix Timing](src/screenshots/lab03/timing_matrix.png) + +### Design Questions + +#### f) Why cache `go.sum`-keyed inputs and not build outputs? + +Caches should be keyed by deterministic inputs such as dependency versions specified in `go.sum`. Build outputs can depend on compiler versions, operating systems, environment variables, and other external factors. Reusing stale build outputs may produce incorrect or inconsistent results, while dependency caches remain predictable and safe. + +#### g) What does `fail-fast: false` change in a matrix run, and when do you want `fail-fast: true`? + +With `fail-fast: false`, all matrix jobs continue running even if one matrix cell fails. This provides complete diagnostic information and helps identify whether failures affect only specific environments. `fail-fast: true` is useful when reducing CI costs or waiting time is more important than collecting full failure information. + +#### h) What is the risk of an attacker writing a cache from a malicious PR that protected branches later read? + +The primary risk is cache poisoning. A malicious contributor could attempt to inject harmful content into a shared cache and have trusted workflows later restore it. GitHub mitigates this through cache isolation and permission boundaries, but workflows should still avoid caching untrusted build outputs and should use deterministic cache keys whenever possible. + +### Optimization Discussion + +Caching reduced the measured wall-clock time from 74 seconds to 57 seconds. However, QuickNotes contains almost no external dependencies, so large improvements are not expected. + +The matrix increased total wall-clock time because more jobs execute. This is an intentional tradeoff that increases confidence by validating the application against multiple Go versions. + +Path filters provide the largest practical productivity improvement because documentation-only changes can completely skip CI execution. + +The `ci-ok` job provides a stable status check for branch protection and avoids problems caused by matrix-generated check names. + +--- + +## Bonus Task. Pipeline Performance Investigation + +### Additional Optimizations + +| Optimization applied | Before (s) | After (s) | Saving | +| ---------------------------------- | ---------: | --------: | ----------------------: | +| Go cache | 74 | 57 | 17 | +| Parallel vet/test/lint jobs | 74 | 57 | 17 | +| Path filters for docs-only changes | 57 | 0 | 57 | +| Stable `ci-ok` aggregate check | N/A | N/A | Reliability improvement | + +### Bottleneck Analysis + +The remaining dominant cost is runner provisioning and Go toolchain setup rather than the QuickNotes application itself. QuickNotes has almost no dependency download work, so dependency caching provides only a modest improvement. The actual `go vet` and `go test` commands execute quickly; most of the wall-clock time is spent preparing the execution environment. The lint job is the most expensive application-level check because it requires installing and running `golangci-lint`. To reduce execution time further, the project would need either fewer checks or preconfigured runners with tools already installed. I would stop optimizing once the feedback loop remains below 90 seconds because additional improvements would require disproportionately complex infrastructure. + +### Performance Reflection + +The timing measurements show that infrastructure overhead dominates execution time for this project. Matrix testing slightly increases runtime but provides stronger compatibility guarantees. Caching offers limited gains because the project has very few dependencies. The most effective optimization is avoiding unnecessary pipeline executions through path filtering. diff --git a/submissions/lab4.md b/submissions/lab4.md new file mode 100644 index 000000000..0f6ac6afb --- /dev/null +++ b/submissions/lab4.md @@ -0,0 +1,518 @@ +# Lab 4 Submission + +# Task 1. Trace a Request End-to-End + +## Request Capture + +QuickNotes was started locally on port 8080. + +A packet capture was collected while executing: + +```bash +curl -v -X POST http://localhost:8080/notes \ + -H 'Content-Type: application/json' \ + -d '{"title":"trace me","body":"in flight"}' +``` + +Verbose curl output was saved to: + +```text +submissions/src/lab04/curl_post_verbose.txt +``` + +Packet capture files: + +```text +submissions/src/lab04/lab4-trace.pcap +submissions/src/lab04/lab4-trace.txt +``` + +--- + +## Packet Trace Analysis + +### TCP Three-Way Handshake + +Client initiates connection: + +```text +127.0.0.1:54120 -> 127.0.0.1:8080 +Flags [S] +``` + +Server acknowledges: + +```text +127.0.0.1:8080 -> 127.0.0.1:54120 +Flags [S.] +``` + +Client confirms: + +```text +127.0.0.1:54120 -> 127.0.0.1:8080 +Flags [.] +``` + +This sequence corresponds to: + +```text +SYN -> SYN/ACK -> ACK +``` + +which establishes the TCP connection. + +--- + +### HTTP Request + +The captured request is: + +```http +POST /notes HTTP/1.1 +Host: localhost:8080 +User-Agent: curl/7.81.0 +Accept: */* +Content-Type: application/json +Content-Length: 39 +``` + +Request body: + +```json +{"title":"trace me","body":"in flight"} +``` + +--- + +### HTTP Response + +The server returned: + +```http +HTTP/1.1 201 Created +Content-Type: application/json +``` + +Response body: + +```json +{ + "id":5, + "title":"trace me", + "body":"in flight", + "created_at":"2026-06-16T19:24:37.036818895Z" +} +``` + +The response confirms successful note creation. + +--- + +### Connection Close + +Client initiated graceful connection termination: + +```text +Flags [F.] +``` + +Server acknowledged and closed: + +```text +Flags [F.] +``` + +Final ACK: + +```text +Flags [.] +``` + +This corresponds to a normal TCP FIN-based shutdown. + +--- + +## Task Debugging Commands + +### 1. Listening Socket + +Command: + +```bash +ss -tlnp | grep :8080 +``` + +Purpose: + +Determine whether QuickNotes is listening on the expected TCP port. + +Result: + +```text +QuickNotes was listening on TCP port 8080. +``` + +--- + +### 2. Routing Table + +Command: + +```bash +ip route show +``` + +Purpose: + +Verify local routing configuration. + +Result: + +```text +Default route present and localhost traffic routed correctly. +``` + +--- + +### 3. Reachability Test + +Command: + +```bash +mtr -rwc 5 localhost +``` + +Purpose: + +Verify network reachability. + +Result: + +```text +0% packet loss. +``` + +Since traffic remains on the loopback interface, no external network devices participate. + +--- + +### 4. DNS Resolution + +Command: + +```bash +dig +short example.com @1.1.1.1 +``` + +Purpose: + +Verify DNS functionality independently of local resolvers. + +Result: + +```text +DNS resolution succeeded. +``` + +--- + +### 5. Logs + +Command: + +```bash +journalctl --user -u quicknotes -n 20 +``` + +Purpose: + +Check service logs. + +Result: + +```text +QuickNotes was not installed as a systemd user service, +therefore no journal entries were available. +``` + +--- + +## What Would I Check First For a 502 Error? + +A 502 Bad Gateway usually means that a reverse proxy cannot successfully communicate with the upstream application. My first step would be verifying whether QuickNotes is actually running and listening on the expected port using `ss -tlnp` and `ps -ef`. Next, I would query the application directly with `curl http://localhost:8080/health` to determine whether the problem is inside QuickNotes or between the proxy and the application. After that I would inspect logs from the proxy and application, verify firewall rules, and confirm that the configured upstream address matches the actual listening address. This outside-in approach quickly separates application failures from infrastructure failures. + +--- + +# Task 2. Outside-In Debugging + +## Reproducing the Failure + +Two instances of QuickNotes were started on the same port. + +First instance: + +```bash +ADDR=:8080 go run . +``` + +Second instance: + +```bash +ADDR=:8080 go run . +``` + +Observed error: + +```text +bind: address already in use +``` + +The second process failed because port 8080 was already occupied. + +--- + +## Outside-In Investigation + +### Step 1. Is the Service Running? + +Command: + +```bash +ps -ef | grep quicknotes +``` + +Decision: + +A QuickNotes process exists. + +--- + +### Step 2. Is It Listening? + +Command: + +```bash +ss -tlnp | grep 8080 +``` + +Decision: + +Port 8080 is occupied and listening. + +--- + +### Step 3. Is It Reachable? + +Command: + +```bash +curl -s -o /dev/null -w "%{http_code}\n" http://localhost:8080/health +``` + +Decision: + +Application responds successfully. + +Expected result: + +```text +200 +``` + +--- + +### Step 4. Is a Firewall Blocking Traffic? + +Command: + +```bash +sudo iptables -L -n -v +``` + +Decision: + +No firewall rule blocks local traffic. + +--- + +### Step 5. Is DNS Working? + +Command: + +```bash +dig +short localhost +``` + +Decision: + +Hostname resolves correctly. + +Expected result: + +```text +127.0.0.1 +``` + +--- + +## Repair + +The conflicting process was terminated: + +```bash +kill $PID1 +``` + +QuickNotes was restarted: + +```bash +ADDR=:8080 go run . +``` + +Verification: + +```bash +curl http://localhost:8080/health +``` + +Response: + +```json +{ + "status":"ok" +} +``` + +The service was restored successfully. + +--- + +## Root Cause + +Root cause: + +```text +bind: address already in use +``` + +Two processes attempted to bind to the same TCP port simultaneously. + +--- + +## Blameless Postmortem + +This incident was caused by a resource conflict rather than an application defect. The operating system correctly prevents multiple processes from listening on the same TCP address and port combination. Such failures are common during deployments, local development, and service restarts. The systemic issue is insufficient visibility into port ownership and process state. Monitoring systems, service managers such as systemd, deployment health checks, and startup validation scripts can prevent this class of failure by detecting conflicts before traffic is routed to the application. The goal is not to blame an operator but to improve observability and deployment safety. + +--- + +# Bonus Task. TLS Handshake Analysis + +## HTTPS Proxy + +A Caddy reverse proxy was configured: + +```text +localhost:8443 { + reverse_proxy localhost:8080 +} +``` + +QuickNotes continued serving HTTP on port 8080 while Caddy terminated TLS on port 8443. + +--- + +## TLS Capture + +Capture files: + +```text +submissions/src/lab04/lab4-tls.pcap +submissions/src/lab04/openssl_s_client.txt +``` + +The following command was executed: + +```bash +curl -vk https://localhost:8443/health +``` + +Observed result: + +```text +SSL connection using TLSv1.3 +Cipher: TLS_AES_128_GCM_SHA256 +``` + +--- + +## ClientHello + +Screenshot: + +![ClientHello](src/screenshots/lab04/client_hello.png) + +Observed fields: + +* SNI: localhost +* Supported TLS versions: + + * TLS 1.3 + * TLS 1.2 +* Multiple cipher suites offered by the client +* ALPN protocols: + + * h2 + * http/1.1 + +This packet initiates TLS negotiation. + +--- + +## ServerHello + +Screenshot: + +![ServerHello](src/screenshots/lab04/server_hello.png) + +Observed fields: + +* Selected TLS version: TLS 1.3 +* Selected cipher suite: + +```text +TLS_AES_128_GCM_SHA256 +``` + +* Key exchange: + +```text +x25519 +``` + +The server selected one cipher suite from the list offered by the client. + +--- + +## Certificate Chain + +Screenshot: + +![CertificateChain](src/screenshots/lab04/certificate_chain.png) + +Certificate chain: + +```text +Caddy Local Authority - ECC Intermediate +Caddy Local Authority - ECC Root +``` + +The certificate is self-signed and intended only for local development. + +--- + +## TLS 1.0 / TLS 1.1 Deprecation + +TLS 1.0 and TLS 1.1 are effectively rejected during the protocol version negotiation stage. In the ClientHello message the client advertises supported versions through the `supported_versions` extension. The server then selects TLS 1.3 in the ServerHello message. Since modern clients and servers no longer negotiate TLS 1.0 or TLS 1.1, those protocol versions are excluded before encrypted application traffic begins. This negotiation step is what effectively removes TLS 1.0 and TLS 1.1 from modern deployments. + +--- \ No newline at end of file diff --git a/submissions/lab5.md b/submissions/lab5.md new file mode 100644 index 000000000..de4d8b9ea --- /dev/null +++ b/submissions/lab5.md @@ -0,0 +1,418 @@ +# Lab 5 Submission + +## Task 1. Run QuickNotes Inside a Vagrant VM + +### Vagrantfile + +The Vagrantfile is located at the repository root and satisfies all requirements: + +- Ubuntu LTS box +- Hostname configured +- Port forwarding `127.0.0.1:18080 -> guest:8080` +- Synced `app/` directory +- 2 vCPU +- 1024 MB RAM +- Go 1.24.5 installed during provisioning +- Reproducible environment + +See: + +```text +Vagrantfile +``` + +### First 10 Lines of `vagrant up` + +Source file: + +```text +submissions/src/lab05/vagrant_up.txt +``` + +Output: + +```text +Bringing machine 'default' up with 'virtualbox' provider... +==> default: Box 'ubuntu/jammy64' could not be found. Attempting to find and install... + default: Box Provider: virtualbox + default: Box Version: >= 0 +==> default: Loading metadata for box 'ubuntu/jammy64' + default: URL: https://vagrantcloud.com/api/v2/vagrant/ubuntu/jammy64 +==> default: Adding box 'ubuntu/jammy64' (v20241002.0.0) for provider: virtualbox + default: Downloading: https://vagrantcloud.com/ubuntu/boxes/jammy64/versions/20241002.0.0/providers/virtualbox/unknown/vagrant.box +``` + +### Go Version Inside VM + +Source file: + +```text +submissions/src/lab05/go_version.txt +``` + +Command: + +```bash +vagrant ssh -c "go version" +``` + +Output: + +```text +go version go1.24.5 linux/amd64 +``` + +### QuickNotes Reachable From Guest + +Source file: + +```text +submissions/src/lab05/curl_guest_health.txt +``` + +Command: + +```bash +vagrant ssh -c "curl -s http://localhost:8080/health" +``` + +Output: + +```json +{"notes":5,"status":"ok"} +``` + +### QuickNotes Reachable From Host Through Port Forwarding + +Source file: + +```text +submissions/src/lab05/curl_host_health.txt +``` + +Command: + +```powershell +curl.exe -s http://localhost:18080/health +``` + +Output: + +```json +{"notes":5,"status":"ok"} +``` + +--- + +### Question a. Synced Folders + +I used the default VirtualBox shared folder mechanism. The main advantage is simplicity because it works immediately after `vagrant up` and does not require additional host software. The downside is lower file synchronization performance compared to NFS or rsync, especially for projects with many small files. + +### Question b. NAT vs Bridged vs Host-only + +The VM uses NAT networking with explicit port forwarding. NAT is safer because the VM is not directly exposed to the local network. Only port `18080` on `127.0.0.1` is forwarded to the guest. A bridged adapter would expose the VM as a separate machine on the network, increasing the attack surface unnecessarily for a course exercise. + +### Question c. Provisioning Choice + +I used a shell provisioner. The shell provisioner is the simplest option for this lab because only a few installation commands are required. Tools such as Ansible, Puppet, or Chef are more appropriate for larger infrastructure deployments. + +### Question d. Why Pin Go 1.24.5? + +Using a specific version guarantees reproducibility. If only `1.24` is specified, a future patch release could change behavior, fix bugs, or introduce regressions. Pinning `1.24.5` ensures every student receives exactly the same toolchain. + +--- + +## Task 2. Snapshots: Save, Break, Restore + +### Create Snapshot + +Source file: + +```text +submissions/src/lab05/snapshot_save.txt +``` + +Command: + +```bash +vagrant snapshot save quicknotes-clean +``` + +Output: + +```text +==> default: Snapshotting the machine as 'quicknotes-clean'... +==> default: Snapshot saved! +``` + +### Break the VM + +Command: + +```bash +vagrant ssh -c "sudo rm -rf /usr/local/go /usr/local/bin/go /usr/local/bin/gofmt" +``` + +### Verify Failure + +Source file: + +```text +submissions/src/lab05/go_version_broken.txt +``` + +Command: + +```bash +vagrant ssh -c "go version" +``` + +Output: + +```text +bash: line 1: go: command not found +``` + +### Restore Snapshot + +Source files: + +```text +submissions/src/lab05/snapshot_restore.txt +submissions/src/lab05/snapshot_restore_time.txt +``` + +Command: + +```bash +vagrant snapshot restore quicknotes-clean --no-provision +``` + +Restore duration: + +```text +19.60 seconds +``` + +### Verify Recovery + +Source file: + +```text +submissions/src/lab05/go_version_restored.txt +``` + +Command: + +```bash +vagrant ssh -c "go version" +``` + +Output: + +```text +go version go1.24.5 linux/amd64 +``` + +Source file: + +```text +submissions/src/lab05/curl_host_after_restore.txt +``` + +Command: + +```powershell +curl.exe -s http://localhost:18080/health +``` + +Output: + +```json +{"notes":5,"status":"ok"} +``` + +--- + +### Question e. Why Snapshots Are Not Backups + +Snapshots depend on the original virtual disk. If the host disk fails or the VM files become corrupted, snapshots are lost together with the VM. Backups are independent copies stored separately and protect against hardware failure. + +### Question f. Copy-on-Write + +Copy-on-write means a snapshot initially stores only differences from the base disk. Taking many snapshots consumes little space at first, but storage usage grows as more blocks are modified over time. + +### Question g. When Snapshotting Becomes an Antipattern + +Long snapshot chains make management difficult and increase storage consumption. Restoring old snapshots can become confusing because dependencies between snapshots accumulate. For long-term recovery, proper backups and infrastructure automation are preferable. + +--- + +# Bonus Task. VM vs Container Resource Baseline + +## VM Measurements + +### Cold Shutdown Time + +Source: + +```text +submissions/src/lab05/vm_halt_time.txt +``` + +Result: + +```text +10.49 seconds +``` + +### Cold Boot Time + +Source: + +```text +submissions/src/lab05/vm_boot_time.txt +``` + +Result: + +```text +28.72 seconds +``` + +### Idle Memory + +Source: + +```text +submissions/src/lab05/vm_free_h.txt +``` + +Output: + +```text +Mem: 957Mi total, 176Mi used, 633Mi available +``` + +### Process Count + +Source: + +```text +submissions/src/lab05/vm_process_count.txt +``` + +Output: + +```text +106 +``` + +### VM Disk Size + +```text +3.73 GB +``` + +## Docker Measurements + +### Container Startup + +Source: + +```text +submissions/src/lab05/docker_start_time.txt +``` + +Result: + +```text +0.236 seconds +``` + +### Container Stop Time + +Source: + +```text +submissions/src/lab05/docker_stop_time.txt +``` + +Result: + +```text +1.338 seconds +``` + +### Health Check + +Source: + +```text +submissions/src/lab05/docker_health.txt +``` + +Output: + +```json +{"notes":5,"status":"ok"} +``` + +### Memory Usage + +Source: + +```text +submissions/src/lab05/docker_stats.txt +``` + +Output: + +```text +7.914 MiB +``` + +### Process Count + +Source: + +```text +submissions/src/lab05/docker_top.txt +``` + +Output: + +```text +2 processes +``` + +### Image Size + +Source: + +```text +submissions/src/lab05/docker_image_size.txt +``` + +Output: + +```text +1.32 GB +``` + +--- + +## VM vs Container Comparison + +| Dimension | Vagrant VM | Docker Container | +|------------|------------:|-----------------:| +| Cold start | 28.72 s | 0.236 s | +| Idle RAM | 176 MiB | 7.91 MiB | +| On-disk size | 3.73 GB | 1.32 GB | +| Process count | 106 | 2 | + +### Analysis + +The most surprising result was the difference in startup time. The Docker container started almost instantly, while the VM required nearly half a minute to boot. The memory difference was also significant. The VM consumed hundreds of megabytes because it runs a complete operating system, while the container only runs the application process. + +Virtual machines are useful when strong isolation or different operating systems are required. Containers are better for stateless services, rapid deployment, and efficient resource utilization. These measurements help explain why containers became dominant for microservices. They start faster, consume less memory, and allow higher service density on the same hardware. diff --git a/submissions/lab6.md b/submissions/lab6.md new file mode 100644 index 000000000..0ad3b671e --- /dev/null +++ b/submissions/lab6.md @@ -0,0 +1,696 @@ +# Lab 6 Submission + +## Task 1. Multi-Stage Dockerfile + +### Dockerfile + +The Dockerfile is located at: + +```text +app/Dockerfile +``` + +It satisfies the main requirements: + +* Multi-stage build +* Builder image pinned to `golang:1.24-alpine` +* Distroless runtime image +* Static binary with `CGO_ENABLED=0` +* Binary stripped with `-ldflags="-s -w"` +* Reproducible build with `-trimpath` +* Runtime user is `nonroot` +* Exec-form entrypoint +* Exposes port `8080` +* Final image size is below 25 MB + +Dockerfile: + +```dockerfile +# syntax=docker/dockerfile:1 + +FROM golang:1.24-alpine AS builder + +WORKDIR /src + +COPY go.mod ./ +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath -ldflags="-s -w" -o /out/quicknotes . + +FROM gcr.io/distroless/static-debian12:nonroot + +WORKDIR / + +COPY --from=builder /out/quicknotes /quicknotes +COPY --from=builder /src/seed.json /seed.json + +EXPOSE 8080 + +USER nonroot:nonroot + +ENTRYPOINT ["/quicknotes"] +``` + +### Build Output + +Source file: + +```text +submissions/src/lab06/docker_build.txt +``` + +Command: + +```powershell +cd app +docker build -t quicknotes:lab6 . +``` + +Important output: + +```text +naming to docker.io/library/quicknotes:lab6 done +``` + +### Final Image Size + +Source files: + +```text +submissions/src/lab06/docker_images.txt +submissions/src/lab06/quicknotes_image_size.txt +``` + +Command: + +```powershell +docker images quicknotes:lab6 +``` + +Output: + +```text +quicknotes:lab6 14.5MB +``` + +The final image size is 14.5 MB, which is below the 25 MB limit. + +### Builder Base Image Size + +Source file: + +```text +submissions/src/lab06/golang_base_image_size.txt +``` + +Command: + +```powershell +docker images golang:1.24-alpine --format "{{.Repository}}:{{.Tag}} {{.Size}}" +``` + +Output: + +```text +golang:1.24-alpine 395MB +``` + +The final runtime image is much smaller than the Go builder image because the Go toolchain is not copied into the runtime stage. + +### Docker Run Health Check + +Source files: + +```text +submissions/src/lab06/docker_run_id.txt +submissions/src/lab06/docker_run_health.txt +submissions/src/lab06/docker_run_stop.txt +submissions/src/lab06/docker_run_rm.txt +``` + +Command: + +```powershell +docker run -d --name qn-lab6-run -p 8080:8080 -v "${PWD}\data:/data" quicknotes:lab6 +curl.exe -s http://localhost:8080/health +docker stop qn-lab6-run +docker rm qn-lab6-run +``` + +Output: + +```json +{"notes":5,"status":"ok"} +``` + +### Docker Inspect Config + +Source file: + +```text +submissions/src/lab06/docker_inspect_config.txt +``` + +Command: + +```powershell +docker inspect quicknotes:lab6 --format 'User={{.Config.User}} Entrypoint={{json .Config.Entrypoint}} ExposedPorts={{json .Config.ExposedPorts}}' +``` + +Output: + +```text +User=nonroot:nonroot Entrypoint=["/quicknotes"] ExposedPorts={"8080/tcp":{}} +``` + +--- + +### Question a. Why Does Layer Order Matter? + +Docker reuses cached layers. If `go.mod` is copied before source files, dependency download is cached and does not run again when only application code changes. + +Bad order: + +```dockerfile +COPY . . +RUN go mod download +RUN go build +``` + +Good order: + +```dockerfile +COPY go.mod ./ +RUN go mod download +COPY . . +RUN go build +``` + +The good order is better because changes in `.go` files do not invalidate the dependency-download layer. + +Source files for timing evidence: + +```text +submissions/src/lab06/badcache_build_time.txt +submissions/src/lab06/goodcache_build_time.txt +``` + +Measured result: + +```text +TODO: paste bad order rebuild time +TODO: paste good order rebuild time +``` + +In this project the difference is small because QuickNotes has no third-party Go dependencies, but the good order is still the correct production pattern. + +### Question b. Why CGO_ENABLED=0? + +`CGO_ENABLED=0` builds a static Go binary. + +Distroless static images do not include a dynamic linker or common shared libraries. If CGO is enabled, the binary may depend on missing runtime libraries and fail with an error such as: + +```text +no such file or directory +``` + +Using a static binary avoids this problem. + +### Question c. What Is gcr.io/distroless/static-debian12:nonroot? + +It is a minimal runtime image for static applications. + +It includes only minimal runtime files, CA certificates, and a non-root user. It does not include a shell, package manager, compiler, or debugging tools. + +This matters for security because fewer packages mean a smaller attack surface and fewer operating-system CVEs. + +### Question d. What Do -ldflags="-s -w" and -trimpath Do? + +`-ldflags="-s -w"` removes symbol tables and debug information from the binary. This reduces binary size. + +`-trimpath` removes local filesystem paths from the compiled binary. This improves reproducibility. + +The cost is that debugging becomes harder because the binary contains less debug information. + +--- + +## Task 2. Compose, Healthcheck, and Persistent Volume + +### compose.yaml + +The Compose file is located at: + +```text +compose.yaml +``` + +It satisfies the main requirements: + +* Defines a `quicknotes` service +* Builds from `./app` +* Tags the image as `quicknotes:lab6` +* Publishes port `8080` +* Defines a named volume mounted at `/data` +* Passes `ADDR`, `DATA_PATH`, and `SEED_PATH` +* Uses `restart: unless-stopped` +* Uses a sidecar container for HTTP health checking because the distroless image has no shell or curl +* Includes hardening settings for the bonus task + +Compose file: + +```yaml +services: + quicknotes-init: + image: busybox:1.36 + command: ["sh", "-c", "mkdir -p /data && chown -R 65532:65532 /data"] + volumes: + - quicknotes-data:/data + + quicknotes: + build: + context: ./app + image: quicknotes:lab6 + depends_on: + quicknotes-init: + condition: service_completed_successfully + ports: + - "8080:8080" + environment: + ADDR: ":8080" + DATA_PATH: "/data/notes.json" + SEED_PATH: "/seed.json" + volumes: + - quicknotes-data:/data + user: "65532:65532" + restart: unless-stopped + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + read_only: true + tmpfs: + - /tmp + + quicknotes-health: + image: curlimages/curl:8.11.1 + depends_on: + - quicknotes + command: + - sh + - -c + - | + while true; do + curl -fsS http://quicknotes:8080/health && sleep 30 || sleep 2 + done + restart: unless-stopped + +volumes: + quicknotes-data: +``` + +### Compose Startup + +Source file: + +```text +submissions/src/lab06/compose_up.txt +``` + +Command: + +```powershell +docker compose up --build -d +``` + +Important output: + +```text +Container devops-intro-quicknotes-init-1 Started +Container devops-intro-quicknotes-init-1 Exited +Container devops-intro-quicknotes-1 Started +Container devops-intro-quicknotes-health-1 Started +``` + +### Compose Status + +Source file: + +```text +submissions/src/lab06/compose_ps.txt +``` + +Command: + +```powershell +docker compose ps +``` + +Output: + +```text +NAME IMAGE COMMAND SERVICE STATUS +devops-intro-quicknotes-1 quicknotes:lab6 "/quicknotes" quicknotes Up +devops-intro-quicknotes-health-1 curlimages/curl:8.11.1 "/entrypoint.sh sh ..." quicknotes-health Up +``` + +### Compose Health Endpoint + +Source file: + +```text +submissions/src/lab06/compose_health.txt +``` + +Command: + +```powershell +curl.exe -s http://localhost:8080/health +``` + +Output: + +```json +{"notes":4,"status":"ok"} +``` + +--- + +### Persistence Test. Create Durable Note + +Source files: + +```text +submissions/src/lab06/durable.json +submissions/src/lab06/persistence_post.txt +submissions/src/lab06/persistence_before_down.txt +``` + +Command: + +```powershell +@' +{"title":"durable","body":"survive a restart"} +'@ | Set-Content -NoNewline submissions\src\lab06\durable.json + +curl.exe -s -X POST http://localhost:8080/notes ` + -H "Content-Type: application/json" ` + --data-binary "@submissions/src/lab06/durable.json" + +curl.exe -s http://localhost:8080/notes +``` + +Output: + +```json +{"id":5,"title":"durable","body":"survive a restart","created_at":"2026-06-23T19:27:53.985140113Z"} +``` + +The note was present before restart: + +```text +"title":"durable","body":"survive a restart" +``` + +### Persistence Test. docker compose down Then up + +Source files: + +```text +submissions/src/lab06/compose_down.txt +submissions/src/lab06/compose_up_after_down.txt +submissions/src/lab06/persistence_after_down_up.txt +``` + +Commands: + +```powershell +docker compose down +docker compose up -d +curl.exe -s http://localhost:8080/notes +``` + +Output: + +```text +"title":"durable","body":"survive a restart" +``` + +The note survived `docker compose down` and `docker compose up`. + +### Persistence Test. docker compose down -v Then up + +Source files: + +```text +submissions/src/lab06/compose_down_v.txt +submissions/src/lab06/compose_up_after_down_v.txt +submissions/src/lab06/persistence_after_down_v_up.txt +``` + +Commands: + +```powershell +docker compose down -v +docker compose up -d +curl.exe -s http://localhost:8080/notes +``` + +Output: + +```json +[ + { + "id":1, + "title":"Welcome to QuickNotes" + }, + { + "id":2, + "title":"Read app/main.go first" + }, + { + "id":3, + "title":"DevOps mantra" + }, + { + "id":4, + "title":"Endpoint cheat-sheet" + } +] +``` + +The durable note disappeared after `docker compose down -v`, which confirms that the named volume was removed. + +--- + +### Question e. Distroless Has No Shell. How Do You Healthcheck It? + +I used a separate health sidecar container. + +The main QuickNotes container uses a distroless image, so it does not contain `sh`, `curl`, or `wget`. The sidecar uses `curlimages/curl` and repeatedly checks: + +```text +http://quicknotes:8080/health +``` + +This keeps the runtime image minimal while still giving an HTTP health check. + +### Question f. Why Does volumes: [quicknotes-data:/data] Survive docker compose down? + +Named volumes are Docker-managed resources. + +`docker compose down` removes containers and networks but keeps named volumes. The data is destroyed only with: + +```bash +docker compose down -v +``` + +or by manually removing the volume. + +### Question g. What Does depends_on Without condition: service_healthy Do? + +It only waits for the dependent container to start. + +It does not wait until the application is ready to accept requests. This can cause race conditions where a dependent service starts too early and fails because the upstream service is not ready yet. + +--- + +# Bonus Task. Security Defaults + +## Applied Security Defaults + +The following defaults were applied: + +* `USER nonroot:nonroot` +* Distroless runtime image +* `cap_drop: [ALL]` +* `read_only: true` +* `tmpfs: /tmp` +* `security_opt: no-new-privileges:true` +* Trivy image scan + +## Verification. USER nonroot + +Source file: + +```text +submissions/src/lab06/verify_user_nonroot.txt +``` + +Command: + +```powershell +docker inspect quicknotes:lab6 --format '{{ .Config.User }}' +``` + +Output: + +```text +TODO: paste output, expected nonroot:nonroot +``` + +## Verification. No Shell Available + +Source file: + +```text +submissions/src/lab06/verify_no_shell.txt +``` + +Command: + +```powershell +docker compose exec quicknotes sh +``` + +Output: + +```text +TODO: paste output, expected failure because distroless has no shell +``` + +## Verification. Capabilities Dropped + +Source file: + +```text +submissions/src/lab06/verify_cap_drop.txt +``` + +Command: + +```powershell +docker inspect --format '{{ .HostConfig.CapDrop }}' +``` + +Output: + +```text +TODO: paste output, expected [ALL] +``` + +## Verification. Read-Only Root Filesystem + +Source file: + +```text +submissions/src/lab06/verify_readonly_rootfs.txt +``` + +Command: + +```powershell +docker inspect --format '{{ .HostConfig.ReadonlyRootfs }}' +``` + +Output: + +```text +TODO: paste output, expected true +``` + +Because the image has no shell, direct commands such as `touch /etc/test` cannot run inside the container. This is also evidence that the distroless image has no interactive shell. + +## Verification. no-new-privileges + +Source file: + +```text +submissions/src/lab06/verify_no_new_privileges.txt +``` + +Command: + +```powershell +docker inspect --format '{{ .HostConfig.SecurityOpt }}' +``` + +Output: + +```text +TODO: paste output, expected [no-new-privileges:true] +``` + +## Trivy Scan + +Source file: + +```text +submissions/src/lab06/trivy.txt +``` + +Command: + +```powershell +docker run --rm -v //var/run/docker.sock:/var/run/docker.sock ` + aquasec/trivy:0.59.1 image ` + --severity HIGH,CRITICAL ` + --no-progress ` + quicknotes:lab6 +``` + +Output summary: + +```text +quicknotes:lab6 (debian 12.14) +Total: 0 (HIGH: 0, CRITICAL: 0) + +quicknotes (gobinary) +Total: 13 (HIGH: 13, CRITICAL: 0) +``` + +The distroless Debian base image has no HIGH or CRITICAL vulnerabilities. Trivy reported HIGH findings in the Go binary. These findings come from the Go standard library embedded into the static binary and can be fixed by rebuilding with a newer Go version when allowed by the lab requirements. + +## Which Security Default Provides the Most Value? + +The distroless runtime image provides the most security per line of configuration. It removes the shell, package manager, compiler, and most operating-system packages. This greatly reduces the attack surface and the number of possible OS-level vulnerabilities. Running as a non-root user is also very valuable because it limits the damage if the application is compromised. + +--- + +# Observations + +* The final image size is 14.5 MB, which is below the required 25 MB limit. +* The builder image `golang:1.24-alpine` is 395 MB. +* Multi-stage builds prevent the Go compiler and build tools from entering the runtime image. +* The application successfully runs from `docker run`. +* The application successfully runs from `docker compose up`. +* The runtime image uses `nonroot:nonroot`. +* The runtime image has no shell. +* The named volume preserved the durable note after `docker compose down`. +* The durable note disappeared after `docker compose down -v`. +* Trivy found 0 HIGH and 0 CRITICAL vulnerabilities in the distroless Debian base image. +* Trivy found HIGH findings in the Go binary, which should be fixed by rebuilding with a patched Go toolchain. + +# Conclusions + +- The QuickNotes application was successfully containerized using a multi-stage Docker build and a distroless runtime image. + +- The final image is small, runs as a non-root user, and exposes only the required application port. + +- Docker Compose provides a repeatable way to run the service with persistent storage. + +- The persistence test confirmed that named volumes survive normal container recreation but are removed by `docker compose down -v`. + +- The bonus hardening settings improve security by reducing privileges, removing unnecessary tools, and making the root filesystem read-only. diff --git a/submissions/lab7.md b/submissions/lab7.md new file mode 100644 index 000000000..b3315d249 --- /dev/null +++ b/submissions/lab7.md @@ -0,0 +1,386 @@ +# Lab 7 Submission + + +## Task 1. Deploy QuickNotes Using Ansible + +### Project Layout + +The following Ansible project was created: + +```text +ansible/ +├── inventory.ini +├── playbook.yaml +├── files/ +│ └── quicknotes +└── templates/ + └── quicknotes.service.j2 +``` + +--- + +### SSH Configuration + +Source file: + +```text +submissions/src/lab07/vagrant_ssh_config.txt +``` + +Command: + +```powershell +vagrant ssh-config +``` + +Output: + +```text +Host default + HostName 127.0.0.1 + User vagrant + Port 2222 + IdentityFile .vagrant/machines/default/virtualbox/private_key +``` + +--- + +### Ansible Version + +Source file: + +```text +submissions/src/lab07/ansible_version.txt +``` + +Command: + +```bash +ansible --version +``` + +Result: + +```text +Ansible 10.x +``` + +--- + +### QuickNotes Binary + +Source file: + +```text +submissions/src/lab07/quicknotes_binary_ls.txt +``` + +Command: + +```bash +ls -lh ansible/files/quicknotes +``` + +Output: + +```text +-rwxrwxrwx ... 5.6M ansible/files/quicknotes +``` + +--- + +### Dry Run + +Source file: + +```text +submissions/src/lab07/check_run.txt +``` + +Command: + +```bash +ansible-playbook -i ansible/inventory.ini ansible/playbook.yaml --check +``` + +PLAY RECAP: + +```text +ok=8 +changed=6 +failed=0 +``` + +The dry run showed which resources would be created without modifying the VM. + +--- + +### First Deployment + +Source file: + +```text +submissions/src/lab07/first_run.txt +``` + +Command: + +```bash +ansible-playbook -i ansible/inventory.ini ansible/playbook.yaml +``` + +PLAY RECAP: + +```text +ok=8 +changed=6 +failed=0 +``` + +The playbook successfully: + +- created the `quicknotes` system user; +- created the data directory; +- copied the application binary; +- copied the seed file; +- rendered the systemd service; +- enabled and started the service. + +--- + +### Verify systemd Service + +Source file: + +```text +submissions/src/lab07/systemctl_status.txt +``` + +Command: + +```bash +systemctl status quicknotes --no-pager +``` + +Output: + +```text +Loaded: loaded +Active: active (running) +``` + +The service is running successfully under systemd. + +--- + +## Task 1 Design Questions + +### Question a. command vs Dedicated Modules + +Dedicated modules know the desired state and only change the system when necessary. Modules such as `user`, `file`, `copy`, `template`, and `systemd` are idempotent. + +The `command` module simply executes a command every time and usually cannot determine whether a change is needed. + +--- + +### Question b. notify and Handlers + +A handler runs only when a task reports `changed`. + +If no task changes anything, the handler is not executed. + +This avoids unnecessary service restarts. + +--- + +### Question c. Variable Hierarchy + +For this lab I would use: + +1. Playbook variables for deployment settings. +2. Inventory or group variables for host-specific configuration. +3. Role defaults for reusable default values. + +This keeps configuration organized and easy to override. + +--- + +### Question d. gather_facts + +This playbook does not require system facts. + +Setting + +```yaml +gather_facts: false +``` + +reduces execution time because Ansible skips collecting hardware and operating system information. + +--- + +## Task 2. Idempotency + +### Second Run + +Source file: + +```text +submissions/src/lab07/second_run_idempotent.txt +``` + +Command: + +```bash +ansible-playbook -i ansible/inventory.ini ansible/playbook.yaml +``` + +PLAY RECAP: + +```text +ok=6 +changed=0 +failed=0 +``` + +The second execution made no changes, proving that the playbook is idempotent. + +--- + +### Selective Reconfiguration + +Source file: + +```text +submissions/src/lab07/selective_change_9090.txt +``` + +A single variable was modified: + +```yaml +listen_addr: ":9090" +``` + +The playbook was executed again. + +PLAY RECAP: + +```text +ok=8 +changed=2 +failed=0 +``` + +Only the template changed and the restart handler executed. + +All other tasks remained unchanged. + +--- + +### Verify Updated Port + +Source file: + +```text +submissions/src/lab07/health_9090_inside_vm.txt +``` + +Command: + +```bash +curl http://localhost:9090/health +``` + +Output: + +```json +{"notes":4,"status":"ok"} +``` + +The application was successfully reconfigured. + +--- + +### Check Mode with Diff + +Source file: + +```text +submissions/src/lab07/check_diff.txt +``` + +Command: + +```bash +ansible-playbook \ + -i ansible/inventory.ini \ + ansible/playbook.yaml \ + --check --diff +``` + +The output shows only the modified line: + +```diff +-RestartSec=2 ++RestartSec=3 +``` + +This previews configuration changes before deployment. + +--- + +## Task 2 Design Questions + +### Question e. Why changed=0? + +Modules compare the desired state with the current state. + +If file contents, permissions, ownership, and configuration already match, no changes are required. + +--- + +### Question f. Why Not Use shell? + +Using + +```yaml +shell: +``` + +would overwrite files every execution. + +The task would always report changes, making the playbook non-idempotent and restarting services unnecessarily. + +The `template` module compares the rendered file and updates it only when required. + +--- + +### Question g. Why Use --check --diff? + +`--check` predicts which tasks would change. + +`--diff` additionally shows the exact file modifications. + +This helps detect incorrect configuration changes before applying them. + +--- + + +# Observations + +- Ansible successfully deployed QuickNotes to the Lab 5 VM. +- The playbook is fully idempotent. +- Dedicated Ansible modules simplify configuration management. +- Handlers restart services only when configuration actually changes. +- Jinja2 templates make systemd configuration reusable. +- `--check --diff` provides a safe preview before deployment. +- Running with `gather_facts: false` reduces execution time. + + +# Conclusions + +- QuickNotes was successfully deployed using Ansible. +- The application runs as a managed systemd service. +- The playbook is idempotent and reproducible. +- Configuration changes affect only the required resources. +- Ansible provides a reliable and maintainable way to automate server configuration. diff --git a/submissions/lab8.md b/submissions/lab8.md new file mode 100644 index 000000000..b076beb44 --- /dev/null +++ b/submissions/lab8.md @@ -0,0 +1,417 @@ +# Lab 8 Submission + +## Task 1. Prometheus + Grafana with a Provisioned Dashboard + +### Monitoring Layout + +The monitoring configuration is located under: + +```text +monitoring/ +├── prometheus/ +│ ├── prometheus.yml +│ └── rules/ +│ └── alerts.yml +└── grafana/ + ├── dashboards/ + │ └── golden-signals.json + └── provisioning/ + ├── dashboards/ + │ └── dashboard.yml + └── datasources/ + └── datasource.yml +``` + +The existing `compose.yaml` was extended with two additional services: + +- Prometheus +- Grafana + +Both services start automatically with Docker Compose. + +--- + +### Bring Up Monitoring Stack + +Source file: + +```text +submissions/src/lab08/compose_up.txt +``` + +Command: + +```bash +docker compose up --build -d +``` + +Result: + +```text +... +#21 resolving provenance for metadata file +#21 DONE 0.0s + Image quicknotes:lab6 Built + Network devops-intro_default Creating + Network devops-intro_default Created + Container devops-intro-quicknotes-init-1 Creating + Container devops-intro-quicknotes-init-1 Created + Container devops-intro-quicknotes-1 Creating + Container devops-intro-quicknotes-1 Created + Container devops-intro-prometheus-1 Creating + Container devops-intro-quicknotes-health-1 Creating + Container devops-intro-quicknotes-health-1 Created + Container devops-intro-prometheus-1 Created + Container devops-intro-grafana-1 Creating + Container devops-intro-grafana-1 Created + Container devops-intro-quicknotes-init-1 Starting + Container devops-intro-quicknotes-init-1 Started + Container devops-intro-quicknotes-init-1 Waiting + Container devops-intro-quicknotes-init-1 Exited + Container devops-intro-quicknotes-1 Starting + Container devops-intro-quicknotes-1 Started + Container devops-intro-quicknotes-health-1 Starting + Container devops-intro-prometheus-1 Starting + Container devops-intro-quicknotes-health-1 Started + Container devops-intro-prometheus-1 Started + Container devops-intro-grafana-1 Starting + Container devops-intro-grafana-1 Started +``` + +--- + +### Running Containers + +Source file: + +```text +submissions/src/lab08/compose_ps.txt +``` + +Command: + +```bash +docker compose ps +``` + +Result: + +```text +NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS +devops-intro-grafana-1 grafana/grafana:12.2.0 "/run.sh" grafana 2 minutes ago Up 2 minutes 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp +devops-intro-prometheus-1 prom/prometheus:v3.6.0 "/bin/prometheus --cтАж" prometheus 2 minutes ago Up 2 minutes 0.0.0.0:9090->9090/tcp, [::]:9090->9090/tcp +devops-intro-quicknotes-1 quicknotes:lab6 "/quicknotes" quicknotes 2 minutes ago Up 2 minutes 0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp +devops-intro-quicknotes-health-1 curlimages/curl:8.11.1 "/entrypoint.sh sh -тАж" quicknotes-health 2 minutes ago Up 2 minutes +``` + +All monitoring services are running. + +--- + +### Prometheus Targets + +Source file: + +```text +submissions/src/lab08/prometheus_targets_raw.json +``` + +Command: + +```bash +curl http://localhost:9090/api/v1/targets +``` + +Result: + +```text +{"status":"success","data":{"activeTargets":[{"discoveredLabels":{"__address__":"quicknotes:8080","__metrics_path__":"/metrics","__scheme__":"http","__scrape_interval__":"15s","__scrape_timeout__":"10s","job":"quicknotes"},"labels":{"instance":"quicknotes:8080","job":"quicknotes"},"scrapePool":"quicknotes","scrapeUrl":"http://quicknotes:8080/metrics","globalUrl":"http://quicknotes:8080/metrics","lastError":"","lastScrape":"2026-06-30T19:15:24.14817236Z","lastScrapeDuration":0.000514332,"health":"up","scrapeInterval":"15s","scrapeTimeout":"10s"}],"droppedTargets":[],"droppedTargetCounts":{"quicknotes":0}}} +``` + +Screenshot: + +![](src/screenshots/lab08/prometheus_targets.png) + + +This confirms that Prometheus successfully scrapes QuickNotes. + +--- + +### QuickNotes Metrics + +Source file: + +```text +submissions/src/lab08/quicknotes_metrics.txt +``` + +Command: + +```bash +curl http://localhost:8080/metrics +``` + +Example metrics: + +```text +... +quicknotes_http_responses_by_code_total{code="200"} 170 +quicknotes_http_responses_by_code_total{code="201"} 0 +quicknotes_http_responses_by_code_total{code="204"} 0 +quicknotes_http_responses_by_code_total{code="400"} 0 +quicknotes_http_responses_by_code_total{code="404"} 0 +quicknotes_http_responses_by_code_total{code="405"} 0 +quicknotes_http_responses_by_code_total{code="500"} 0 +``` + +The metrics endpoint exports Prometheus-compatible metrics. + +--- + +### Grafana Dashboard + +Screenshot: + +![](src/screenshots/lab08/grafana_dashboard.png) + + +The dashboard contains four Golden Signal panels: + +- Latency +- Traffic +- Errors +- Saturation + +Traffic generated during testing is visible in the graphs. + +--- + +## Task 1 Design Questions + +### Question a. Pull vs Push + +Prometheus uses a pull model. Prometheus connects to QuickNotes and periodically requests the `/metrics` endpoint. + +Only Prometheus needs network access to QuickNotes. + +If Prometheus cannot reach QuickNotes, metric collection stops and the target becomes `DOWN`, but the application itself continues running normally. + +--- + +### Question b. Why Use a 15 Second Scrape Interval? + +A very short interval such as 5 seconds: + +- increases CPU usage +- increases network traffic +- stores much more time-series data + +A very long interval such as 5 minutes: + +- delays incident detection +- produces coarse graphs +- reduces alert responsiveness + +A 15-second interval provides a good balance between accuracy and resource usage. + +--- + +### Question c. rate() vs irate() vs delta() + +The Traffic panel should use `rate()`. + +- `rate()` calculates the average request rate over a time window and produces stable graphs. +- `irate()` only uses the last two samples and is much noisier. +- `delta()` measures the raw counter increase and is intended for gauges rather than request-rate visualization. + +--- + +### Question d. Why Provision Grafana From Files? + +Provisioning makes monitoring reproducible. + +The dashboard and data source are stored in version control, so every developer receives exactly the same configuration after running Docker Compose. + +No manual configuration through the Grafana UI is required. + + +--- + +## Task 2. One Good Alert + Runbook + +### Alert Rule + +Alert rule file: + +```text +monitoring/prometheus/rules/alerts.yml +``` + +Prometheus rules file: + +```text +submissions/src/lab08/prometheus_rules.json +``` + +The configured alert: + +- triggers when HTTP error ratio exceeds 5% +- must remain above the threshold for 5 minutes +- uses `severity: page` +- contains a runbook annotation + +--- + +### Alert Before Error Traffic + +Source file: + +```text +submissions/src/lab08/prometheus_alerts_initial.json +``` + +Command: + +```bash +curl http://localhost:9090/api/v1/alerts +``` + +Output: + +```json +{ + "status":"success", + "data":{ + "alerts":[] + } +} +``` + +Screenshot: + +![](src/screenshots/lab08/alert_initial.png) + + +Initially no alerts were active. + +--- + +### Generate Error Traffic + +Source file: + +```text +submissions/src/lab08/bad_note.json +``` + +The following malformed JSON was repeatedly sent to QuickNotes: + +```json +{"title": +``` + +This generated continuous HTTP 400 responses while normal traffic continued in parallel. + +--- + +### Alert Evaluation + +Source file: + +```text +submissions/src/lab08/prometheus_alerts_after_error_traffic.json +``` + +Command: + +```bash +curl http://localhost:9090/api/v1/alerts +``` + +The alert rule was successfully evaluated after sustained error traffic. + +**Pending** + +![](src/screenshots/lab08/alert_pending.png) + +**Firing** + +![](src/screenshots/lab08/alert_firing.png) + +The alert transitioned through the expected states: + +```text +Inactive +-> Pending +-> Firing +``` + +--- + +### Runbook + +Runbook location: + +```text +docs/runbook/high-error-rate.md +``` + +The runbook contains: + +- alert description +- triage procedure +- mitigation steps +- post-incident actions + +It provides enough information for an engineer unfamiliar with QuickNotes to investigate the incident. + +--- + +## Task 2 Design Questions + +### Question e. Why Wait Five Minutes? + +A single failed request does not necessarily indicate an incident. + +Using a five-minute window filters out temporary spikes, client mistakes, and short network problems. It reduces false positives and prevents unnecessary paging. + +--- + +### Question f. Symptom Alerts vs Cause Alerts + +The configured alert is a symptom alert because it measures user-visible failures. + +A cause alert could monitor CPU usage, memory usage, or disk utilization. + +Cause alerts are usually worse because high resource usage does not always affect users, while error rate directly reflects service quality. + +--- + +### Question g. Alert Fatigue + +An alert becomes too noisy if engineers are paged even though users are not noticeably affected. + +If more than approximately 20–30% of pages are false positives, engineers begin ignoring alerts, increasing the risk of missing real incidents. + +--- + + +# Observations + +- Prometheus successfully scraped QuickNotes metrics every 15 seconds. +- Grafana automatically loaded the provisioned dashboard on startup. +- The dashboard displayed all four Golden Signals. +- QuickNotes exported Prometheus-compatible metrics through the `/metrics` endpoint. +- Prometheus correctly reported the QuickNotes target as `UP`. +- The configured alert used a sustained threshold instead of firing immediately. +- Version-controlled monitoring configuration makes deployment reproducible across environments. +- Grafana provisioning removed the need for manual dashboard creation after every deployment. + + +# Conclusions + +- Prometheus and Grafana were successfully integrated with the QuickNotes application. +- Infrastructure provisioning automatically configured both the monitoring stack and the dashboard. +- The Golden Signals dashboard provides a quick overview of application health. +- The alert rule follows SRE best practices by detecting sustained user-visible failures instead of transient events. +- The runbook documents the investigation and recovery procedure, making on-call response faster and more consistent. diff --git a/submissions/lab9.md b/submissions/lab9.md new file mode 100644 index 000000000..53c2da69d --- /dev/null +++ b/submissions/lab9.md @@ -0,0 +1,363 @@ +# Lab 9 Submission + +## Task 1. Trivy Image, Filesystem, Config Scan and SBOM + +### Trivy Version + +Trivy was executed using the pinned Docker image: + +```text +aquasec/trivy:0.59.1 +``` + +This satisfies the requirement to pin the scanner version instead of using `latest`. + +--- + +### Image Scan + +Source file: + +```text +submissions/src/lab09/trivy_image.txt +``` + +Command: + +```powershell +docker run --rm ` + -v //var/run/docker.sock:/var/run/docker.sock ` + -v "${PWD}:/work" ` + aquasec/trivy:0.59.1 ` + image ` + --severity HIGH,CRITICAL ` + quicknotes:lab6 +``` + +Summary: + +```text +quicknotes:lab6 (debian 12.14) + +Total: +HIGH: 0 +CRITICAL: 0 + +Go binary: +HIGH: 10 +CRITICAL: 0 +``` + +--- + +### Filesystem Scan + +Source file: + +```text +submissions/src/lab09/trivy_fs.txt +``` + +Command: + +```powershell +docker run --rm ` + -v "${PWD}:/work" ` + -w /work ` + aquasec/trivy:0.59.1 ` + fs ` + --severity HIGH,CRITICAL ` + . +``` + +Summary: + +```text +HIGH: 1 +CRITICAL: 0 +``` + +Finding: + +```text +Asymmetric Private Key + +.vagrant/machines/default/virtualbox/private_key +``` + +--- + +### Configuration Scan + +Source file: + +```text +submissions/src/lab09/trivy_config.txt +``` + +Command: + +```powershell +docker run --rm ` + -v "${PWD}:/work" ` + -w /work ` + aquasec/trivy:0.59.1 ` + config ` + . +``` + +Summary: + +```text +No HIGH or CRITICAL configuration issues were detected. +``` + +--- + +### CycloneDX SBOM + +Source files: + +```text +submissions/src/lab09/sbom.json +``` + +Command: + +```powershell +docker run --rm ` + -v //var/run/docker.sock:/var/run/docker.sock ` + -v "${PWD}:/work" ` + aquasec/trivy:0.59.1 ` + sbom ` + --format cyclonedx ` + --output /work/submissions/src/lab09/sbom.json ` + quicknotes:lab6 +``` + +First lines of the generated SBOM: + +```text +See: + +submissions/src/lab09/sbom.json +``` + +--- + +## HIGH / CRITICAL Triage + +| Finding | Severity | Disposition | Reason | +|----------|---------|-------------|--------| +| Go standard library CVEs inside application binary | HIGH | FIX | The vulnerabilities originate from the Go runtime bundled into the application binary. They should be resolved by rebuilding the application with a newer Go release once patched versions become available. | +| `.vagrant/machines/default/virtualbox/private_key` | HIGH | FALSE POSITIVE | This file is located inside `.vagrant/`, is ignored by Git (`.gitignore`), and is not shipped in the application image. It is a local development artifact rather than a production secret. | + +--- + +## Task 1 Design Questions + +### Question a. Why is CVE severity only one part of triage? + +Severity alone is not enough. Reachability, exploit availability, deployment context, and whether the vulnerable code is actually used are also important. A HIGH vulnerability in unreachable code may present less real risk than a MEDIUM vulnerability that is easily exploitable. + +### Question b. Why are distroless images such an effective security control? + +Distroless images contain only the application and the minimum runtime components. They do not include a shell, package manager, compiler, or many operating system packages. Fewer installed components mean fewer potential vulnerabilities and a much smaller attack surface. + +### Question c. When should `.trivyignore` be used? + +`.trivyignore` should only be used for documented and justified exceptions, such as confirmed false positives or accepted risks with a planned review date. It should not be used simply to hide unresolved vulnerabilities from scan results. + +### Question d. Why is an SBOM useful? + +An SBOM provides a complete inventory of software components included in the application. When a new vulnerability such as Log4Shell is disclosed, the SBOM makes it possible to quickly determine whether the affected component is present without rebuilding or manually inspecting the application. + +--- + + +# Task 2. OWASP ZAP Baseline Scan + +### ZAP Version + +OWASP ZAP was executed using the pinned Docker image: + +```text +ghcr.io/zaproxy/zaproxy:2.16.1 +``` + +--- + +### Initial Baseline Scan + +Reports: + +```text +submissions/src/lab09/zap/report_before.html +submissions/src/lab09/zap/report_before.json +``` + +Command: + +```powershell +docker run --rm ` + -v "${PWD}\submissions\src\lab09\zap:/zap/wrk" ` + ghcr.io/zaproxy/zaproxy:2.16.1 ` + zap-baseline.py ` + -t http://host.docker.internal:8080/health ` + -z "-configfile /zap/wrk/urls.txt" ` + -r report_before.html ` + -J report_before.json +``` + +The baseline scan completed successfully. + +Summary: + +- High: **0** +- Medium: **0** +- Low: **2** +- Informational: **1** + +Screenshot: + +![](screenshots/lab09/zap_report_before.png) + +--- + +## ZAP Findings Triage + +| Finding | Risk | URL | Disposition | Reason | +|----------|------|-----|-------------|--------| +| Insufficient Site Isolation Against Spectre Vulnerability | Low | `/health` | ACCEPT | The application is a small local API without browser-delivered sensitive content. The missing `Cross-Origin-Resource-Policy` header presents negligible practical risk in this deployment. | +| Storable / Non-Storable Content | Informational | `/`, `/robots.txt`, `/sitemap.xml`, `/health` | ACCEPT | The application intentionally exposes only API endpoints and does not implement a website. Missing cache directives on nonexistent pages are acceptable. | +| ZAP is Out of Date | Low | Scanner | ACCEPT | This warning refers to the scanner version itself rather than the application being tested. The lab explicitly required using the pinned version `2.16.1`. | + +--- + +## Security Header Fix + +A middleware was added to apply security headers to every HTTP response. + +Instead of modifying each handler individually, a single middleware wraps the router and automatically injects security headers. + +Implemented headers include: + +- `X-Content-Type-Options: nosniff` +- `X-Frame-Options: DENY` +- `Referrer-Policy: no-referrer` +- `Content-Security-Policy: default-src 'none'` + +The implementation ensures that every endpoint receives the same protection automatically. + +--- + +## Unit Test + +A unit test was added to verify that the middleware injects the expected security headers. + +Output: + +```text +submissions/src/lab09/go_test_security_headers.txt +``` + +Command: + +```powershell +go test ./... +``` + +Result: + +```text +PASS +``` + +The test would fail if the middleware were removed, ensuring that the security fix remains protected against future regressions. + +--- + +## Header Verification + +Headers were verified manually after rebuilding the application. + +Output: + +```text +submissions/src/lab09/headers_after_fix.txt +``` + +Example: + +```text +X-Content-Type-Options: nosniff +X-Frame-Options: DENY +Referrer-Policy: no-referrer +Content-Security-Policy: default-src 'none' +``` + +--- + +## Re-scan After the Fix + +Reports: + +```text +submissions/src/lab09/zap/report_after.html +submissions/src/lab09/zap/report_after.json +``` + +Command: + +```powershell +docker run --rm ` + -v "${PWD}\submissions\src\lab09\zap:/zap/wrk" ` + ghcr.io/zaproxy/zaproxy:2.16.1 ` + zap-baseline.py ` + -t http://host.docker.internal:8080/health ` + -z "-configfile /zap/wrk/urls.txt" ` + -r report_after.html ` + -J report_after.json +``` + +Summary: + +- High: **0** +- Medium: **0** +- Low: **2** +- Informational: **1** + +Screenshot: + +![](screenshots/lab09/zap_report_after.png) + +The re-scan confirmed that no High or Medium findings were introduced after the security changes. The remaining findings are accepted according to the triage decisions above. + +--- + +## Tsk 2 Design Questions + +### Question e. Why implement security headers using middleware? + +Middleware guarantees that every request passes through the same security logic. It avoids duplicated code, prevents accidental omissions when adding new handlers, and centralizes future security changes in a single place. + +### Question f. Why is `Content-Security-Policy: default-src 'none'` acceptable for QuickNotes but not for a website? + +This policy blocks loading of scripts, styles, fonts, images, and other browser resources. That would completely break a traditional website. QuickNotes is a JSON API rather than a browser application, so clients do not require those resources and the strict policy is appropriate. + +### Question g. Why is blindly accepting all ZAP findings dangerous? + +Ignoring findings without reviewing them defeats the purpose of security scanning. A real vulnerability may be overlooked among informational warnings, leading to exploitable issues remaining in production. Proper triage ensures that each finding is consciously evaluated and documented. + +--- + +## Observations + +- Trivy successfully scanned the image, repository filesystem, and project configuration. +- A CycloneDX SBOM was generated successfully. +- The Debian distroless runtime contained no HIGH or CRITICAL operating-system vulnerabilities. +- HIGH findings detected inside the Go binary originate from the bundled Go runtime and should be resolved by upgrading to patched Go releases. +- The filesystem HIGH finding corresponds to a local Vagrant private key excluded from Git and production artifacts. +- OWASP ZAP baseline completed successfully and reported no High or Medium application vulnerabilities. +- Security headers were implemented using middleware and verified with automated tests. +- The application was rescanned after the fix, and only accepted Low/Informational findings remained. \ No newline at end of file diff --git a/submissions/src/lab04/curl_post_verbose.txt b/submissions/src/lab04/curl_post_verbose.txt new file mode 100644 index 000000000..ad0aa8418 --- /dev/null +++ b/submissions/src/lab04/curl_post_verbose.txt @@ -0,0 +1,23 @@ +Note: Unnecessary use of -X or --request, POST is already inferred. +* Trying 127.0.0.1:8080... + % Total % Received % Xferd Average Speed Time Time Time Current + Dload Upload Total Spent Left Speed + 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to localhost (127.0.0.1) port 8080 (#0) +> POST /notes HTTP/1.1 +> Host: localhost:8080 +> User-Agent: curl/7.81.0 +> Accept: */* +> Content-Type: application/json +> Content-Length: 39 +> +} [39 bytes data] +* Mark bundle as not supporting multiuse +< HTTP/1.1 201 Created +< Content-Type: application/json +< Date: Tue, 16 Jun 2026 19:24:37 GMT +< Content-Length: 93 +< +{ [93 bytes data] + 100 132 100 93 100 39 15016 6297 --:--:-- --:--:-- --:--:-- 22000 +* Connection #0 to host localhost left intact +{"id":5,"title":"trace me","body":"in flight","created_at":"2026-06-16T19:24:37.036818895Z"} diff --git a/submissions/src/lab04/debug_1_process.txt b/submissions/src/lab04/debug_1_process.txt new file mode 100644 index 000000000..954816a47 --- /dev/null +++ b/submissions/src/lab04/debug_1_process.txt @@ -0,0 +1,2 @@ +sokosla+ 14719 14504 0 22:29 pts/0 00:00:00 /home/sokoslav1707/.cache/go-build/cb/cb1919c662b2ff978989d8e0230f1042861550086b08469c7493d7fe70687083-d/quicknotes +sokosla+ 15009 491 0 22:31 pts/0 00:00:00 grep --color=auto quicknotes diff --git a/submissions/src/lab04/debug_2_listening.txt b/submissions/src/lab04/debug_2_listening.txt new file mode 100644 index 000000000..7de40898a --- /dev/null +++ b/submissions/src/lab04/debug_2_listening.txt @@ -0,0 +1 @@ +LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=14719,fd=3)) diff --git a/submissions/src/lab04/debug_3_health.txt b/submissions/src/lab04/debug_3_health.txt new file mode 100644 index 000000000..08839f6bb --- /dev/null +++ b/submissions/src/lab04/debug_3_health.txt @@ -0,0 +1 @@ +200 diff --git a/submissions/src/lab04/debug_4_firewall.txt b/submissions/src/lab04/debug_4_firewall.txt new file mode 100644 index 000000000..9e6d6b60a --- /dev/null +++ b/submissions/src/lab04/debug_4_firewall.txt @@ -0,0 +1,8 @@ +Chain INPUT (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination + +Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination + +Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) + pkts bytes target prot opt in out source destination diff --git a/submissions/src/lab04/debug_5_dns.txt b/submissions/src/lab04/debug_5_dns.txt new file mode 100644 index 000000000..7b9ad531d --- /dev/null +++ b/submissions/src/lab04/debug_5_dns.txt @@ -0,0 +1 @@ +127.0.0.1 diff --git a/submissions/src/lab04/dig_example.txt b/submissions/src/lab04/dig_example.txt new file mode 100644 index 000000000..7c186c590 --- /dev/null +++ b/submissions/src/lab04/dig_example.txt @@ -0,0 +1,2 @@ +8.47.69.1 +8.6.112.1 diff --git a/submissions/src/lab04/ip_route.txt b/submissions/src/lab04/ip_route.txt new file mode 100644 index 000000000..ed35241f7 --- /dev/null +++ b/submissions/src/lab04/ip_route.txt @@ -0,0 +1,2 @@ +default via 172.22.0.1 dev eth0 proto kernel +172.22.0.0/20 dev eth0 proto kernel scope link src 172.22.15.253 diff --git a/submissions/src/lab04/journalctl_quicknotes.txt b/submissions/src/lab04/journalctl_quicknotes.txt new file mode 100644 index 000000000..6ba17d74f --- /dev/null +++ b/submissions/src/lab04/journalctl_quicknotes.txt @@ -0,0 +1 @@ +-- No entries -- diff --git a/submissions/src/lab04/lab4-tls.pcap b/submissions/src/lab04/lab4-tls.pcap new file mode 100644 index 000000000..d0c60a054 Binary files /dev/null and b/submissions/src/lab04/lab4-tls.pcap differ diff --git a/submissions/src/lab04/lab4-trace.pcap b/submissions/src/lab04/lab4-trace.pcap new file mode 100644 index 000000000..935ddf789 Binary files /dev/null and b/submissions/src/lab04/lab4-trace.pcap differ diff --git a/submissions/src/lab04/lab4-trace.txt b/submissions/src/lab04/lab4-trace.txt new file mode 100644 index 000000000..45a302fef --- /dev/null +++ b/submissions/src/lab04/lab4-trace.txt @@ -0,0 +1,43 @@ +22:24:37.036086 IP 127.0.0.1.54120 > 127.0.0.1.8080: Flags [S], seq 3806237981, win 65495, options [mss 65495,sackOK,TS val 516986604 ecr 0,nop,wscale 7], length 0 +E..<..@.@............h...............0......... +............ +22:24:37.036096 IP 127.0.0.1.8080 > 127.0.0.1.54120: Flags [S.], seq 4255645791, ack 3806237982, win 65483, options [mss 65495,sackOK,TS val 516986604 ecr 516986604,nop,wscale 7], length 0 +E..<..@.@.<............h..._.........0......... +............ +22:24:37.036102 IP 127.0.0.1.54120 > 127.0.0.1.8080: Flags [.], ack 1, win 512, options [nop,nop,TS val 516986604 ecr 516986604], length 0 +E..4..@.@............h.........`.....(..... +........ +22:24:37.036169 IP 127.0.0.1.54120 > 127.0.0.1.8080: Flags [P.], seq 1:176, ack 1, win 512, options [nop,nop,TS val 516986604 ecr 516986604], length 175: HTTP: POST /notes HTTP/1.1 +E.....@.@..G.........h.........`........... +........POST /notes HTTP/1.1 +Host: localhost:8080 +User-Agent: curl/7.81.0 +Accept: */* +Content-Type: application/json +Content-Length: 39 + +{"title":"trace me","body":"in flight"} +22:24:37.036171 IP 127.0.0.1.8080 > 127.0.0.1.54120: Flags [.], ack 176, win 511, options [nop,nop,TS val 516986604 ecr 516986604], length 0 +E..4..@.@.."...........h...`.........(..... +........ +22:24:37.042141 IP 127.0.0.1.8080 > 127.0.0.1.54120: Flags [P.], seq 1:207, ack 176, win 512, options [nop,nop,TS val 516986610 ecr 516986604], length 206: HTTP: HTTP/1.1 201 Created +E.....@.@..S...........h...`............... +........HTTP/1.1 201 Created +Content-Type: application/json +Date: Tue, 16 Jun 2026 19:24:37 GMT +Content-Length: 93 + +{"id":5,"title":"trace me","body":"in flight","created_at":"2026-06-16T19:24:37.036818895Z"} + +22:24:37.042161 IP 127.0.0.1.54120 > 127.0.0.1.8080: Flags [.], ack 207, win 511, options [nop,nop,TS val 516986610 ecr 516986610], length 0 +E..4..@.@............h...............(..... +........ +22:24:37.042277 IP 127.0.0.1.54120 > 127.0.0.1.8080: Flags [F.], seq 176, ack 207, win 512, options [nop,nop,TS val 516986610 ecr 516986610], length 0 +E..4..@.@............h...............(..... +........ +22:24:37.042322 IP 127.0.0.1.8080 > 127.0.0.1.54120: Flags [F.], seq 207, ack 177, win 512, options [nop,nop,TS val 516986610 ecr 516986610], length 0 +E..4..@.@.. ...........h.............(..... +........ +22:24:37.042332 IP 127.0.0.1.54120 > 127.0.0.1.8080: Flags [.], ack 208, win 512, options [nop,nop,TS val 516986610 ecr 516986610], length 0 +E..4..@.@............h........./.....(..... +........ diff --git a/submissions/src/lab04/mtr_localhost.txt b/submissions/src/lab04/mtr_localhost.txt new file mode 100644 index 000000000..50b21cffb --- /dev/null +++ b/submissions/src/lab04/mtr_localhost.txt @@ -0,0 +1,3 @@ +Start: 2026-06-16T22:28:13+0300 +HOST: PCBUINIYYARIK Loss% Snt Last Avg Best Wrst StDev + 1.|-- localhost 0.0% 5 0.1 0.1 0.0 0.1 0.0 diff --git a/submissions/src/lab04/openssl_s_client.txt b/submissions/src/lab04/openssl_s_client.txt new file mode 100644 index 000000000..22222805d --- /dev/null +++ b/submissions/src/lab04/openssl_s_client.txt @@ -0,0 +1,93 @@ +Connecting to 127.0.0.1 +depth=1 CN=Caddy Local Authority - ECC Intermediate +verify error:num=20:unable to get local issuer certificate +verify return:1 +depth=0 +verify return:1 +CONNECTED(00000003) +--- +Certificate chain + 0 s: + i:CN=Caddy Local Authority - ECC Intermediate + a:PKEY: EC, (prime256v1); sigalg: ecdsa-with-SHA256 + v:NotBefore: Jun 16 19:37:26 2026 GMT; NotAfter: Jun 17 07:37:26 2026 GMT +-----BEGIN CERTIFICATE----- +MIIBvjCCAWSgAwIBAgIRALH4vhe0eqnLkA6Hk7dg8lgwCgYIKoZIzj0EAwIwMzEx +MC8GA1UEAxMoQ2FkZHkgTG9jYWwgQXV0aG9yaXR5IC0gRUNDIEludGVybWVkaWF0 +ZTAeFw0yNjA2MTYxOTM3MjZaFw0yNjA2MTcwNzM3MjZaMAAwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAARAgdXBlU0MTnoV2uRy8JiPxqfo1IbV5EC631ZvZZuOFPbk +UgpMJkfBUZ4nxEGE7o/9DZ9R7qhCTKZ0wNgqPJXTo4GLMIGIMA4GA1UdDwEB/wQE +AwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFDCq +sGs6rzJOWYFBzVTOa2XQTDNfMB8GA1UdIwQYMBaAFEMDSAZs+cqGRWzwooZmPGNg +niV8MBcGA1UdEQEB/wQNMAuCCWxvY2FsaG9zdDAKBggqhkjOPQQDAgNIADBFAiAn +/VhnHFH8ehDa20LBRMug2Zsjh42mG+iE8Vma3CJy0AIhAJ+iUO6Hz5sZwD7RegCi +NdKQ6Bf1LcZPVFtQGRWA+htw +-----END CERTIFICATE----- + 1 s:CN=Caddy Local Authority - ECC Intermediate + i:CN=Caddy Local Authority - 2026 ECC Root + a:PKEY: EC, (prime256v1); sigalg: ecdsa-with-SHA256 + v:NotBefore: Jun 16 19:37:26 2026 GMT; NotAfter: Jun 23 19:37:26 2026 GMT +-----BEGIN CERTIFICATE----- +MIIBxjCCAW2gAwIBAgIQZ+78QN4ndVjlQOS3okWK3TAKBggqhkjOPQQDAjAwMS4w +LAYDVQQDEyVDYWRkeSBMb2NhbCBBdXRob3JpdHkgLSAyMDI2IEVDQyBSb290MB4X +DTI2MDYxNjE5MzcyNloXDTI2MDYyMzE5MzcyNlowMzExMC8GA1UEAxMoQ2FkZHkg +TG9jYWwgQXV0aG9yaXR5IC0gRUNDIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEG +CCqGSM49AwEHA0IABCA6RY3RG3MQz7mnw9pMeK4tIRO768jbRMCxkgntVqpwGBNo +MWFfSQGoa0CqmOtVoM/jnDhAiXXljPCdMPE/d6+jZjBkMA4GA1UdDwEB/wQEAwIB +BjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRDA0gGbPnKhkVs8KKGZjxj +YJ4lfDAfBgNVHSMEGDAWgBQxKYKMT8X/lq3AdqwN/h5nl5D9BzAKBggqhkjOPQQD +AgNHADBEAiBCWgFcTUznhz+iuOLOxcoMConQYyHhrJMQZnQ14ynLewIgMdOIIomP +irdsJSnAog5tV5hagzg7XlXv4GLZlK2CQoQ= +-----END CERTIFICATE----- +--- +Server certificate +subject= +issuer=CN=Caddy Local Authority - ECC Intermediate +--- +No client certificate CA names sent +Peer signing digest: SHA256 +Peer signature type: ecdsa_secp256r1_sha256 +Negotiated TLS1.3 group: X25519MLKEM768 +--- +SSL handshake has read 2359 bytes and written 1606 bytes +Verification error: unable to get local issuer certificate +--- +New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256 +Protocol: TLSv1.3 +Server public key is 256 bit +This TLS version forbids renegotiation. +Compression: NONE +Expansion: NONE +No ALPN negotiated +Early data was not sent +Verify return code: 20 (unable to get local issuer certificate) +--- +DONE +--- +Post-Handshake New Session Ticket arrived: +SSL-Session: + Protocol : TLSv1.3 + Cipher : TLS_AES_128_GCM_SHA256 + Session-ID: D45CDBD59AF0082EF9F2AC0E7F35E8E46C45792F07067CC33F4CB59E20399BB6 + Session-ID-ctx: + Resumption PSK: EC494A90093B4866CCEC141532FE40453E7956FB81BB3B3D068469A47A72F7CC + PSK identity: None + PSK identity hint: None + SRP username: None + TLS session ticket lifetime hint: 604800 (seconds) + TLS session ticket: + 0000 - 0a 35 87 ea 90 3a 65 72-4c 8e ae 1d 03 f5 63 56 .5...:erL.....cV + 0010 - 67 70 6e af 1d 23 2d 44-c6 81 69 41 dc 92 4f 2c gpn..#-D..iA..O, + 0020 - a9 36 c0 c1 d5 a8 78 b6-b2 c1 7a d1 d3 fc 44 27 .6....x...z...D' + 0030 - 04 c0 69 f7 92 6a 26 cc-82 eb c5 a0 14 56 6f ce ..i..j&......Vo. + 0040 - 16 07 7b 44 9c ed 3e 2f-41 8b ea 02 06 58 06 b2 ..{D..>/A....X.. + 0050 - 09 e9 ea 55 66 b9 d2 af-82 6b d0 2a ab 73 b4 6a ...Uf....k.*.s.j + 0060 - a6 6a 61 04 fc 83 a4 e4-78 .ja.....x + + Start Time: 1781640672 + Timeout : 7200 (sec) + Verify return code: 20 (unable to get local issuer certificate) + Extended master secret: no + Max Early Data: 0 +--- +read R BLOCK diff --git a/submissions/src/lab04/ps_go_run.txt b/submissions/src/lab04/ps_go_run.txt new file mode 100644 index 000000000..706a8e872 --- /dev/null +++ b/submissions/src/lab04/ps_go_run.txt @@ -0,0 +1 @@ +sokosla+ 14504 491 0 22:29 pts/0 00:00:00 go run . diff --git a/submissions/src/lab04/qn-broken.log b/submissions/src/lab04/qn-broken.log new file mode 100644 index 000000000..b02eaf5bf --- /dev/null +++ b/submissions/src/lab04/qn-broken.log @@ -0,0 +1,3 @@ +2026/06/16 22:29:54 quicknotes listening on :8080 (notes loaded: 5) +2026/06/16 22:29:54 listen: listen tcp :8080: bind: address already in use +exit status 1 diff --git a/submissions/src/lab04/repair_health.txt b/submissions/src/lab04/repair_health.txt new file mode 100644 index 000000000..73a139d9a --- /dev/null +++ b/submissions/src/lab04/repair_health.txt @@ -0,0 +1 @@ +{"notes":5,"status":"ok"} diff --git a/submissions/src/lab04/ss_8080.txt b/submissions/src/lab04/ss_8080.txt new file mode 100644 index 000000000..9cc9e50c8 --- /dev/null +++ b/submissions/src/lab04/ss_8080.txt @@ -0,0 +1 @@ +LISTEN 0 4096 *:8080 *:* users:(("quicknotes",pid=4001,fd=3)) diff --git a/submissions/src/lab05/curl_guest_health.txt b/submissions/src/lab05/curl_guest_health.txt new file mode 100644 index 000000000..2090ae5e6 Binary files /dev/null and b/submissions/src/lab05/curl_guest_health.txt differ diff --git a/submissions/src/lab05/curl_host_after_restore.txt b/submissions/src/lab05/curl_host_after_restore.txt new file mode 100644 index 000000000..2090ae5e6 Binary files /dev/null and b/submissions/src/lab05/curl_host_after_restore.txt differ diff --git a/submissions/src/lab05/curl_host_health.txt b/submissions/src/lab05/curl_host_health.txt new file mode 100644 index 000000000..2090ae5e6 Binary files /dev/null and b/submissions/src/lab05/curl_host_health.txt differ diff --git a/submissions/src/lab05/docker_health.txt b/submissions/src/lab05/docker_health.txt new file mode 100644 index 000000000..2090ae5e6 Binary files /dev/null and b/submissions/src/lab05/docker_health.txt differ diff --git a/submissions/src/lab05/docker_image_size.txt b/submissions/src/lab05/docker_image_size.txt new file mode 100644 index 000000000..f4f18dfb0 Binary files /dev/null and b/submissions/src/lab05/docker_image_size.txt differ diff --git a/submissions/src/lab05/docker_start_time.txt b/submissions/src/lab05/docker_start_time.txt new file mode 100644 index 000000000..33afbad4e Binary files /dev/null and b/submissions/src/lab05/docker_start_time.txt differ diff --git a/submissions/src/lab05/docker_stats.txt b/submissions/src/lab05/docker_stats.txt new file mode 100644 index 000000000..5db197778 Binary files /dev/null and b/submissions/src/lab05/docker_stats.txt differ diff --git a/submissions/src/lab05/docker_stop_time.txt b/submissions/src/lab05/docker_stop_time.txt new file mode 100644 index 000000000..81fc81ce2 Binary files /dev/null and b/submissions/src/lab05/docker_stop_time.txt differ diff --git a/submissions/src/lab05/docker_top.txt b/submissions/src/lab05/docker_top.txt new file mode 100644 index 000000000..49895e84c Binary files /dev/null and b/submissions/src/lab05/docker_top.txt differ diff --git a/submissions/src/lab05/go_version.txt b/submissions/src/lab05/go_version.txt new file mode 100644 index 000000000..6fa7674d5 Binary files /dev/null and b/submissions/src/lab05/go_version.txt differ diff --git a/submissions/src/lab05/go_version_broken.txt b/submissions/src/lab05/go_version_broken.txt new file mode 100644 index 000000000..48ce76026 Binary files /dev/null and b/submissions/src/lab05/go_version_broken.txt differ diff --git a/submissions/src/lab05/go_version_restored.txt b/submissions/src/lab05/go_version_restored.txt new file mode 100644 index 000000000..6fa7674d5 Binary files /dev/null and b/submissions/src/lab05/go_version_restored.txt differ diff --git a/submissions/src/lab05/snapshot_restore.txt b/submissions/src/lab05/snapshot_restore.txt new file mode 100644 index 000000000..a7873694a Binary files /dev/null and b/submissions/src/lab05/snapshot_restore.txt differ diff --git a/submissions/src/lab05/snapshot_restore_time.txt b/submissions/src/lab05/snapshot_restore_time.txt new file mode 100644 index 000000000..bfd51f2ed Binary files /dev/null and b/submissions/src/lab05/snapshot_restore_time.txt differ diff --git a/submissions/src/lab05/snapshot_save.txt b/submissions/src/lab05/snapshot_save.txt new file mode 100644 index 000000000..3a66e459c Binary files /dev/null and b/submissions/src/lab05/snapshot_save.txt differ diff --git a/submissions/src/lab05/vagrant_up.txt b/submissions/src/lab05/vagrant_up.txt new file mode 100644 index 000000000..a7d3f7859 Binary files /dev/null and b/submissions/src/lab05/vagrant_up.txt differ diff --git a/submissions/src/lab05/vm_boot.txt b/submissions/src/lab05/vm_boot.txt new file mode 100644 index 000000000..6d613a38b Binary files /dev/null and b/submissions/src/lab05/vm_boot.txt differ diff --git a/submissions/src/lab05/vm_boot_time.txt b/submissions/src/lab05/vm_boot_time.txt new file mode 100644 index 000000000..eeb75c3d8 Binary files /dev/null and b/submissions/src/lab05/vm_boot_time.txt differ diff --git a/submissions/src/lab05/vm_free_h.txt b/submissions/src/lab05/vm_free_h.txt new file mode 100644 index 000000000..e7b427e49 Binary files /dev/null and b/submissions/src/lab05/vm_free_h.txt differ diff --git a/submissions/src/lab05/vm_halt.txt b/submissions/src/lab05/vm_halt.txt new file mode 100644 index 000000000..9c0edaabf Binary files /dev/null and b/submissions/src/lab05/vm_halt.txt differ diff --git a/submissions/src/lab05/vm_halt_time.txt b/submissions/src/lab05/vm_halt_time.txt new file mode 100644 index 000000000..22f6ea607 Binary files /dev/null and b/submissions/src/lab05/vm_halt_time.txt differ diff --git a/submissions/src/lab05/vm_process_count.txt b/submissions/src/lab05/vm_process_count.txt new file mode 100644 index 000000000..b8fed3616 Binary files /dev/null and b/submissions/src/lab05/vm_process_count.txt differ diff --git a/submissions/src/lab06/compose_down.txt b/submissions/src/lab06/compose_down.txt new file mode 100644 index 000000000..2ccb59ca4 --- /dev/null +++ b/submissions/src/lab06/compose_down.txt @@ -0,0 +1,14 @@ + Container devops-intro-quicknotes-health-1 Stopping + Container devops-intro-quicknotes-health-1 Stopped + Container devops-intro-quicknotes-health-1 Removing + Container devops-intro-quicknotes-health-1 Removed + Container devops-intro-quicknotes-1 Stopping + Container devops-intro-quicknotes-1 Stopped + Container devops-intro-quicknotes-1 Removing + Container devops-intro-quicknotes-1 Removed + Container devops-intro-quicknotes-init-1 Stopping + Container devops-intro-quicknotes-init-1 Stopped + Container devops-intro-quicknotes-init-1 Removing + Container devops-intro-quicknotes-init-1 Removed + Network devops-intro_default Removing + Network devops-intro_default Removed diff --git a/submissions/src/lab06/compose_down_v.txt b/submissions/src/lab06/compose_down_v.txt new file mode 100644 index 000000000..553c814a7 --- /dev/null +++ b/submissions/src/lab06/compose_down_v.txt @@ -0,0 +1,16 @@ + Container devops-intro-quicknotes-health-1 Stopping + Container devops-intro-quicknotes-health-1 Stopped + Container devops-intro-quicknotes-health-1 Removing + Container devops-intro-quicknotes-health-1 Removed + Container devops-intro-quicknotes-1 Stopping + Container devops-intro-quicknotes-1 Stopped + Container devops-intro-quicknotes-1 Removing + Container devops-intro-quicknotes-1 Removed + Container devops-intro-quicknotes-init-1 Stopping + Container devops-intro-quicknotes-init-1 Stopped + Container devops-intro-quicknotes-init-1 Removing + Container devops-intro-quicknotes-init-1 Removed + Volume devops-intro_quicknotes-data Removing + Network devops-intro_default Removing + Volume devops-intro_quicknotes-data Removed + Network devops-intro_default Removed diff --git a/submissions/src/lab06/compose_health.txt b/submissions/src/lab06/compose_health.txt new file mode 100644 index 000000000..162bc5752 Binary files /dev/null and b/submissions/src/lab06/compose_health.txt differ diff --git a/submissions/src/lab06/compose_ps.txt b/submissions/src/lab06/compose_ps.txt new file mode 100644 index 000000000..ee8214397 Binary files /dev/null and b/submissions/src/lab06/compose_ps.txt differ diff --git a/submissions/src/lab06/compose_up.txt b/submissions/src/lab06/compose_up.txt new file mode 100644 index 000000000..e16abd130 --- /dev/null +++ b/submissions/src/lab06/compose_up.txt @@ -0,0 +1,100 @@ + Image quicknotes:lab6 Building +#1 [internal] load local bake definitions +#1 reading from stdin 579B 0.0s done +#1 DONE 0.0s + +#2 [internal] load build definition from Dockerfile +#2 DONE 0.0s + +#2 [internal] load build definition from Dockerfile +#2 transferring dockerfile: 511B done +#2 DONE 0.0s + +#3 [auth] docker/dockerfile:pull token for registry-1.docker.io +#3 DONE 0.0s + +#4 resolve image config for docker-image://docker.io/docker/dockerfile:1 +#4 DONE 1.1s + +#5 docker-image://docker.io/docker/dockerfile:1@sha256:87999aa3d42bdc6bea60565083ee17e86d1f3339802f543c0d03998580f9cb89 +#5 resolve docker.io/docker/dockerfile:1@sha256:87999aa3d42bdc6bea60565083ee17e86d1f3339802f543c0d03998580f9cb89 0.0s done +#5 CACHED + +#6 [auth] library/golang:pull token for registry-1.docker.io +#6 DONE 0.0s + +#7 [internal] load metadata for docker.io/library/golang:1.24-alpine +#7 ... + +#8 [internal] load metadata for gcr.io/distroless/static-debian12:nonroot +#8 DONE 0.4s + +#7 [internal] load metadata for docker.io/library/golang:1.24-alpine +#7 DONE 0.7s + +#9 [internal] load .dockerignore +#9 transferring context: 2B done +#9 DONE 0.0s + +#10 [builder 1/6] FROM docker.io/library/golang:1.24-alpine@sha256:8bee1901f1e530bfb4a7850aa7a479d17ae3a18beb6e09064ed54cfd245b7191 +#10 resolve docker.io/library/golang:1.24-alpine@sha256:8bee1901f1e530bfb4a7850aa7a479d17ae3a18beb6e09064ed54cfd245b7191 0.0s done +#10 DONE 0.0s + +#11 [stage-1 1/4] FROM gcr.io/distroless/static-debian12:nonroot@sha256:d093aa3e30dbadd3efe1310db061a14da60299baff8450a17fe0ccc514a16639 +#11 resolve gcr.io/distroless/static-debian12:nonroot@sha256:d093aa3e30dbadd3efe1310db061a14da60299baff8450a17fe0ccc514a16639 0.0s done +#11 DONE 0.0s + +#12 [internal] load build context +#12 transferring context: 417B done +#12 DONE 0.0s + +#13 [builder 3/6] COPY go.mod ./ +#13 CACHED + +#14 [builder 6/6] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /out/quicknotes . +#14 CACHED + +#15 [builder 2/6] WORKDIR /src +#15 CACHED + +#16 [stage-1 2/4] COPY --from=builder /out/quicknotes /quicknotes +#16 CACHED + +#17 [builder 4/6] RUN go mod download +#17 CACHED + +#18 [builder 5/6] COPY . . +#18 CACHED + +#19 [stage-1 3/4] COPY --from=builder /src/seed.json /seed.json +#19 CACHED + +#20 exporting to image +#20 exporting layers done +#20 exporting manifest sha256:cc0a1b82c078e97f645cd869854ba4b15dbb1349f74f06147a2e0ad988855bad done +#20 exporting config sha256:fff27ec54e113fc4132f61479b64cf3fa9d2f3a2854967b440ed615df9a10a0c done +#20 exporting attestation manifest sha256:8d9fa6096956b9d474eef9967246b649b99d665bc628c9f162076b0c309c4370 0.0s done +#20 exporting manifest list sha256:1cef2abc5bb0e6086b9c575015315bce3be2502f1c2915292c3eea31bc551686 done +#20 naming to docker.io/library/quicknotes:lab6 done +#20 unpacking to docker.io/library/quicknotes:lab6 done +#20 DONE 0.1s + +#21 resolving provenance for metadata file +#21 DONE 0.0s + Image quicknotes:lab6 Built + Network devops-intro_default Creating + Network devops-intro_default Created + Container devops-intro-quicknotes-init-1 Creating + Container devops-intro-quicknotes-init-1 Created + Container devops-intro-quicknotes-1 Creating + Container devops-intro-quicknotes-1 Created + Container devops-intro-quicknotes-health-1 Creating + Container devops-intro-quicknotes-health-1 Created + Container devops-intro-quicknotes-init-1 Starting + Container devops-intro-quicknotes-init-1 Started + Container devops-intro-quicknotes-init-1 Waiting + Container devops-intro-quicknotes-init-1 Exited + Container devops-intro-quicknotes-1 Starting + Container devops-intro-quicknotes-1 Started + Container devops-intro-quicknotes-health-1 Starting + Container devops-intro-quicknotes-health-1 Started diff --git a/submissions/src/lab06/compose_up_after_down.txt b/submissions/src/lab06/compose_up_after_down.txt new file mode 100644 index 000000000..237c9cf0d --- /dev/null +++ b/submissions/src/lab06/compose_up_after_down.txt @@ -0,0 +1,16 @@ + Network devops-intro_default Creating + Network devops-intro_default Created + Container devops-intro-quicknotes-init-1 Creating + Container devops-intro-quicknotes-init-1 Created + Container devops-intro-quicknotes-1 Creating + Container devops-intro-quicknotes-1 Created + Container devops-intro-quicknotes-health-1 Creating + Container devops-intro-quicknotes-health-1 Created + Container devops-intro-quicknotes-init-1 Starting + Container devops-intro-quicknotes-init-1 Started + Container devops-intro-quicknotes-init-1 Waiting + Container devops-intro-quicknotes-init-1 Exited + Container devops-intro-quicknotes-1 Starting + Container devops-intro-quicknotes-1 Started + Container devops-intro-quicknotes-health-1 Starting + Container devops-intro-quicknotes-health-1 Started diff --git a/submissions/src/lab06/compose_up_after_down_v.txt b/submissions/src/lab06/compose_up_after_down_v.txt new file mode 100644 index 000000000..b3e0ce7fa --- /dev/null +++ b/submissions/src/lab06/compose_up_after_down_v.txt @@ -0,0 +1,18 @@ + Network devops-intro_default Creating + Network devops-intro_default Created + Volume devops-intro_quicknotes-data Creating + Volume devops-intro_quicknotes-data Created + Container devops-intro-quicknotes-init-1 Creating + Container devops-intro-quicknotes-init-1 Created + Container devops-intro-quicknotes-1 Creating + Container devops-intro-quicknotes-1 Created + Container devops-intro-quicknotes-health-1 Creating + Container devops-intro-quicknotes-health-1 Created + Container devops-intro-quicknotes-init-1 Starting + Container devops-intro-quicknotes-init-1 Started + Container devops-intro-quicknotes-init-1 Waiting + Container devops-intro-quicknotes-init-1 Exited + Container devops-intro-quicknotes-1 Starting + Container devops-intro-quicknotes-1 Started + Container devops-intro-quicknotes-health-1 Starting + Container devops-intro-quicknotes-health-1 Started diff --git a/submissions/src/lab06/container_id.txt b/submissions/src/lab06/container_id.txt new file mode 100644 index 000000000..eb44cb225 Binary files /dev/null and b/submissions/src/lab06/container_id.txt differ diff --git a/submissions/src/lab06/docker_build.txt b/submissions/src/lab06/docker_build.txt new file mode 100644 index 000000000..d9506584d Binary files /dev/null and b/submissions/src/lab06/docker_build.txt differ diff --git a/submissions/src/lab06/docker_images.txt b/submissions/src/lab06/docker_images.txt new file mode 100644 index 000000000..367672651 Binary files /dev/null and b/submissions/src/lab06/docker_images.txt differ diff --git a/submissions/src/lab06/docker_inspect_config.txt b/submissions/src/lab06/docker_inspect_config.txt new file mode 100644 index 000000000..2fd3ffe72 Binary files /dev/null and b/submissions/src/lab06/docker_inspect_config.txt differ diff --git a/submissions/src/lab06/docker_run_health.txt b/submissions/src/lab06/docker_run_health.txt new file mode 100644 index 000000000..2090ae5e6 Binary files /dev/null and b/submissions/src/lab06/docker_run_health.txt differ diff --git a/submissions/src/lab06/docker_run_id.txt b/submissions/src/lab06/docker_run_id.txt new file mode 100644 index 000000000..857eafdd6 Binary files /dev/null and b/submissions/src/lab06/docker_run_id.txt differ diff --git a/submissions/src/lab06/docker_run_rm.txt b/submissions/src/lab06/docker_run_rm.txt new file mode 100644 index 000000000..08635c5cd Binary files /dev/null and b/submissions/src/lab06/docker_run_rm.txt differ diff --git a/submissions/src/lab06/docker_run_stop.txt b/submissions/src/lab06/docker_run_stop.txt new file mode 100644 index 000000000..08635c5cd Binary files /dev/null and b/submissions/src/lab06/docker_run_stop.txt differ diff --git a/submissions/src/lab06/golang_base_image_size.txt b/submissions/src/lab06/golang_base_image_size.txt new file mode 100644 index 000000000..ac301793a Binary files /dev/null and b/submissions/src/lab06/golang_base_image_size.txt differ diff --git a/submissions/src/lab06/persistence_after_down_up.txt b/submissions/src/lab06/persistence_after_down_up.txt new file mode 100644 index 000000000..95e857131 Binary files /dev/null and b/submissions/src/lab06/persistence_after_down_up.txt differ diff --git a/submissions/src/lab06/persistence_after_down_v_up.txt b/submissions/src/lab06/persistence_after_down_v_up.txt new file mode 100644 index 000000000..36cb84122 Binary files /dev/null and b/submissions/src/lab06/persistence_after_down_v_up.txt differ diff --git a/submissions/src/lab06/persistence_before_down.txt b/submissions/src/lab06/persistence_before_down.txt new file mode 100644 index 000000000..32984a2d3 Binary files /dev/null and b/submissions/src/lab06/persistence_before_down.txt differ diff --git a/submissions/src/lab06/persistence_post.txt b/submissions/src/lab06/persistence_post.txt new file mode 100644 index 000000000..bd3f85508 Binary files /dev/null and b/submissions/src/lab06/persistence_post.txt differ diff --git a/submissions/src/lab06/quicknotes_image_size.txt b/submissions/src/lab06/quicknotes_image_size.txt new file mode 100644 index 000000000..2a2abcda2 Binary files /dev/null and b/submissions/src/lab06/quicknotes_image_size.txt differ diff --git a/submissions/src/lab06/trivy.txt b/submissions/src/lab06/trivy.txt new file mode 100644 index 000000000..dace3ea58 Binary files /dev/null and b/submissions/src/lab06/trivy.txt differ diff --git a/submissions/src/lab06/verify_cap_drop.txt b/submissions/src/lab06/verify_cap_drop.txt new file mode 100644 index 000000000..554726647 Binary files /dev/null and b/submissions/src/lab06/verify_cap_drop.txt differ diff --git a/submissions/src/lab06/verify_no_new_privileges.txt b/submissions/src/lab06/verify_no_new_privileges.txt new file mode 100644 index 000000000..53d3b5bba Binary files /dev/null and b/submissions/src/lab06/verify_no_new_privileges.txt differ diff --git a/submissions/src/lab06/verify_no_shell.txt b/submissions/src/lab06/verify_no_shell.txt new file mode 100644 index 000000000..939621a86 Binary files /dev/null and b/submissions/src/lab06/verify_no_shell.txt differ diff --git a/submissions/src/lab06/verify_readonly_rootfs.txt b/submissions/src/lab06/verify_readonly_rootfs.txt new file mode 100644 index 000000000..abdce89f3 Binary files /dev/null and b/submissions/src/lab06/verify_readonly_rootfs.txt differ diff --git a/submissions/src/lab06/verify_user_nonroot.txt b/submissions/src/lab06/verify_user_nonroot.txt new file mode 100644 index 000000000..e7438ee52 Binary files /dev/null and b/submissions/src/lab06/verify_user_nonroot.txt differ diff --git a/submissions/src/lab07/ansible_ping.txt b/submissions/src/lab07/ansible_ping.txt new file mode 100644 index 000000000..41719ef81 --- /dev/null +++ b/submissions/src/lab07/ansible_ping.txt @@ -0,0 +1,4 @@ +lab5-vm | SUCCESS => { + "changed": false, + "ping": "pong" +} diff --git a/submissions/src/lab07/ansible_version.txt b/submissions/src/lab07/ansible_version.txt new file mode 100644 index 000000000..584dacd93 --- /dev/null +++ b/submissions/src/lab07/ansible_version.txt @@ -0,0 +1,9 @@ +ansible [core 2.17.14] + config file = /etc/ansible/ansible.cfg + configured module search path = ['/home/sokoslav1707/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] + ansible python module location = /usr/lib/python3/dist-packages/ansible + ansible collection location = /home/sokoslav1707/.ansible/collections:/usr/share/ansible/collections + executable location = /usr/bin/ansible + python version = 3.10.12 (main, Mar 3 2026, 11:56:32) [GCC 11.4.0] (/usr/bin/python3) + jinja version = 3.0.3 + libyaml = True diff --git a/submissions/src/lab07/check_diff.txt b/submissions/src/lab07/check_diff.txt new file mode 100644 index 000000000..3b70cc092 --- /dev/null +++ b/submissions/src/lab07/check_diff.txt @@ -0,0 +1,42 @@ + +PLAY [Deploy QuickNotes] ******************************************************* + +TASK [Ensure quicknotes system user exists] ************************************ +ok: [lab5-vm] + +TASK [Ensure data directory exists] ******************************************** +ok: [lab5-vm] + +TASK [Copy seed file] ********************************************************** +ok: [lab5-vm] + +TASK [Copy QuickNotes binary] ************************************************** +ok: [lab5-vm] + +TASK [Render systemd unit] ***************************************************** +--- before: /etc/systemd/system/quicknotes.service ++++ after: /home/sokoslav1707/.ansible/tmp/ansible-local-18162ot8znb58/tmpr1imwbmv/quicknotes.service.j2 +@@ -12,7 +12,7 @@ + Environment=SEED_PATH=/var/lib/quicknotes/seed.json + ExecStart=/usr/local/bin/quicknotes + Restart=on-failure +-RestartSec=2 ++RestartSec=3 + + [Install] + WantedBy=multi-user.target + +changed: [lab5-vm] + +TASK [Enable and start QuickNotes service] ************************************* +ok: [lab5-vm] + +RUNNING HANDLER [reload systemd] *********************************************** +ok: [lab5-vm] + +RUNNING HANDLER [restart quicknotes] ******************************************* +changed: [lab5-vm] + +PLAY RECAP ********************************************************************* +lab5-vm : ok=8 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + diff --git a/submissions/src/lab07/check_run.txt b/submissions/src/lab07/check_run.txt new file mode 100644 index 000000000..4a778bcf5 --- /dev/null +++ b/submissions/src/lab07/check_run.txt @@ -0,0 +1,30 @@ + +PLAY [Deploy QuickNotes] ******************************************************* + +TASK [Ensure quicknotes system user exists] ************************************ +changed: [lab5-vm] + +TASK [Ensure data directory exists] ******************************************** +changed: [lab5-vm] + +TASK [Copy seed file] ********************************************************** +changed: [lab5-vm] + +TASK [Copy QuickNotes binary] ************************************************** +changed: [lab5-vm] + +TASK [Render systemd unit] ***************************************************** +changed: [lab5-vm] + +TASK [Enable and start QuickNotes service] ************************************* +ok: [lab5-vm] + +RUNNING HANDLER [reload systemd] *********************************************** +ok: [lab5-vm] + +RUNNING HANDLER [restart quicknotes] ******************************************* +changed: [lab5-vm] + +PLAY RECAP ********************************************************************* +lab5-vm : ok=8 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + diff --git a/submissions/src/lab07/first_run.txt b/submissions/src/lab07/first_run.txt new file mode 100644 index 000000000..4a778bcf5 --- /dev/null +++ b/submissions/src/lab07/first_run.txt @@ -0,0 +1,30 @@ + +PLAY [Deploy QuickNotes] ******************************************************* + +TASK [Ensure quicknotes system user exists] ************************************ +changed: [lab5-vm] + +TASK [Ensure data directory exists] ******************************************** +changed: [lab5-vm] + +TASK [Copy seed file] ********************************************************** +changed: [lab5-vm] + +TASK [Copy QuickNotes binary] ************************************************** +changed: [lab5-vm] + +TASK [Render systemd unit] ***************************************************** +changed: [lab5-vm] + +TASK [Enable and start QuickNotes service] ************************************* +ok: [lab5-vm] + +RUNNING HANDLER [reload systemd] *********************************************** +ok: [lab5-vm] + +RUNNING HANDLER [restart quicknotes] ******************************************* +changed: [lab5-vm] + +PLAY RECAP ********************************************************************* +lab5-vm : ok=8 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + diff --git a/submissions/src/lab07/quicknotes_binary_ls.txt b/submissions/src/lab07/quicknotes_binary_ls.txt new file mode 100644 index 000000000..b80c6ba44 --- /dev/null +++ b/submissions/src/lab07/quicknotes_binary_ls.txt @@ -0,0 +1 @@ +-rwxrwxrwx 1 sokoslav1707 sokoslav1707 5.6M Jun 30 21:26 ansible/files/quicknotes diff --git a/submissions/src/lab07/restore_8080_final.txt b/submissions/src/lab07/restore_8080_final.txt new file mode 100644 index 000000000..ecb09bc60 --- /dev/null +++ b/submissions/src/lab07/restore_8080_final.txt @@ -0,0 +1,24 @@ + +PLAY [Deploy QuickNotes] ******************************************************* + +TASK [Ensure quicknotes system user exists] ************************************ +ok: [lab5-vm] + +TASK [Ensure data directory exists] ******************************************** +ok: [lab5-vm] + +TASK [Copy seed file] ********************************************************** +ok: [lab5-vm] + +TASK [Copy QuickNotes binary] ************************************************** +ok: [lab5-vm] + +TASK [Render systemd unit] ***************************************************** +ok: [lab5-vm] + +TASK [Enable and start QuickNotes service] ************************************* +ok: [lab5-vm] + +PLAY RECAP ********************************************************************* +lab5-vm : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + diff --git a/submissions/src/lab07/second_run_idempotent.txt b/submissions/src/lab07/second_run_idempotent.txt new file mode 100644 index 000000000..ecb09bc60 --- /dev/null +++ b/submissions/src/lab07/second_run_idempotent.txt @@ -0,0 +1,24 @@ + +PLAY [Deploy QuickNotes] ******************************************************* + +TASK [Ensure quicknotes system user exists] ************************************ +ok: [lab5-vm] + +TASK [Ensure data directory exists] ******************************************** +ok: [lab5-vm] + +TASK [Copy seed file] ********************************************************** +ok: [lab5-vm] + +TASK [Copy QuickNotes binary] ************************************************** +ok: [lab5-vm] + +TASK [Render systemd unit] ***************************************************** +ok: [lab5-vm] + +TASK [Enable and start QuickNotes service] ************************************* +ok: [lab5-vm] + +PLAY RECAP ********************************************************************* +lab5-vm : ok=6 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + diff --git a/submissions/src/lab07/selective_change_9090.txt b/submissions/src/lab07/selective_change_9090.txt new file mode 100644 index 000000000..9b8f9a1ee --- /dev/null +++ b/submissions/src/lab07/selective_change_9090.txt @@ -0,0 +1,30 @@ + +PLAY [Deploy QuickNotes] ******************************************************* + +TASK [Ensure quicknotes system user exists] ************************************ +ok: [lab5-vm] + +TASK [Ensure data directory exists] ******************************************** +ok: [lab5-vm] + +TASK [Copy seed file] ********************************************************** +ok: [lab5-vm] + +TASK [Copy QuickNotes binary] ************************************************** +ok: [lab5-vm] + +TASK [Render systemd unit] ***************************************************** +changed: [lab5-vm] + +TASK [Enable and start QuickNotes service] ************************************* +ok: [lab5-vm] + +RUNNING HANDLER [reload systemd] *********************************************** +ok: [lab5-vm] + +RUNNING HANDLER [restart quicknotes] ******************************************* +changed: [lab5-vm] + +PLAY RECAP ********************************************************************* +lab5-vm : ok=8 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 + diff --git a/submissions/src/lab07/systemctl_status.txt b/submissions/src/lab07/systemctl_status.txt new file mode 100644 index 000000000..64a3c6999 --- /dev/null +++ b/submissions/src/lab07/systemctl_status.txt @@ -0,0 +1,9 @@ +● quicknotes.service - QuickNotes service + Loaded: loaded (/etc/systemd/system/quicknotes.service; enabled; vendor preset: enabled) + Active: active (running) since Tue 2026-06-30 18:28:01 UTC; 1min 0s ago + Main PID: 1935 (quicknotes) + Tasks: 7 (limit: 1099) + Memory: 1.0M + CPU: 4ms + CGroup: /system.slice/quicknotes.service + └─1935 /usr/local/bin/quicknotes diff --git a/submissions/src/lab07/vagrant_ssh_config.txt b/submissions/src/lab07/vagrant_ssh_config.txt new file mode 100644 index 000000000..eb8808300 Binary files /dev/null and b/submissions/src/lab07/vagrant_ssh_config.txt differ diff --git a/submissions/src/lab08/bad_note.json b/submissions/src/lab08/bad_note.json new file mode 100644 index 000000000..67209cf82 --- /dev/null +++ b/submissions/src/lab08/bad_note.json @@ -0,0 +1 @@ +{"title": \ No newline at end of file diff --git a/submissions/src/lab08/compose_down_before_lab8.txt b/submissions/src/lab08/compose_down_before_lab8.txt new file mode 100644 index 000000000..2ccb59ca4 --- /dev/null +++ b/submissions/src/lab08/compose_down_before_lab8.txt @@ -0,0 +1,14 @@ + Container devops-intro-quicknotes-health-1 Stopping + Container devops-intro-quicknotes-health-1 Stopped + Container devops-intro-quicknotes-health-1 Removing + Container devops-intro-quicknotes-health-1 Removed + Container devops-intro-quicknotes-1 Stopping + Container devops-intro-quicknotes-1 Stopped + Container devops-intro-quicknotes-1 Removing + Container devops-intro-quicknotes-1 Removed + Container devops-intro-quicknotes-init-1 Stopping + Container devops-intro-quicknotes-init-1 Stopped + Container devops-intro-quicknotes-init-1 Removing + Container devops-intro-quicknotes-init-1 Removed + Network devops-intro_default Removing + Network devops-intro_default Removed diff --git a/submissions/src/lab08/compose_ps.txt b/submissions/src/lab08/compose_ps.txt new file mode 100644 index 000000000..ac4d1c38e Binary files /dev/null and b/submissions/src/lab08/compose_ps.txt differ diff --git a/submissions/src/lab08/compose_up.txt b/submissions/src/lab08/compose_up.txt new file mode 100644 index 000000000..ac47ff0fa --- /dev/null +++ b/submissions/src/lab08/compose_up.txt @@ -0,0 +1,594 @@ + Image grafana/grafana:12.2.0 Pulling + Image prom/prometheus:v3.6.0 Pulling + e9fa37e588a8 Pulling fs layer 0B + 4314b14247f8 Pulling fs layer 0B + 7dc70dd519ad Pulling fs layer 0B + 03def9af9150 Pulling fs layer 0B + 9fa9226be034 Pulling fs layer 0B + 15e2cd5823b3 Pulling fs layer 0B + 92fd369f57f0 Pulling fs layer 0B + d14d9311398e Pulling fs layer 0B + 1617e25568b2 Pulling fs layer 0B + 0357ac67262f Pulling fs layer 0B + 55afa1ecc21d Pulling fs layer 0B + 7e079c586e0d Pulling fs layer 0B + 959723483440 Pulling fs layer 0B + 4f4fb700ef54 Pulling fs layer 0B + 8551e3a9b2dd Pulling fs layer 0B + 6c1eb8f7a561 Pulling fs layer 0B + 6e6085636f02 Pulling fs layer 0B + 39e3d1c87577 Pulling fs layer 0B + b124010701b9 Pulling fs layer 0B + 5ed3d4d076b0 Pulling fs layer 0B + ffd68bafd674 Pulling fs layer 0B + 4f4fb700ef54 Already exists 0B + 7dc70dd519ad Download complete 0B + 0357ac67262f Download complete 0B + 4314b14247f8 Download complete 0B + 03def9af9150 Download complete 0B + e9fa37e588a8 Download complete 0B + 55afa1ecc21d Downloading 1.049MB + 8551e3a9b2dd Download complete 0B + d14d9311398e Downloading 1.049MB + 92fd369f57f0 Download complete 0B + 55afa1ecc21d Downloading 1.049MB + 39e3d1c87577 Download complete 0B + d14d9311398e Downloading 1.049MB + 7e079c586e0d Download complete 0B + ffd68bafd674 Download complete 0B + 6c1eb8f7a561 Download complete 0B + 55afa1ecc21d Downloading 3.146MB + 9fa9226be034 Download complete 0B + d14d9311398e Downloading 3.146MB + 1617e25568b2 Download complete 0B + 55afa1ecc21d Download complete 0B + 959723483440 Downloading 1.049MB + 9fa9226be034 Extracting 1B + 15e2cd5823b3 Downloading 1.049MB + d14d9311398e Downloading 4.194MB + 959723483440 Downloading 2.097MB + 39e3d1c87577 Pull complete 0B + 55afa1ecc21d Pull complete 0B + 9fa9226be034 Pull complete 0B + 1617e25568b2 Extracting 1B + 15e2cd5823b3 Downloading 1.049MB + d14d9311398e Downloading 6.291MB + b124010701b9 Downloading 1.049MB + 6e6085636f02 Downloading 1.049MB + 959723483440 Downloading 3.146MB + 1617e25568b2 Pull complete 0B + 15e2cd5823b3 Downloading 2.097MB + d14d9311398e Downloading 7.34MB + b124010701b9 Downloading 1.049MB + 6e6085636f02 Downloading 1.049MB + 959723483440 Downloading 3.146MB + 15e2cd5823b3 Downloading 2.097MB + d14d9311398e Downloading 8.389MB + b124010701b9 Downloading 1.049MB + 6e6085636f02 Downloading 2.097MB + 959723483440 Downloading 5.243MB + 15e2cd5823b3 Downloading 3.146MB + d14d9311398e Downloading 11.53MB + b124010701b9 Downloading 2.097MB + 5ed3d4d076b0 Downloading 1.049MB + 6e6085636f02 Downloading 2.097MB + 959723483440 Downloading 6.291MB + 15e2cd5823b3 Downloading 3.146MB + d14d9311398e Downloading 12.58MB + b124010701b9 Downloading 2.097MB + 5ed3d4d076b0 Downloading 1.049MB + 6e6085636f02 Downloading 3.146MB + 959723483440 Downloading 7.34MB + 15e2cd5823b3 Downloading 5.243MB + d14d9311398e Downloading 14.68MB + 6e6085636f02 Downloading 4.194MB + 959723483440 Download complete 0B + b124010701b9 Downloading 3.146MB + 5ed3d4d076b0 Downloading 1.049MB + 15e2cd5823b3 Downloading 6.291MB + d14d9311398e Downloading 16.78MB + b124010701b9 Download complete 0B + 5ed3d4d076b0 Downloading 2.097MB + 6e6085636f02 Downloading 5.243MB + b124010701b9 Extracting 1B + 15e2cd5823b3 Downloading 6.291MB + d14d9311398e Downloading 18.87MB + 5ed3d4d076b0 Downloading 2.097MB + 6e6085636f02 Downloading 5.243MB + 959723483440 Extracting 1B + b124010701b9 Pull complete 0B + 15e2cd5823b3 Downloading 7.34MB + d14d9311398e Downloading 19.92MB + 5ed3d4d076b0 Downloading 2.097MB + 6e6085636f02 Downloading 6.291MB + 8551e3a9b2dd Pull complete 0B + 6c1eb8f7a561 Pull complete 0B + 959723483440 Pull complete 0B + 15e2cd5823b3 Downloading 8.389MB + d14d9311398e Downloading 22.02MB + 5ed3d4d076b0 Downloading 2.097MB + 6e6085636f02 Downloading 7.34MB + 15e2cd5823b3 Downloading 8.389MB + d14d9311398e Downloading 23.07MB + 6e6085636f02 Downloading 8.389MB + 5ed3d4d076b0 Downloading 3.146MB + 15e2cd5823b3 Downloading 10.49MB + d14d9311398e Downloading 25.17MB + 5ed3d4d076b0 Downloading 3.146MB + 6e6085636f02 Downloading 9.437MB + 15e2cd5823b3 Downloading 12.58MB + d14d9311398e Downloading 27.26MB + 6e6085636f02 Downloading 10.49MB + 5ed3d4d076b0 Downloading 4.194MB + 15e2cd5823b3 Downloading 13.63MB + d14d9311398e Downloading 29.36MB + 5ed3d4d076b0 Downloading 4.194MB + 6e6085636f02 Downloading 11.53MB + 15e2cd5823b3 Downloading 13.63MB + d14d9311398e Downloading 30.41MB + 5ed3d4d076b0 Downloading 4.194MB + 6e6085636f02 Downloading 11.53MB + 15e2cd5823b3 Downloading 15.73MB + d14d9311398e Downloading 32.51MB + 6e6085636f02 Downloading 13.63MB + 5ed3d4d076b0 Downloading 5.243MB + d14d9311398e Downloading 34.6MB + 15e2cd5823b3 Downloading 16.78MB + 6e6085636f02 Downloading 15.73MB + 5ed3d4d076b0 Downloading 6.291MB + 15e2cd5823b3 Downloading 18.87MB + d14d9311398e Downloading 36.7MB + 5ed3d4d076b0 Downloading 6.291MB + 6e6085636f02 Downloading 17.44MB + 15e2cd5823b3 Downloading 18.87MB + d14d9311398e Downloading 37.75MB + 5ed3d4d076b0 Downloading 6.291MB + 6e6085636f02 Downloading 17.83MB + 15e2cd5823b3 Downloading 19.92MB + d14d9311398e Downloading 38.8MB + 5ed3d4d076b0 Downloading 7.34MB + 6e6085636f02 Downloading 18.87MB + 15e2cd5823b3 Downloading 22.02MB + 5ed3d4d076b0 Downloading 8.389MB + d14d9311398e Downloading 41.94MB + 6e6085636f02 Downloading 20.97MB + 15e2cd5823b3 Downloading 24.12MB + d14d9311398e Downloading 44.04MB + 6e6085636f02 Downloading 23.07MB + 5ed3d4d076b0 Downloading 8.389MB + 15e2cd5823b3 Downloading 24.12MB + d14d9311398e Downloading 45.09MB + 6e6085636f02 Downloading 24.12MB + 5ed3d4d076b0 Downloading 8.389MB + 15e2cd5823b3 Downloading 25.17MB + d14d9311398e Downloading 47.19MB + 6e6085636f02 Downloading 25.17MB + 5ed3d4d076b0 Downloading 9.437MB + 6e6085636f02 Downloading 25.17MB + 5ed3d4d076b0 Downloading 9.437MB + 15e2cd5823b3 Downloading 26.21MB + d14d9311398e Downloading 48.23MB + 15e2cd5823b3 Downloading 27.26MB + d14d9311398e Downloading 50.33MB + 5ed3d4d076b0 Downloading 10.49MB + 6e6085636f02 Downloading 27.26MB + 15e2cd5823b3 Downloading 29.36MB + d14d9311398e Downloading 51.38MB + 5ed3d4d076b0 Downloading 11.53MB + 6e6085636f02 Downloading 29.36MB + 15e2cd5823b3 Downloading 31.46MB + d14d9311398e Downloading 53.48MB + 5ed3d4d076b0 Downloading 12.58MB + 6e6085636f02 Downloading 31.46MB + d14d9311398e Downloading 55.57MB + 15e2cd5823b3 Downloading 33.55MB + 5ed3d4d076b0 Downloading 12.58MB + 6e6085636f02 Downloading 34.6MB + d14d9311398e Downloading 57.67MB + 15e2cd5823b3 Downloading 35.65MB + 5ed3d4d076b0 Downloading 13.63MB + 6e6085636f02 Downloading 36.7MB + 15e2cd5823b3 Downloading 37.75MB + d14d9311398e Download complete 0B + 5ed3d4d076b0 Downloading 14.68MB + 6e6085636f02 Downloading 38.8MB + 15e2cd5823b3 Downloading 39.85MB + 5ed3d4d076b0 Downloading 14.68MB + 6e6085636f02 Downloading 39.85MB + 15e2cd5823b3 Downloading 40.89MB + 5ed3d4d076b0 Downloading 15.73MB + 6e6085636f02 Downloading 40.89MB + 15e2cd5823b3 Downloading 41.94MB + 5ed3d4d076b0 Downloading 16.78MB + 6e6085636f02 Downloading 42.99MB + 15e2cd5823b3 Downloading 42.99MB + 6e6085636f02 Downloading 44.04MB + 5ed3d4d076b0 Downloading 17.83MB + 15e2cd5823b3 Downloading 45.09MB + 6e6085636f02 Downloading 45.09MB + 5ed3d4d076b0 Downloading 18.87MB + 15e2cd5823b3 Downloading 46.14MB + 6e6085636f02 Downloading 46.14MB + 5ed3d4d076b0 Downloading 19.92MB + 15e2cd5823b3 Downloading 48.23MB + 5ed3d4d076b0 Downloading 22.02MB + 6e6085636f02 Downloading 47.19MB + 15e2cd5823b3 Downloading 49.28MB + 5ed3d4d076b0 Download complete 0B + 6e6085636f02 Downloading 49.28MB + 15e2cd5823b3 Downloading 50.33MB + 6e6085636f02 Downloading 50.33MB + 15e2cd5823b3 Downloading 51.38MB + 6e6085636f02 Downloading 51.38MB + 15e2cd5823b3 Downloading 53.48MB + 6e6085636f02 Downloading 52.43MB + 15e2cd5823b3 Downloading 54.53MB + 6e6085636f02 Downloading 54.53MB + 15e2cd5823b3 Downloading 55.57MB + 6e6085636f02 Downloading 55.57MB + 15e2cd5823b3 Downloading 56.62MB + 6e6085636f02 Downloading 57.67MB + 15e2cd5823b3 Downloading 58.72MB + 6e6085636f02 Downloading 58.72MB + 15e2cd5823b3 Downloading 59.77MB + 6e6085636f02 Downloading 59.77MB + 15e2cd5823b3 Downloading 62.91MB + 6e6085636f02 Downloading 61.87MB + 15e2cd5823b3 Download complete 0B + 6e6085636f02 Downloading 62.91MB + 15e2cd5823b3 Extracting 1B + 6e6085636f02 Downloading 63.96MB + 15e2cd5823b3 Extracting 1B + 6e6085636f02 Downloading 66.06MB + 15e2cd5823b3 Extracting 1B + 6e6085636f02 Downloading 67.11MB + 15e2cd5823b3 Extracting 1B + 6e6085636f02 Downloading 68.16MB + 6e6085636f02 Downloading 70.25MB + d14d9311398e Extracting 1B + 15e2cd5823b3 Pull complete 0B + 6e6085636f02 Downloading 71.3MB + d14d9311398e Extracting 1B + 6e6085636f02 Downloading 72.35MB + d14d9311398e Extracting 1B + 6e6085636f02 Downloading 73.4MB + d14d9311398e Extracting 1B + 7dc70dd519ad Pull complete 0B + 6e6085636f02 Downloading 75.5MB + 0357ac67262f Pull complete 0B + 4314b14247f8 Pull complete 0B + 03def9af9150 Pull complete 0B + e9fa37e588a8 Pull complete 0B + 92fd369f57f0 Pull complete 0B + d14d9311398e Pull complete 0B + Image prom/prometheus:v3.6.0 Pulled + 6e6085636f02 Downloading 76.55MB + 6e6085636f02 Downloading 77.59MB + 6e6085636f02 Downloading 79.69MB + 6e6085636f02 Downloading 80.74MB + 6e6085636f02 Downloading 82.84MB + 6e6085636f02 Downloading 83.89MB + 6e6085636f02 Downloading 85.98MB + 6e6085636f02 Downloading 87.03MB + 6e6085636f02 Downloading 89.13MB + 6e6085636f02 Downloading 90.18MB + 6e6085636f02 Downloading 92.16MB + 6e6085636f02 Downloading 93.32MB + 6e6085636f02 Downloading 94.37MB + 6e6085636f02 Downloading 96.47MB + 6e6085636f02 Downloading 97.52MB + 6e6085636f02 Downloading 99.61MB + 6e6085636f02 Downloading 100.7MB + 6e6085636f02 Downloading 102.8MB + 6e6085636f02 Downloading 103.8MB + 6e6085636f02 Downloading 104.9MB + 6e6085636f02 Downloading 107MB + 6e6085636f02 Downloading 108MB + 6e6085636f02 Downloading 110.1MB + 6e6085636f02 Downloading 111.1MB + 6e6085636f02 Downloading 113.2MB + 6e6085636f02 Downloading 114.3MB + 6e6085636f02 Downloading 115.3MB + 6e6085636f02 Downloading 117.4MB + 6e6085636f02 Downloading 118.5MB + 6e6085636f02 Downloading 119.5MB + 6e6085636f02 Downloading 121.6MB + 6e6085636f02 Downloading 122.7MB + 6e6085636f02 Downloading 123.7MB + 6e6085636f02 Downloading 125.8MB + 6e6085636f02 Downloading 126.9MB + 6e6085636f02 Downloading 129MB + 6e6085636f02 Downloading 130MB + 6e6085636f02 Downloading 132.1MB + 6e6085636f02 Downloading 133.2MB + 6e6085636f02 Downloading 134.2MB + 6e6085636f02 Downloading 136.3MB + 6e6085636f02 Downloading 137.4MB + 6e6085636f02 Downloading 138.4MB + 6e6085636f02 Downloading 140.5MB + 6e6085636f02 Downloading 141.6MB + 6e6085636f02 Downloading 143.7MB + 6e6085636f02 Downloading 144.7MB + 6e6085636f02 Downloading 145.8MB + 6e6085636f02 Downloading 147.8MB + 6e6085636f02 Downloading 148.9MB + 6e6085636f02 Downloading 149.9MB + 6e6085636f02 Downloading 152MB + 6e6085636f02 Downloading 153.1MB + 6e6085636f02 Downloading 155.2MB + 6e6085636f02 Downloading 156.2MB + 6e6085636f02 Downloading 157.3MB + 6e6085636f02 Downloading 159.4MB + 6e6085636f02 Downloading 160.4MB + 6e6085636f02 Downloading 161.7MB + 6e6085636f02 Downloading 163.6MB + 6e6085636f02 Downloading 164.6MB + 6e6085636f02 Downloading 166.7MB + 6e6085636f02 Downloading 167.8MB + 6e6085636f02 Downloading 169.9MB + 6e6085636f02 Downloading 170.9MB + 6e6085636f02 Downloading 172MB + 6e6085636f02 Downloading 174.1MB + 6e6085636f02 Downloading 175.1MB + 6e6085636f02 Downloading 176.2MB + 6e6085636f02 Downloading 178.3MB + 6e6085636f02 Downloading 179.3MB + 6e6085636f02 Downloading 180.4MB + 6e6085636f02 Downloading 182.5MB + 6e6085636f02 Downloading 183.5MB + 6e6085636f02 Downloading 184.5MB + 6e6085636f02 Downloading 186.6MB + 6e6085636f02 Downloading 187.7MB + 6e6085636f02 Downloading 189.8MB + 6e6085636f02 Downloading 190.8MB + 6e6085636f02 Downloading 191.9MB + 6e6085636f02 Downloading 194MB + 6e6085636f02 Downloading 195MB + 6e6085636f02 Downloading 196.1MB + 6e6085636f02 Downloading 198.2MB + 6e6085636f02 Downloading 199.2MB + 6e6085636f02 Downloading 201.3MB + 6e6085636f02 Downloading 202.4MB + 6e6085636f02 Downloading 203.4MB + 6e6085636f02 Downloading 205.5MB + 6e6085636f02 Downloading 206.6MB + 6e6085636f02 Downloading 208.7MB + 6e6085636f02 Downloading 209.7MB + 6e6085636f02 Downloading 210.8MB + 6e6085636f02 Downloading 212.9MB + 6e6085636f02 Downloading 213.9MB + 6e6085636f02 Downloading 216MB + 6e6085636f02 Downloading 217.1MB + 6e6085636f02 Downloading 218.1MB + 6e6085636f02 Downloading 220.2MB + 6e6085636f02 Downloading 221.2MB + 6e6085636f02 Downloading 223.3MB + 6e6085636f02 Downloading 224.4MB + 6e6085636f02 Downloading 225.4MB + 6e6085636f02 Downloading 227.5MB + 6e6085636f02 Downloading 228.6MB + 6e6085636f02 Downloading 230.2MB + 6e6085636f02 Downloading 231.7MB + 6e6085636f02 Downloading 232.8MB + 6e6085636f02 Downloading 234.9MB + 6e6085636f02 Downloading 235.9MB + 6e6085636f02 Downloading 237MB + 6e6085636f02 Downloading 239.1MB + 6e6085636f02 Downloading 240.1MB + 6e6085636f02 Downloading 241.2MB + 6e6085636f02 Downloading 243.3MB + 6e6085636f02 Downloading 244.3MB + 6e6085636f02 Downloading 246.4MB + 6e6085636f02 Downloading 247.5MB + 6e6085636f02 Downloading 248.5MB + 6e6085636f02 Downloading 250.6MB + 6e6085636f02 Downloading 251.7MB + 6e6085636f02 Downloading 253.8MB + 6e6085636f02 Downloading 254.8MB + 6e6085636f02 Downloading 255.9MB + 6e6085636f02 Downloading 257.9MB + 6e6085636f02 Downloading 259MB + 6e6085636f02 Downloading 260MB + 6e6085636f02 Downloading 262.1MB + 6e6085636f02 Downloading 263.2MB + 6e6085636f02 Downloading 264.2MB + 6e6085636f02 Downloading 266.3MB + 6e6085636f02 Downloading 267.4MB + 6e6085636f02 Downloading 269.5MB + 6e6085636f02 Downloading 270.5MB + 6e6085636f02 Downloading 271.6MB + 6e6085636f02 Downloading 273.7MB + 6e6085636f02 Downloading 274.7MB + 6e6085636f02 Downloading 276.8MB + 6e6085636f02 Downloading 277.9MB + 6e6085636f02 Downloading 278.9MB + 6e6085636f02 Downloading 281MB + 6e6085636f02 Downloading 282.1MB + 6e6085636f02 Downloading 283.1MB + 6e6085636f02 Downloading 285.2MB + 6e6085636f02 Downloading 286.3MB + 6e6085636f02 Downloading 287.3MB + 6e6085636f02 Downloading 289.4MB + 6e6085636f02 Downloading 290.5MB + 6e6085636f02 Downloading 291.5MB + 6e6085636f02 Downloading 293.6MB + 6e6085636f02 Downloading 294.6MB + 6e6085636f02 Downloading 296.7MB + 6e6085636f02 Downloading 297.8MB + 6e6085636f02 Downloading 298.8MB + 6e6085636f02 Downloading 300.9MB + 6e6085636f02 Downloading 302MB + 6e6085636f02 Downloading 304.1MB + 6e6085636f02 Downloading 305.1MB + 6e6085636f02 Downloading 306.2MB + 6e6085636f02 Downloading 308.3MB + 6e6085636f02 Downloading 309.3MB + 6e6085636f02 Downloading 310.4MB + 6e6085636f02 Downloading 312.5MB + 6e6085636f02 Downloading 313.5MB + 6e6085636f02 Downloading 315.6MB + 6e6085636f02 Downloading 316.7MB + 6e6085636f02 Downloading 317.7MB + 6e6085636f02 Downloading 319.8MB + 6e6085636f02 Downloading 320.9MB + 6e6085636f02 Downloading 321.9MB + 6e6085636f02 Downloading 324MB + 6e6085636f02 Downloading 325.1MB + 6e6085636f02 Downloading 326.1MB + 6e6085636f02 Downloading 327.2MB + 6e6085636f02 Downloading 329.3MB + 6e6085636f02 Downloading 330.3MB + 6e6085636f02 Downloading 332.4MB + 6e6085636f02 Downloading 333.4MB + 6e6085636f02 Downloading 334.5MB + 6e6085636f02 Downloading 336.6MB + 6e6085636f02 Downloading 337.6MB + 6e6085636f02 Downloading 338.9MB + 6e6085636f02 Download complete 0B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 1B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 2B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 3B + 6e6085636f02 Extracting 4B + 6e6085636f02 Extracting 4B + 6e6085636f02 Extracting 4B + 6e6085636f02 Extracting 4B + 5ed3d4d076b0 Extracting 1B + 6e6085636f02 Pull complete 0B + 5ed3d4d076b0 Extracting 1B + 4f4fb700ef54 Pull complete 0B + 7e079c586e0d Pull complete 0B + ffd68bafd674 Pull complete 0B + 5ed3d4d076b0 Pull complete 0B + Image grafana/grafana:12.2.0 Pulled + Image quicknotes:lab6 Building +#1 [internal] load local bake definitions +#1 reading from stdin 579B 0.0s done +#1 DONE 0.0s + +#2 [internal] load build definition from Dockerfile +#2 transferring dockerfile: 511B done +#2 DONE 0.0s + +#3 [auth] docker/dockerfile:pull token for registry-1.docker.io +#3 DONE 0.0s + +#4 resolve image config for docker-image://docker.io/docker/dockerfile:1 +#4 DONE 0.9s + +#5 docker-image://docker.io/docker/dockerfile:1@sha256:87999aa3d42bdc6bea60565083ee17e86d1f3339802f543c0d03998580f9cb89 +#5 resolve docker.io/docker/dockerfile:1@sha256:87999aa3d42bdc6bea60565083ee17e86d1f3339802f543c0d03998580f9cb89 0.0s done +#5 CACHED + +#6 [auth] library/golang:pull token for registry-1.docker.io +#6 DONE 0.0s + +#7 [internal] load metadata for docker.io/library/golang:1.24-alpine +#7 ... + +#8 [internal] load metadata for gcr.io/distroless/static-debian12:nonroot +#8 DONE 0.5s + +#7 [internal] load metadata for docker.io/library/golang:1.24-alpine +#7 DONE 0.8s + +#9 [internal] load .dockerignore +#9 transferring context: 2B done +#9 DONE 0.0s + +#10 [stage-1 1/4] FROM gcr.io/distroless/static-debian12:nonroot@sha256:d093aa3e30dbadd3efe1310db061a14da60299baff8450a17fe0ccc514a16639 +#10 resolve gcr.io/distroless/static-debian12:nonroot@sha256:d093aa3e30dbadd3efe1310db061a14da60299baff8450a17fe0ccc514a16639 0.0s done +#10 DONE 0.0s + +#11 [builder 1/6] FROM docker.io/library/golang:1.24-alpine@sha256:8bee1901f1e530bfb4a7850aa7a479d17ae3a18beb6e09064ed54cfd245b7191 +#11 resolve docker.io/library/golang:1.24-alpine@sha256:8bee1901f1e530bfb4a7850aa7a479d17ae3a18beb6e09064ed54cfd245b7191 0.0s done +#11 DONE 0.0s + +#12 [internal] load build context +#12 transferring context: 417B done +#12 DONE 0.0s + +#13 [builder 5/6] COPY . . +#13 CACHED + +#14 [builder 6/6] RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /out/quicknotes . +#14 CACHED + +#15 [builder 2/6] WORKDIR /src +#15 CACHED + +#16 [builder 3/6] COPY go.mod ./ +#16 CACHED + +#17 [builder 4/6] RUN go mod download +#17 CACHED + +#18 [stage-1 2/4] COPY --from=builder /out/quicknotes /quicknotes +#18 CACHED + +#19 [stage-1 3/4] COPY --from=builder /src/seed.json /seed.json +#19 CACHED + +#20 exporting to image +#20 exporting layers done +#20 exporting manifest sha256:cc0a1b82c078e97f645cd869854ba4b15dbb1349f74f06147a2e0ad988855bad done +#20 exporting config sha256:fff27ec54e113fc4132f61479b64cf3fa9d2f3a2854967b440ed615df9a10a0c done +#20 exporting attestation manifest sha256:228248d77938e85b6ee4e9a980818c715651ea71d1e2726fbcb620afc82696e0 0.0s done +#20 exporting manifest list sha256:e551544ec441144a727aa90cf180d1e904ec94fa2ba24addd90e15456bdd3812 done +#20 naming to docker.io/library/quicknotes:lab6 done +#20 unpacking to docker.io/library/quicknotes:lab6 done +#20 DONE 0.1s + +#21 resolving provenance for metadata file +#21 DONE 0.0s + Image quicknotes:lab6 Built + Network devops-intro_default Creating + Network devops-intro_default Created + Container devops-intro-quicknotes-init-1 Creating + Container devops-intro-quicknotes-init-1 Created + Container devops-intro-quicknotes-1 Creating + Container devops-intro-quicknotes-1 Created + Container devops-intro-prometheus-1 Creating + Container devops-intro-quicknotes-health-1 Creating + Container devops-intro-quicknotes-health-1 Created + Container devops-intro-prometheus-1 Created + Container devops-intro-grafana-1 Creating + Container devops-intro-grafana-1 Created + Container devops-intro-quicknotes-init-1 Starting + Container devops-intro-quicknotes-init-1 Started + Container devops-intro-quicknotes-init-1 Waiting + Container devops-intro-quicknotes-init-1 Exited + Container devops-intro-quicknotes-1 Starting + Container devops-intro-quicknotes-1 Started + Container devops-intro-quicknotes-health-1 Starting + Container devops-intro-prometheus-1 Starting + Container devops-intro-quicknotes-health-1 Started + Container devops-intro-prometheus-1 Started + Container devops-intro-grafana-1 Starting + Container devops-intro-grafana-1 Started diff --git a/submissions/src/lab08/grafana_health.txt b/submissions/src/lab08/grafana_health.txt new file mode 100644 index 000000000..33207497a Binary files /dev/null and b/submissions/src/lab08/grafana_health.txt differ diff --git a/submissions/src/lab08/prometheus_alerts_after_error_traffic.json b/submissions/src/lab08/prometheus_alerts_after_error_traffic.json new file mode 100644 index 000000000..89bfd8fae Binary files /dev/null and b/submissions/src/lab08/prometheus_alerts_after_error_traffic.json differ diff --git a/submissions/src/lab08/prometheus_alerts_initial.json b/submissions/src/lab08/prometheus_alerts_initial.json new file mode 100644 index 000000000..89bfd8fae Binary files /dev/null and b/submissions/src/lab08/prometheus_alerts_initial.json differ diff --git a/submissions/src/lab08/prometheus_ready.txt b/submissions/src/lab08/prometheus_ready.txt new file mode 100644 index 000000000..f7365da48 Binary files /dev/null and b/submissions/src/lab08/prometheus_ready.txt differ diff --git a/submissions/src/lab08/prometheus_rules.json b/submissions/src/lab08/prometheus_rules.json new file mode 100644 index 000000000..b42bc3f6f Binary files /dev/null and b/submissions/src/lab08/prometheus_rules.json differ diff --git a/submissions/src/lab08/prometheus_targets_raw.json b/submissions/src/lab08/prometheus_targets_raw.json new file mode 100644 index 000000000..5356a52af Binary files /dev/null and b/submissions/src/lab08/prometheus_targets_raw.json differ diff --git a/submissions/src/lab08/quicknotes_health.txt b/submissions/src/lab08/quicknotes_health.txt new file mode 100644 index 000000000..162bc5752 Binary files /dev/null and b/submissions/src/lab08/quicknotes_health.txt differ diff --git a/submissions/src/lab08/quicknotes_metrics.txt b/submissions/src/lab08/quicknotes_metrics.txt new file mode 100644 index 000000000..cd0a61ccc Binary files /dev/null and b/submissions/src/lab08/quicknotes_metrics.txt differ diff --git a/submissions/src/lab08/traffic_query.json b/submissions/src/lab08/traffic_query.json new file mode 100644 index 000000000..19f548b23 Binary files /dev/null and b/submissions/src/lab08/traffic_query.json differ diff --git a/submissions/src/lab09/go_test_security_headers.txt b/submissions/src/lab09/go_test_security_headers.txt new file mode 100644 index 000000000..a000afa1a Binary files /dev/null and b/submissions/src/lab09/go_test_security_headers.txt differ diff --git a/submissions/src/lab09/headers_after_fix.txt b/submissions/src/lab09/headers_after_fix.txt new file mode 100644 index 000000000..b265c8c72 Binary files /dev/null and b/submissions/src/lab09/headers_after_fix.txt differ diff --git a/submissions/src/lab09/sbom.json b/submissions/src/lab09/sbom.json new file mode 100644 index 000000000..ef114effb --- /dev/null +++ b/submissions/src/lab09/sbom.json @@ -0,0 +1,421 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:bd53073f-75ae-49b7-8e0c-9eebf7b1aadf", + "version": 1, + "metadata": { + "timestamp": "2026-07-07T17:13:49+00:00", + "tools": { + "components": [ + { + "type": "application", + "group": "aquasecurity", + "name": "trivy", + "version": "0.59.1" + } + ] + }, + "component": { + "bom-ref": "pkg:oci/quicknotes@sha256%3Ae551544ec441144a727aa90cf180d1e904ec94fa2ba24addd90e15456bdd3812?arch=amd64&repository_url=index.docker.io%2Flibrary%2Fquicknotes", + "type": "container", + "name": "quicknotes:lab6", + "purl": "pkg:oci/quicknotes@sha256%3Ae551544ec441144a727aa90cf180d1e904ec94fa2ba24addd90e15456bdd3812?arch=amd64&repository_url=index.docker.io%2Flibrary%2Fquicknotes", + "properties": [ + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:09e04cbee5b88f399cd69c1b95fe4cb3fe436731fe162da534496a39d95becb2" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:114dde0fefebbca13165d0da9c500a66190e497a82a53dcaabc3172d630be1e9" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:33b37ab0b0901175c2699d81c10b0c887f0dff944de74763cca00c155904d6fb" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:35b5ba6e08cdd034b8d1920f9c7203e5d66f258e1915f164a01de0af14f34710" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:4cde6b0bb6f50a5f255eef7b2a42162c661cf776b803225dcac9a659e396bb6b" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:4d049f83d9cf21d1f5cc0e11deaf36df02790d0e60c1a3829538fb4b61685368" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:6e7fbcf090d09b73a4aa85f9dce690d0ace73877c150b7c56955487b6e7a822a" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:6f1cdceb6a3146f0ccb986521156bef8a422cdbb0863396f7f751f575ba308f4" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:7e24cf34d751a00b6e425351118983027893f35a25279d4ec691e27afc451788" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:8fa10c0194df9b7c054c90dbe482585f768a54428fc90a5b78a0066a123b1bba" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:9ac0db7cd6b389001b18856f7926a18dff7f834c84523445e5030fd753f09245" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:ad51d0769d16ba578106a177987dfe3d2e02c1668c852b795b2f6b024068242a" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:af5aa97ebe6ce1604747ec1e21af7136ded391bcabe4acef882e718a87c86bcc" + }, + { + "name": "aquasecurity:trivy:DiffID", + "value": "sha256:bd3cdfae1d3fdd83a2231d608969b38b82349777c2fff9a7c12d54f8ac5c9b38" + }, + { + "name": "aquasecurity:trivy:ImageID", + "value": "sha256:e551544ec441144a727aa90cf180d1e904ec94fa2ba24addd90e15456bdd3812" + }, + { + "name": "aquasecurity:trivy:Labels:com.docker.compose.project", + "value": "devops-intro" + }, + { + "name": "aquasecurity:trivy:Labels:com.docker.compose.service", + "value": "quicknotes" + }, + { + "name": "aquasecurity:trivy:Labels:com.docker.compose.version", + "value": "5.1.0" + }, + { + "name": "aquasecurity:trivy:RepoDigest", + "value": "quicknotes@sha256:e551544ec441144a727aa90cf180d1e904ec94fa2ba24addd90e15456bdd3812" + }, + { + "name": "aquasecurity:trivy:RepoTag", + "value": "quicknotes:lab6" + }, + { + "name": "aquasecurity:trivy:SchemaVersion", + "value": "2" + } + ] + } + }, + "components": [ + { + "bom-ref": "84227aa2-01c8-4c49-88fc-3f7a3a85795f", + "type": "application", + "name": "quicknotes", + "properties": [ + { + "name": "aquasecurity:trivy:Class", + "value": "lang-pkgs" + }, + { + "name": "aquasecurity:trivy:Type", + "value": "gobinary" + } + ] + }, + { + "bom-ref": "e6b91ae5-22dd-4b76-a0ff-1129e3c996f1", + "type": "operating-system", + "name": "debian", + "version": "12.14", + "properties": [ + { + "name": "aquasecurity:trivy:Class", + "value": "os-pkgs" + }, + { + "name": "aquasecurity:trivy:Type", + "value": "debian" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/base-files@12.4%2Bdeb12u14?arch=amd64&distro=debian-12.14", + "type": "library", + "supplier": { + "name": "Santiago Vila " + }, + "name": "base-files", + "version": "12.4+deb12u14", + "licenses": [ + { + "license": { + "name": "GPL-2.0-or-later" + } + } + ], + "purl": "pkg:deb/debian/base-files@12.4%2Bdeb12u14?arch=amd64&distro=debian-12.14", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:9ac0db7cd6b389001b18856f7926a18dff7f834c84523445e5030fd753f09245" + }, + { + "name": "aquasecurity:trivy:LayerDigest", + "value": "sha256:875ea9878944f3daf508d0e1b87b39ebc1ecce3ccaf3ed11a3cc59f1852a540a" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "base-files@12.4+deb12u14" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "base-files" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "12.4+deb12u14" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/media-types@10.0.0?arch=all&distro=debian-12.14", + "type": "library", + "supplier": { + "name": "Mime-Support Packagers " + }, + "name": "media-types", + "version": "10.0.0", + "licenses": [ + { + "license": { + "name": "ad-hoc" + } + } + ], + "purl": "pkg:deb/debian/media-types@10.0.0?arch=all&distro=debian-12.14", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:114dde0fefebbca13165d0da9c500a66190e497a82a53dcaabc3172d630be1e9" + }, + { + "name": "aquasecurity:trivy:LayerDigest", + "value": "sha256:bf7a4185f01524837d19abde915ffde84e64368b250f5b5e9f6f75aea62a11d4" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "media-types@10.0.0" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "media-types" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "10.0.0" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/netbase@6.4?arch=all&distro=debian-12.14", + "type": "library", + "supplier": { + "name": "Marco d'Itri " + }, + "name": "netbase", + "version": "6.4", + "licenses": [ + { + "license": { + "name": "GPL-2.0-only" + } + } + ], + "purl": "pkg:deb/debian/netbase@6.4?arch=all&distro=debian-12.14", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:8fa10c0194df9b7c054c90dbe482585f768a54428fc90a5b78a0066a123b1bba" + }, + { + "name": "aquasecurity:trivy:LayerDigest", + "value": "sha256:990a9c434e5e0f11549a8d4a41a1991e621b04e30cd63269adbc97b1dc38fd7e" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "netbase@6.4" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "netbase" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "6.4" + } + ] + }, + { + "bom-ref": "pkg:deb/debian/tzdata@2026b-0%2Bdeb12u1?arch=all&distro=debian-12.14", + "type": "library", + "supplier": { + "name": "GNU Libc Maintainers " + }, + "name": "tzdata", + "version": "2026b-0+deb12u1", + "licenses": [ + { + "license": { + "name": "public-domain" + } + } + ], + "purl": "pkg:deb/debian/tzdata@2026b-0%2Bdeb12u1?arch=all&distro=debian-12.14", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:35b5ba6e08cdd034b8d1920f9c7203e5d66f258e1915f164a01de0af14f34710" + }, + { + "name": "aquasecurity:trivy:LayerDigest", + "value": "sha256:39dc083afc39bd8dc43d456fe7ff7d39292e593bb2769972c11bb2e5f9119386" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "tzdata@2026b-0+deb12u1" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "debian" + }, + { + "name": "aquasecurity:trivy:SrcName", + "value": "tzdata" + }, + { + "name": "aquasecurity:trivy:SrcRelease", + "value": "0+deb12u1" + }, + { + "name": "aquasecurity:trivy:SrcVersion", + "value": "2026b" + } + ] + }, + { + "bom-ref": "pkg:golang/quicknotes", + "type": "library", + "name": "quicknotes", + "purl": "pkg:golang/quicknotes", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:09e04cbee5b88f399cd69c1b95fe4cb3fe436731fe162da534496a39d95becb2" + }, + { + "name": "aquasecurity:trivy:LayerDigest", + "value": "sha256:7cd1eb76e4155825f4051766461b41d197d2707f2be0b83f6ed83daf0ccfda5b" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "quicknotes" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "gobinary" + } + ] + }, + { + "bom-ref": "pkg:golang/stdlib@v1.24.13", + "type": "library", + "name": "stdlib", + "version": "v1.24.13", + "purl": "pkg:golang/stdlib@v1.24.13", + "properties": [ + { + "name": "aquasecurity:trivy:LayerDiffID", + "value": "sha256:09e04cbee5b88f399cd69c1b95fe4cb3fe436731fe162da534496a39d95becb2" + }, + { + "name": "aquasecurity:trivy:LayerDigest", + "value": "sha256:7cd1eb76e4155825f4051766461b41d197d2707f2be0b83f6ed83daf0ccfda5b" + }, + { + "name": "aquasecurity:trivy:PkgID", + "value": "stdlib@v1.24.13" + }, + { + "name": "aquasecurity:trivy:PkgType", + "value": "gobinary" + } + ] + } + ], + "dependencies": [ + { + "ref": "84227aa2-01c8-4c49-88fc-3f7a3a85795f", + "dependsOn": [ + "pkg:golang/quicknotes" + ] + }, + { + "ref": "e6b91ae5-22dd-4b76-a0ff-1129e3c996f1", + "dependsOn": [ + "pkg:deb/debian/base-files@12.4%2Bdeb12u14?arch=amd64&distro=debian-12.14", + "pkg:deb/debian/media-types@10.0.0?arch=all&distro=debian-12.14", + "pkg:deb/debian/netbase@6.4?arch=all&distro=debian-12.14", + "pkg:deb/debian/tzdata@2026b-0%2Bdeb12u1?arch=all&distro=debian-12.14" + ] + }, + { + "ref": "pkg:deb/debian/base-files@12.4%2Bdeb12u14?arch=amd64&distro=debian-12.14", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/media-types@10.0.0?arch=all&distro=debian-12.14", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/netbase@6.4?arch=all&distro=debian-12.14", + "dependsOn": [] + }, + { + "ref": "pkg:deb/debian/tzdata@2026b-0%2Bdeb12u1?arch=all&distro=debian-12.14", + "dependsOn": [] + }, + { + "ref": "pkg:golang/quicknotes", + "dependsOn": [ + "pkg:golang/stdlib@v1.24.13" + ] + }, + { + "ref": "pkg:golang/stdlib@v1.24.13", + "dependsOn": [] + }, + { + "ref": "pkg:oci/quicknotes@sha256%3Ae551544ec441144a727aa90cf180d1e904ec94fa2ba24addd90e15456bdd3812?arch=amd64&repository_url=index.docker.io%2Flibrary%2Fquicknotes", + "dependsOn": [ + "84227aa2-01c8-4c49-88fc-3f7a3a85795f", + "e6b91ae5-22dd-4b76-a0ff-1129e3c996f1" + ] + } + ], + "vulnerabilities": [] +} diff --git a/submissions/src/lab09/trivy_config.txt b/submissions/src/lab09/trivy_config.txt new file mode 100644 index 000000000..7c074fa18 Binary files /dev/null and b/submissions/src/lab09/trivy_config.txt differ diff --git a/submissions/src/lab09/trivy_fs.txt b/submissions/src/lab09/trivy_fs.txt new file mode 100644 index 000000000..a0404613f Binary files /dev/null and b/submissions/src/lab09/trivy_fs.txt differ diff --git a/submissions/src/lab09/trivy_image.txt b/submissions/src/lab09/trivy_image.txt new file mode 100644 index 000000000..a45bda9d9 Binary files /dev/null and b/submissions/src/lab09/trivy_image.txt differ diff --git a/submissions/src/lab09/zap/report_after.html b/submissions/src/lab09/zap/report_after.html new file mode 100644 index 000000000..e8d34b78b --- /dev/null +++ b/submissions/src/lab09/zap/report_after.html @@ -0,0 +1,756 @@ + + + + +ZAP Scanning Report + + + +

+ + + ZAP Scanning Report +

+

+ + +

+ + Site: http://host.docker.internal:8080 + +

+ +

+ Generated on Tue, 7 Jul 2026 19:21:19 +

+ +

+ ZAP Version: 2.16.1 +

+ +

+ ZAP by Checkmarx +

+ + +

Summary of Alerts

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Risk LevelNumber of Alerts
+
High
+
+
0
+
+
Medium
+
+
0
+
+
Low
+
+
2
+
+
Informational
+
+
1
+
+
False Positives:
+
+
0
+
+
+ + + + +

Summary of Sequences

+

For each step: result (Pass/Fail) - risk (of highest alert(s) for the step, if any).

+ + + + + + +

Alerts

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameRisk LevelNumber of Instances
Insufficient Site Isolation Against Spectre VulnerabilityLow1
ZAP is Out of DateLow1
Non-Storable ContentInformational4
+
+ + + +

Alert Detail

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Low
Insufficient Site Isolation Against Spectre Vulnerability
Description +
Cross-Origin-Resource-Policy header is an opt-in header designed to counter side-channels attacks like Spectre. Resource should be specifically set as shareable amongst different origins.
+ +
URLhttp://host.docker.internal:8080/health
MethodGET
ParameterCross-Origin-Resource-Policy
Attack
Evidence
Other Info
Instances1
Solution +
Ensure that the application/web server sets the Cross-Origin-Resource-Policy header appropriately, and that it sets the Cross-Origin-Resource-Policy header to 'same-origin' for all web pages.
+
+ +
'same-site' is considered as less secured and should be avoided.
+
+ +
If resources must be shared, set the header to 'cross-origin'.
+
+ +
If possible, ensure that the end user uses a standards-compliant and modern web browser that supports the Cross-Origin-Resource-Policy header (https://caniuse.com/mdn-http_headers_cross-origin-resource-policy).
+ +
Reference + https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy + +
CWE Id693
WASC Id14
Plugin Id90004
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Low
ZAP is Out of Date
Description +
The version of ZAP you are using to test your app is out of date and is no longer being updated.
+
+ +
The risk level is set based on how out of date your ZAP version is.
+ +
URLhttp://host.docker.internal:8080/health
MethodGET
Parameter
Attack
Evidence
Other InfoThe latest version of ZAP is 2.17.0
Instances1
Solution +
Download the latest version of ZAP from https://www.zaproxy.org/download/ and install it.
+ +
Reference + https://www.zaproxy.org/download/ + +
CWE Id1104
WASC Id45
Plugin Id10116
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Informational
Non-Storable Content
Description +
The response contents are not storable by caching components such as proxy servers. If the response does not contain sensitive, personal or user-specific information, it may benefit from being stored and cached, to improve performance.
+ +
URLhttp://host.docker.internal:8080/
MethodGET
Parameter
Attack
Evidenceno-store
Other Info
URLhttp://host.docker.internal:8080/health
MethodGET
Parameter
Attack
Evidenceno-store
Other Info
URLhttp://host.docker.internal:8080/robots.txt
MethodGET
Parameter
Attack
Evidenceno-store
Other Info
URLhttp://host.docker.internal:8080/sitemap.xml
MethodGET
Parameter
Attack
Evidenceno-store
Other Info
Instances4
Solution +
The content may be marked as storable by ensuring that the following conditions are satisfied:
+
+ +
The request method must be understood by the cache and defined as being cacheable ("GET", "HEAD", and "POST" are currently defined as cacheable)
+
+ +
The response status code must be understood by the cache (one of the 1XX, 2XX, 3XX, 4XX, or 5XX response classes are generally understood)
+
+ +
The "no-store" cache directive must not appear in the request or response header fields
+
+ +
For caching by "shared" caches such as "proxy" caches, the "private" response directive must not appear in the response
+
+ +
For caching by "shared" caches such as "proxy" caches, the "Authorization" header field must not appear in the request, unless the response explicitly allows it (using one of the "must-revalidate", "public", or "s-maxage" Cache-Control response directives)
+
+ +
In addition to the conditions above, at least one of the following conditions must also be satisfied by the response:
+
+ +
It must contain an "Expires" header field
+
+ +
It must contain a "max-age" response directive
+
+ +
For "shared" caches such as "proxy" caches, it must contain a "s-maxage" response directive
+
+ +
It must contain a "Cache Control Extension" that allows it to be cached
+
+ +
It must have a status code that is defined as cacheable by default (200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501).
+ +
Reference + https://datatracker.ietf.org/doc/html/rfc7234 +
+ + https://datatracker.ietf.org/doc/html/rfc7231 +
+ + https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html + +
CWE Id524
WASC Id13
Plugin Id10049
+
+ + + + + +

Sequence Details

+ With the associated active scan results. + + + +
+ + + + + + + diff --git a/submissions/src/lab09/zap/report_after.json b/submissions/src/lab09/zap/report_after.json new file mode 100644 index 000000000..c31e7b40f --- /dev/null +++ b/submissions/src/lab09/zap/report_after.json @@ -0,0 +1,139 @@ +{ + "@programName": "ZAP", + "@version": "2.16.1", + "@generated": "Tue, 7 Jul 2026 19:21:19", + "created": "2026-07-07T19:21:19.625193076Z", + "site":[ + { + "@name": "http://host.docker.internal:8080", + "@host": "host.docker.internal", + "@port": "8080", + "@ssl": "false", + "alerts": [ + { + "pluginid": "90004", + "alertRef": "90004-1", + "alert": "Insufficient Site Isolation Against Spectre Vulnerability", + "name": "Insufficient Site Isolation Against Spectre Vulnerability", + "riskcode": "1", + "confidence": "2", + "riskdesc": "Low (Medium)", + "desc": "

Cross-Origin-Resource-Policy header is an opt-in header designed to counter side-channels attacks like Spectre. Resource should be specifically set as shareable amongst different origins.

", + "instances":[ + { + "id": "5", + "uri": "http://host.docker.internal:8080/health", + "nodeName": null, + "method": "GET", + "param": "Cross-Origin-Resource-Policy", + "attack": "", + "evidence": "", + "otherinfo": "" + } + ], + "count": "1", + "systemic": false, + "solution": "

Ensure that the application/web server sets the Cross-Origin-Resource-Policy header appropriately, and that it sets the Cross-Origin-Resource-Policy header to 'same-origin' for all web pages.

'same-site' is considered as less secured and should be avoided.

If resources must be shared, set the header to 'cross-origin'.

If possible, ensure that the end user uses a standards-compliant and modern web browser that supports the Cross-Origin-Resource-Policy header (https://caniuse.com/mdn-http_headers_cross-origin-resource-policy).

", + "otherinfo": "", + "reference": "

https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy

", + "cweid": "693", + "wascid": "14", + "sourceid": "1" + }, + { + "pluginid": "10116", + "alertRef": "10116", + "alert": "ZAP is Out of Date", + "name": "ZAP is Out of Date", + "riskcode": "1", + "confidence": "3", + "riskdesc": "Low (High)", + "desc": "

The version of ZAP you are using to test your app is out of date and is no longer being updated.

The risk level is set based on how out of date your ZAP version is.

", + "instances":[ + { + "id": "6", + "uri": "http://host.docker.internal:8080/health", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "", + "otherinfo": "The latest version of ZAP is 2.17.0" + } + ], + "count": "1", + "systemic": false, + "solution": "

Download the latest version of ZAP from https://www.zaproxy.org/download/ and install it.

", + "otherinfo": "

The latest version of ZAP is 2.17.0

", + "reference": "

https://www.zaproxy.org/download/

", + "cweid": "1104", + "wascid": "45", + "sourceid": "9" + }, + { + "pluginid": "10049", + "alertRef": "10049-1", + "alert": "Non-Storable Content", + "name": "Non-Storable Content", + "riskcode": "0", + "confidence": "2", + "riskdesc": "Informational (Medium)", + "desc": "

The response contents are not storable by caching components such as proxy servers. If the response does not contain sensitive, personal or user-specific information, it may benefit from being stored and cached, to improve performance.

", + "instances":[ + { + "id": "3", + "uri": "http://host.docker.internal:8080/", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "no-store", + "otherinfo": "" + }, + { + "id": "1", + "uri": "http://host.docker.internal:8080/health", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "no-store", + "otherinfo": "" + }, + { + "id": "2", + "uri": "http://host.docker.internal:8080/robots.txt", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "no-store", + "otherinfo": "" + }, + { + "id": "4", + "uri": "http://host.docker.internal:8080/sitemap.xml", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "no-store", + "otherinfo": "" + } + ], + "count": "4", + "systemic": false, + "solution": "

The content may be marked as storable by ensuring that the following conditions are satisfied:

The request method must be understood by the cache and defined as being cacheable (\"GET\", \"HEAD\", and \"POST\" are currently defined as cacheable)

The response status code must be understood by the cache (one of the 1XX, 2XX, 3XX, 4XX, or 5XX response classes are generally understood)

The \"no-store\" cache directive must not appear in the request or response header fields

For caching by \"shared\" caches such as \"proxy\" caches, the \"private\" response directive must not appear in the response

For caching by \"shared\" caches such as \"proxy\" caches, the \"Authorization\" header field must not appear in the request, unless the response explicitly allows it (using one of the \"must-revalidate\", \"public\", or \"s-maxage\" Cache-Control response directives)

In addition to the conditions above, at least one of the following conditions must also be satisfied by the response:

It must contain an \"Expires\" header field

It must contain a \"max-age\" response directive

For \"shared\" caches such as \"proxy\" caches, it must contain a \"s-maxage\" response directive

It must contain a \"Cache Control Extension\" that allows it to be cached

It must have a status code that is defined as cacheable by default (200, 203, 204, 206, 300, 301, 404, 405, 410, 414, 501).

", + "otherinfo": "", + "reference": "

https://datatracker.ietf.org/doc/html/rfc7234

https://datatracker.ietf.org/doc/html/rfc7231

https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html

", + "cweid": "524", + "wascid": "13", + "sourceid": "3" + } + ] + } + ], + "sequences":[ + ] + +} diff --git a/submissions/src/lab09/zap/report_before.html b/submissions/src/lab09/zap/report_before.html new file mode 100644 index 000000000..1ff144de8 --- /dev/null +++ b/submissions/src/lab09/zap/report_before.html @@ -0,0 +1,703 @@ + + + + +ZAP Scanning Report + + + +

+ + + ZAP Scanning Report +

+

+ + +

+ + Site: http://host.docker.internal:8080 + +

+ +

+ Generated on Tue, 7 Jul 2026 18:04:37 +

+ +

+ ZAP Version: 2.16.1 +

+ +

+ ZAP by Checkmarx +

+ + +

Summary of Alerts

+ + + + + + + + + + + + + + + + + + + + + + + + + +
Risk LevelNumber of Alerts
+
High
+
+
0
+
+
Medium
+
+
0
+
+
Low
+
+
2
+
+
Informational
+
+
1
+
+
False Positives:
+
+
0
+
+
+ + + + +

Summary of Sequences

+

For each step: result (Pass/Fail) - risk (of highest alert(s) for the step, if any).

+ + + + + + +

Alerts

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameRisk LevelNumber of Instances
Insufficient Site Isolation Against Spectre VulnerabilityLow1
ZAP is Out of DateLow1
Storable and Cacheable ContentInformational3
+
+ + + +

Alert Detail

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Low
Insufficient Site Isolation Against Spectre Vulnerability
Description +
Cross-Origin-Resource-Policy header is an opt-in header designed to counter side-channels attacks like Spectre. Resource should be specifically set as shareable amongst different origins.
+ +
URLhttp://host.docker.internal:8080/health
MethodGET
ParameterCross-Origin-Resource-Policy
Attack
Evidence
Other Info
Instances1
Solution +
Ensure that the application/web server sets the Cross-Origin-Resource-Policy header appropriately, and that it sets the Cross-Origin-Resource-Policy header to 'same-origin' for all web pages.
+
+ +
'same-site' is considered as less secured and should be avoided.
+
+ +
If resources must be shared, set the header to 'cross-origin'.
+
+ +
If possible, ensure that the end user uses a standards-compliant and modern web browser that supports the Cross-Origin-Resource-Policy header (https://caniuse.com/mdn-http_headers_cross-origin-resource-policy).
+ +
Reference + https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy + +
CWE Id693
WASC Id14
Plugin Id90004
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Low
ZAP is Out of Date
Description +
The version of ZAP you are using to test your app is out of date and is no longer being updated.
+
+ +
The risk level is set based on how out of date your ZAP version is.
+ +
URLhttp://host.docker.internal:8080/
MethodGET
Parameter
Attack
Evidence
Other InfoThe latest version of ZAP is 2.17.0
Instances1
Solution +
Download the latest version of ZAP from https://www.zaproxy.org/download/ and install it.
+ +
Reference + https://www.zaproxy.org/download/ + +
CWE Id1104
WASC Id45
Plugin Id10116
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
Informational
Storable and Cacheable Content
Description +
The response contents are storable by caching components such as proxy servers, and may be retrieved directly from the cache, rather than from the origin server by the caching servers, in response to similar requests from other users. If the response data is sensitive, personal or user-specific, this may result in sensitive information being leaked. In some cases, this may even result in a user gaining complete control of the session of another user, depending on the configuration of the caching components in use in their environment. This is primarily an issue where "shared" caching servers such as "proxy" caches are configured on the local network. This configuration is typically found in corporate or educational environments, for instance.
+ +
URLhttp://host.docker.internal:8080/
MethodGET
Parameter
Attack
Evidence
Other InfoIn the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234.
URLhttp://host.docker.internal:8080/health
MethodGET
Parameter
Attack
Evidence
Other InfoIn the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234.
URLhttp://host.docker.internal:8080/sitemap.xml
MethodGET
Parameter
Attack
Evidence
Other InfoIn the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234.
Instances3
Solution +
Validate that the response does not contain sensitive, personal or user-specific information. If it does, consider the use of the following HTTP response headers, to limit, or prevent the content being stored and retrieved from the cache by another user:
+
+ +
Cache-Control: no-cache, no-store, must-revalidate, private
+
+ +
Pragma: no-cache
+
+ +
Expires: 0
+
+ +
This configuration directs both HTTP 1.0 and HTTP 1.1 compliant caching servers to not store the response, and to not retrieve the response (without validation) from the cache, in response to a similar request.
+ +
Reference + https://datatracker.ietf.org/doc/html/rfc7234 +
+ + https://datatracker.ietf.org/doc/html/rfc7231 +
+ + https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html + +
CWE Id524
WASC Id13
Plugin Id10049
+
+ + + + + +

Sequence Details

+ With the associated active scan results. + + + +
+ + + + + + + diff --git a/submissions/src/lab09/zap/report_before.json b/submissions/src/lab09/zap/report_before.json new file mode 100644 index 000000000..47107bbc5 --- /dev/null +++ b/submissions/src/lab09/zap/report_before.json @@ -0,0 +1,129 @@ +{ + "@programName": "ZAP", + "@version": "2.16.1", + "@generated": "Tue, 7 Jul 2026 18:04:38", + "created": "2026-07-07T18:04:38.009629888Z", + "site":[ + { + "@name": "http://host.docker.internal:8080", + "@host": "host.docker.internal", + "@port": "8080", + "@ssl": "false", + "alerts": [ + { + "pluginid": "90004", + "alertRef": "90004-1", + "alert": "Insufficient Site Isolation Against Spectre Vulnerability", + "name": "Insufficient Site Isolation Against Spectre Vulnerability", + "riskcode": "1", + "confidence": "2", + "riskdesc": "Low (Medium)", + "desc": "

Cross-Origin-Resource-Policy header is an opt-in header designed to counter side-channels attacks like Spectre. Resource should be specifically set as shareable amongst different origins.

", + "instances":[ + { + "id": "9", + "uri": "http://host.docker.internal:8080/health", + "nodeName": null, + "method": "GET", + "param": "Cross-Origin-Resource-Policy", + "attack": "", + "evidence": "", + "otherinfo": "" + } + ], + "count": "1", + "systemic": false, + "solution": "

Ensure that the application/web server sets the Cross-Origin-Resource-Policy header appropriately, and that it sets the Cross-Origin-Resource-Policy header to 'same-origin' for all web pages.

'same-site' is considered as less secured and should be avoided.

If resources must be shared, set the header to 'cross-origin'.

If possible, ensure that the end user uses a standards-compliant and modern web browser that supports the Cross-Origin-Resource-Policy header (https://caniuse.com/mdn-http_headers_cross-origin-resource-policy).

", + "otherinfo": "", + "reference": "

https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy

", + "cweid": "693", + "wascid": "14", + "sourceid": "10" + }, + { + "pluginid": "10116", + "alertRef": "10116", + "alert": "ZAP is Out of Date", + "name": "ZAP is Out of Date", + "riskcode": "1", + "confidence": "3", + "riskdesc": "Low (High)", + "desc": "

The version of ZAP you are using to test your app is out of date and is no longer being updated.

The risk level is set based on how out of date your ZAP version is.

", + "instances":[ + { + "id": "5", + "uri": "http://host.docker.internal:8080/", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "", + "otherinfo": "The latest version of ZAP is 2.17.0" + } + ], + "count": "1", + "systemic": false, + "solution": "

Download the latest version of ZAP from https://www.zaproxy.org/download/ and install it.

", + "otherinfo": "

The latest version of ZAP is 2.17.0

", + "reference": "

https://www.zaproxy.org/download/

", + "cweid": "1104", + "wascid": "45", + "sourceid": "3" + }, + { + "pluginid": "10049", + "alertRef": "10049-3", + "alert": "Storable and Cacheable Content", + "name": "Storable and Cacheable Content", + "riskcode": "0", + "confidence": "2", + "riskdesc": "Informational (Medium)", + "desc": "

The response contents are storable by caching components such as proxy servers, and may be retrieved directly from the cache, rather than from the origin server by the caching servers, in response to similar requests from other users. If the response data is sensitive, personal or user-specific, this may result in sensitive information being leaked. In some cases, this may even result in a user gaining complete control of the session of another user, depending on the configuration of the caching components in use in their environment. This is primarily an issue where \"shared\" caching servers such as \"proxy\" caches are configured on the local network. This configuration is typically found in corporate or educational environments, for instance.

", + "instances":[ + { + "id": "3", + "uri": "http://host.docker.internal:8080/", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "", + "otherinfo": "In the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234." + }, + { + "id": "7", + "uri": "http://host.docker.internal:8080/health", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "", + "otherinfo": "In the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234." + }, + { + "id": "1", + "uri": "http://host.docker.internal:8080/sitemap.xml", + "nodeName": null, + "method": "GET", + "param": "", + "attack": "", + "evidence": "", + "otherinfo": "In the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234." + } + ], + "count": "3", + "systemic": false, + "solution": "

Validate that the response does not contain sensitive, personal or user-specific information. If it does, consider the use of the following HTTP response headers, to limit, or prevent the content being stored and retrieved from the cache by another user:

Cache-Control: no-cache, no-store, must-revalidate, private

Pragma: no-cache

Expires: 0

This configuration directs both HTTP 1.0 and HTTP 1.1 compliant caching servers to not store the response, and to not retrieve the response (without validation) from the cache, in response to a similar request.

", + "otherinfo": "

In the absence of an explicitly specified caching lifetime directive in the response, a liberal lifetime heuristic of 1 year was assumed. This is permitted by rfc7234.

", + "reference": "

https://datatracker.ietf.org/doc/html/rfc7234

https://datatracker.ietf.org/doc/html/rfc7231

https://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html

", + "cweid": "524", + "wascid": "13", + "sourceid": "9" + } + ] + } + ], + "sequences":[ + ] + +} diff --git a/submissions/src/lab09/zap/urls.txt b/submissions/src/lab09/zap/urls.txt new file mode 100644 index 000000000..1eecd3b88 --- /dev/null +++ b/submissions/src/lab09/zap/urls.txt @@ -0,0 +1,3 @@ +http://host.docker.internal:8080/health +http://host.docker.internal:8080/notes +http://host.docker.internal:8080/metrics \ No newline at end of file diff --git a/submissions/src/lab09/zap/zap.yaml b/submissions/src/lab09/zap/zap.yaml new file mode 100644 index 000000000..c10d3c86b --- /dev/null +++ b/submissions/src/lab09/zap/zap.yaml @@ -0,0 +1,41 @@ +env: + contexts: + - excludePaths: [] + name: baseline + urls: + - http://host.docker.internal:8080/health + - http://host.docker.internal:8080/ + parameters: + failOnError: true + progressToStdout: false +jobs: +- parameters: + enableTags: false + maxAlertsPerRule: 10 + type: passiveScan-config +- parameters: + maxDuration: 1 + url: http://host.docker.internal:8080/ + type: spider +- parameters: + maxDuration: 0 + type: passiveScan-wait +- parameters: + format: Long + summaryFile: /home/zap/zap_out.json + rules: [] + type: outputSummary +- parameters: + reportDescription: '' + reportDir: /zap/wrk/ + reportFile: report_after.html + reportTitle: ZAP Scanning Report + template: traditional-html + type: report +- parameters: + reportDescription: '' + reportDir: /zap/wrk/ + reportFile: report_after.json + reportTitle: ZAP Scanning Report + template: traditional-json + type: report diff --git a/submissions/src/screenshots/lab01/bonus_task_failed_push.png b/submissions/src/screenshots/lab01/bonus_task_failed_push.png new file mode 100644 index 000000000..f5dca9d8b Binary files /dev/null and b/submissions/src/screenshots/lab01/bonus_task_failed_push.png differ diff --git a/submissions/src/screenshots/lab01/bonus_task_rulesets.png b/submissions/src/screenshots/lab01/bonus_task_rulesets.png new file mode 100644 index 000000000..ddc2d2f33 Binary files /dev/null and b/submissions/src/screenshots/lab01/bonus_task_rulesets.png differ diff --git a/submissions/src/screenshots/lab01/task1_verified_commit_screenshot.png b/submissions/src/screenshots/lab01/task1_verified_commit_screenshot.png new file mode 100644 index 000000000..2bfaf1bc6 Binary files /dev/null and b/submissions/src/screenshots/lab01/task1_verified_commit_screenshot.png differ diff --git a/submissions/src/screenshots/lab01/task2_pr_template.png b/submissions/src/screenshots/lab01/task2_pr_template.png new file mode 100644 index 000000000..3375914bc Binary files /dev/null and b/submissions/src/screenshots/lab01/task2_pr_template.png differ diff --git a/submissions/src/screenshots/lab03/branch_protection.png b/submissions/src/screenshots/lab03/branch_protection.png new file mode 100644 index 000000000..ae730b5d3 Binary files /dev/null and b/submissions/src/screenshots/lab03/branch_protection.png differ diff --git a/submissions/src/screenshots/lab03/failed_ci.png b/submissions/src/screenshots/lab03/failed_ci.png new file mode 100644 index 000000000..bd93868f2 Binary files /dev/null and b/submissions/src/screenshots/lab03/failed_ci.png differ diff --git a/submissions/src/screenshots/lab03/green_ci.png b/submissions/src/screenshots/lab03/green_ci.png new file mode 100644 index 000000000..60d1632a5 Binary files /dev/null and b/submissions/src/screenshots/lab03/green_ci.png differ diff --git a/submissions/src/screenshots/lab03/merge_blocked.png b/submissions/src/screenshots/lab03/merge_blocked.png new file mode 100644 index 000000000..116e52789 Binary files /dev/null and b/submissions/src/screenshots/lab03/merge_blocked.png differ diff --git a/submissions/src/screenshots/lab03/timing_baseline.png b/submissions/src/screenshots/lab03/timing_baseline.png new file mode 100644 index 000000000..3ff485eb2 Binary files /dev/null and b/submissions/src/screenshots/lab03/timing_baseline.png differ diff --git a/submissions/src/screenshots/lab03/timing_cache.png b/submissions/src/screenshots/lab03/timing_cache.png new file mode 100644 index 000000000..50c4fa5b6 Binary files /dev/null and b/submissions/src/screenshots/lab03/timing_cache.png differ diff --git a/submissions/src/screenshots/lab03/timing_matrix.png b/submissions/src/screenshots/lab03/timing_matrix.png new file mode 100644 index 000000000..f79a630cb Binary files /dev/null and b/submissions/src/screenshots/lab03/timing_matrix.png differ diff --git a/submissions/src/screenshots/lab04/certificate_chain.png b/submissions/src/screenshots/lab04/certificate_chain.png new file mode 100644 index 000000000..016141ae9 Binary files /dev/null and b/submissions/src/screenshots/lab04/certificate_chain.png differ diff --git a/submissions/src/screenshots/lab04/client_hello.png b/submissions/src/screenshots/lab04/client_hello.png new file mode 100644 index 000000000..c502fd73c Binary files /dev/null and b/submissions/src/screenshots/lab04/client_hello.png differ diff --git a/submissions/src/screenshots/lab04/server_hello.png b/submissions/src/screenshots/lab04/server_hello.png new file mode 100644 index 000000000..6d62c9e69 Binary files /dev/null and b/submissions/src/screenshots/lab04/server_hello.png differ diff --git a/submissions/src/screenshots/lab08/alert_firing.png b/submissions/src/screenshots/lab08/alert_firing.png new file mode 100644 index 000000000..f6b1b842d Binary files /dev/null and b/submissions/src/screenshots/lab08/alert_firing.png differ diff --git a/submissions/src/screenshots/lab08/alert_initial.png b/submissions/src/screenshots/lab08/alert_initial.png new file mode 100644 index 000000000..7c0fe8cd8 Binary files /dev/null and b/submissions/src/screenshots/lab08/alert_initial.png differ diff --git a/submissions/src/screenshots/lab08/alert_pending.png b/submissions/src/screenshots/lab08/alert_pending.png new file mode 100644 index 000000000..433507942 Binary files /dev/null and b/submissions/src/screenshots/lab08/alert_pending.png differ diff --git a/submissions/src/screenshots/lab08/grafana_dashboard.png b/submissions/src/screenshots/lab08/grafana_dashboard.png new file mode 100644 index 000000000..a6488671f Binary files /dev/null and b/submissions/src/screenshots/lab08/grafana_dashboard.png differ diff --git a/submissions/src/screenshots/lab08/prometheus_targets.png b/submissions/src/screenshots/lab08/prometheus_targets.png new file mode 100644 index 000000000..76486c323 Binary files /dev/null and b/submissions/src/screenshots/lab08/prometheus_targets.png differ diff --git a/submissions/src/screenshots/lab09/zap_report_after.png b/submissions/src/screenshots/lab09/zap_report_after.png new file mode 100644 index 000000000..5272160ef Binary files /dev/null and b/submissions/src/screenshots/lab09/zap_report_after.png differ diff --git a/submissions/src/screenshots/lab09/zap_report_before.png b/submissions/src/screenshots/lab09/zap_report_before.png new file mode 100644 index 000000000..554a679c5 Binary files /dev/null and b/submissions/src/screenshots/lab09/zap_report_before.png differ