Author SHA1 Message Date
Payton Bates da342d40dd add payton's sections to chapter 9 2026-10-15 23:35:33 -04:00
Payton Bates 3eac4a694f add payton's sections to chapter 9; remove unnecessary sections/screenshots, reformat as necessary 2026-10-15 23:35:22 -04:00
Payton Bates 89359a6ce3 feat: architecture diagram and operations explanation 2026-10-15 22:26:18 -04:00
payton 1d7c528795 Merge pull request 'Add repo README, update Gitea README, add Caddy HTTPS proxy' (#7) from payton/caddy-https into main
Reviewed-on: http://localhost:3000/Inviscid/SSW590-DevOps/pulls/7
Reviewed-by: victor <vdelriof@stevens.edu>
2026-10-16 01:22:25 +00:00
Payton Bates 0cd8561d44 Add repo README, update Gitea README, add Caddy HTTPS proxy 2026-10-15 20:47:31 -04:00
payton aeba4fe443 Merge pull request 'Document initial Gitea deployment and team access' (#2) from codex/gitea-foundation-docs into main
Reviewed-on: http://localhost:3000/Inviscid/SSW590-DevOps/pulls/2
Reviewed-by: payton <pbates@stevens.edu>
2026-10-15 23:34:29 +00:00
MasterDefault 64fd3da0fd Document domain registration, DNS verification, and Gitea workflow 2026-10-15 19:29:49 -04:00
MasterDefault da6a369420 Document initial Gitea deployment and team access; refs #1 2026-10-15 18:49:00 -04:00
MasterDefault 28070817c2 Add initial Gitea deployment and setup evidence 2026-10-12 19:38:16 -04:00
Payton Bates c9376c4275 fix: set style file to devopsGlossary so Overleaf renders the index 2026-10-06 20:39:22 -04:00
Payton Bates 96de055b12 Logged AI usage for the LaTeX Docker chapter 2026-10-06 20:35:10 -04:00
MasterDefault 13bce6144c project proposal 2026-10-06 19:12:47 -04:00
Payton Bates c9d06e0b5a feat: Add LaTeX Docker chapter; update metadata and previous chapters 2026-10-05 18:56:10 -04:00
Payton Bates 02189faab0 chore: tidiness 2026-10-05 14:49:39 -04:00
MasterDefault 916ee92f90 EC2 website & project proposal init 2026-10-05 12:06:04 -04:00
Payton Bates 8af93058df fix: Run makeglossaries whenever relevant input files change 2026-10-04 01:54:03 -04:00
Payton Bates 2d30049c4e feat: Add Victor's outputs and screenshots for Linux problem set 2026-09-28 17:33:46 -04:00
Payton Bates 07f65b10e9 feat: Add initial chapters and revisions for Assignment 2 2026-09-26 19:10:32 -04:00
65 changed files with 2106 additions and 88 deletions

No files matched your search

+8
View File
@@ -0,0 +1,8 @@
# Normalize text files to LF in the repo and working tree (matches Overleaf).
* text=auto eol=lf
# Binary assets: never convert.
*.png binary
*.jpg binary
*.jpeg binary
*.pdf binary
+4 -1
View File
@@ -21,4 +21,7 @@
*.lof *.lof
*.lot *.lot
*.lol *.lol
*.pdf *.pdf
!figures/*.pdf
*.data.minted
_minted*/
+58
View File
@@ -0,0 +1,58 @@
# Inviscid
Inviscid is a proposed startup that helps small businesses monitor the health
and performance of their web applications. Customers would use a dashboard to
see availability, response times, request volume, and error rates, paired with
plain-language status summaries such as "Your website is responding slowly."
The semester prototype targets one demonstration customer application, building
on the team's existing Node.js website. Monitoring, the dashboard,
and deployment automation are **planned work, not completed features**. See the
Project Proposal chapter of the manual for the full plan and the Prometheus vs.
VictoriaMetrics comparison.
## Repository contents
| Path | Contents |
|---|---|
| `devopsAssignment.tex`, `chapters/`, `frontmatter/`, `backmatter/`, `figures/` | The team's LaTeX DevOps IT manual (SSW 590) |
| `docker/gitea/` | Versioned deployment files for the Gitea work hub (Compose, Caddyfile, deployment README) |
| `docker/color-buttons/` | Color Buttons demonstration app (Node.js/Express, Dockerfile) |
| `docker/latex-compiler/` | Docker image for compiling the manual |
## Links
- Gitea work hub: <https://git.inviscid.dev/>
- This repository: <https://git.inviscid.dev/Inviscid/SSW590-DevOps>
- Deployment documentation: [`docker/gitea/README.md`](docker/gitea/README.md)
## Building the manual
The manual uses `minted`, which needs shell escape and the `latexminted`
helper. Overleaf compiles it as-is. Locally:
```sh
latexmk -pdf devopsAssignment.tex
```
`latexmkrc` enables `-shell-escape` and runs `makeglossaries` automatically.
## Team
| Member | Gitea account | Role |
|---|---|---|
| Victor | `victor` | Organization owner; site administrator; domain, DNS, and Gitea deployment |
| Payton | `payton` | Organization owner; HTTPS, firewall, persistence testing, repository workflow |
## Workflow
1. Open an issue with a short description and acceptance criteria.
2. Create a branch from `main`.
3. Open a pull request into `main` and reference the issue with `Closes #N`.
4. Request a review from the other team member; merge only after approval.
5. Confirm the linked issue closed.
## Secrets
Never commit passwords, tokens, private keys, `app.ini`, database files, or
volume contents. The manual's Passwords table contains hints only.
+152 -3
View File
@@ -9,8 +9,10 @@ in the detail needed for a reviewer to see what was requested, what was
returned, and what the author changed. Assistance was used for interpreting the returned, and what the author changed. Assistance was used for interpreting the
assignment brief, drafting chapter text, and reviewing the document for internal assignment brief, drafting chapter text, and reviewing the document for internal
consistency. It was not used to generate command output, evidence, or any claim consistency. It was not used to generate command output, evidence, or any claim
about work that was not performed. No credential, key, or personal data was about work that was not performed. No passwords, access tokens, private keys, or recovery codes were supplied.
supplied to the tool. Every decision recorded below was made by the author; the assistant's role was The supplied coursework evidence includes author names, local usernames, host
names, and the local network details visible in the terminal captures.
Every decision recorded below was made by the authors; the assistant's role was
to propose and to check. to propose and to check.
\section{Records} \section{Records}
@@ -46,4 +48,151 @@ split of accounts into generated and derived classes.\\
\textbf{Human review and corrections:} The author determined the column policy \textbf{Human review and corrections:} The author determined the column policy
and work-in-progress criteria, and rejected a proposal to create a GitHub and work-in-progress criteria, and rejected a proposal to create a GitHub
organization as premature while teams remain unformalized. Much of the assistant's organization as premature while teams remain unformalized. Much of the assistant's
wording, structure, and ambitious ideas were rejected for overcomplication. wording, structure, and ambitious ideas were rejected for overcomplication.
\subsection{Record 3 --- Linux Commands}
\textbf{Date:} September 26, 2026.\\
\textbf{Tool and model:} Anthropic Claude (Opus 5).\\
\textbf{Task}: Draft a template for \ChapterRef{LinuxCommands} with appropriate formatting
for terminal inputs and outputs, and a structure for the problem set solutions. Revise
the entire document to reflect the new chapter and team formation.\\
\textbf{Inputs:} The document template and the Linux problem set.\\
\textbf{Output used:} A skeleton of the chapter with its sections, setup figure, and
the problem set headings for each question. Suggested edits to the metadata, abstract, and
introduction for Assignment~2 and the two-person team.\\
\textbf{Human review and corrections:} The authors refined the chapter structure to accommodate
rubric requirements and experimented with different figure formatting for terminal inputs and outputs.
\subsection{Record 4 --- Guided Execution and Evidence Integration}
\textbf{Dates:} September 27, 2026.\\
\textbf{Tool:} OpenAI Codex.\\
\textbf{Task:} Guide Victor through the Linux setup and Q1--Q40, check supplied
results against the assignment, review the group chapter, and integrate evidence
and concise explanations while preserving Payton's existing work.\\
\textbf{Inputs:} Assignment and problem-set PDFs, the group PDF, supplied
chapter and supporting LaTeX files, Victor's screenshots, and his terminal
transcript.\\
\textbf{Output used:} Command guidance; evidence checks; revised environment
description; per-question explanations; labeled terminal screenshots; Q15/Q20
clarifications; explicit Q38 assumptions; history and version updates.\\
\textbf{Human contribution and review:} Victor executed the commands and supplied
the resulting screenshots and transcript. Payton supplied the existing setup
figures and problem-set answers. Codex edited the documentation and checked it
against the recorded results; it did not generate substitute execution output.
The team remains responsible for reviewing the additions and compiling the
combined Overleaf project.\\
\subsection{Record 5 --- AWS Deployment and Proposal Draft}
\textbf{Dates:} October 1--5, 2026.\\
\textbf{Tool:} OpenAI Codex.\\
\textbf{Task:} Guide Victor through AWS account access, EC2 provisioning,
class-based website development, Docker installation and deployment, then draft
his chapter from the actual sources and screenshots.\\
\textbf{Human contribution:} Victor operated AWS, created the application
files, executed local and remote commands, resolved the repository-key failure
with guidance, and provided the execution screenshots. Payton is responsible
for the separate TeX Live compiler exercise and chapter. Both authors will
review the tool-comparison proposal.\\
\textbf{Output used:} Command guidance, source code, saved evidence, deployment
chapter, draft GitHub Actions/Jenkins proposal, metadata, and history updates.
Codex copied additions into the student's supplied course repository location.\\
\textbf{Review boundary:} Proposal selection requires team review. Payton's
compiler work was not present in the inspected repository. Full compilation,
combined report review, repository publication, and Canvas submission are not
claimed by this record.
\subsection{Record 6 --- LaTeX Docker Chapter}
\textbf{Dates:} October 5, 2026.\\
\textbf{Tool and model:} Anthropic Claude (Opus 5.5).\\
\textbf{Task:} Guide Payton through building a Docker container that compiles
a \LaTeX\ document with TeX Live and draft \ChapterRef{LatexDocker};
propose matching updates to the abstract, \ChapterRef{Introduction},
\ChapterRef{Hosts}, glossary, metadata, and document history.\\
\textbf{Inputs:} The assignment brief, Payton's terminal output and screenshots,
and Overleaf's TeX Live documentation.\\
\textbf{Output used:} The finalized Dockerfile, \texttt{.dockerignore}, and sample document;
the chapter draft; proposed replacement text for outdated sections.\\
\textbf{Human decisions and corrections:} Payton selected Debian's TeX Live packages,
built and ran the container under WSL, supplied the evidence screenshots,
and revised the chapter's organization and wording.\\
\textbf{Review boundary:} The assistant compiled the full report with its own
TeX Live installation to check the chapter; the image was built and
run only by Payton. All execution evidence was produced by Payton. The assistant
edited no repository files.
\subsection{Record 7 --- Inviscid Project Proposal}
\textbf{Date:} October 6, 2026.\\
\textbf{Tool:} OpenAI Codex.\\
\textbf{Task:} Brainstorm Inviscid, refine the metrics-tool comparison and
research question, and write the approved proposal into the course repository.\\
\textbf{Inputs:} The assignment outline, instructor clarification, Victor's
project preferences and corrections, current repository sources, and official
Prometheus and VictoriaMetrics documentation.\\
\textbf{Output used:} Project description, sample task, tentative tool choices,
research question, planned comparison, independent SWOT assessments, and
supporting document updates.\\
\textbf{Human decisions and corrections:} Victor approved the monitoring-service
concept, selected VictoriaMetrics instead of InfluxDB, approved the research
question, and requested independent SWOT assessments and candidate tools
explicitly marked undecided.\\
\textbf{Review boundary:} The monitoring prototype, tool comparison, and
pipeline remain proposed work. No performance results or completed monitoring
features are claimed. The combined report still requires final author review
and compilation before submission.
\subsection{Record 8 --- Gitea Foundation and Evidence Draft}
\textbf{Dates:} October 12--15, 2026.\\
\textbf{Tool:} OpenAI Codex.\\
\textbf{Task:} Guide Victor through Student Pack application, Docker/SQLite
Gitea setup, SSH tunneling, Elastic IP association, and Payton's key access;
prepare the pre-PR documentation from supplied execution evidence.\\
\textbf{Human contribution:} Victor operated the consoles and terminals,
created accounts, supplied screenshots, and reported Payton's successful SSH
connection. Payton's remaining work was not executed by the assistant.\\
\textbf{Output used:} Non-secret Compose configuration, deployment notes,
saved screenshots, Gitea chapter draft, and matching inventory, account-hint,
history, and metadata updates.\\
\textbf{Corrections and limits:} Documented benefit activation separately from
academic approval; recorded the current Elastic IP and the unresolved campus
SSH timeout; did not infer public HTTPS or persistence replacement success
from local checks. Domain details, renewal and external checks, PR workflows,
final compilation, and submission remain unverified.
\subsection{Record 9 --- Domain, DNS, and Open PR Evidence}
\textbf{Date:} October 15, 2026.\\
\textbf{Tool:} OpenAI Codex.\\
\textbf{Task:} Guide Victor through organization/repository setup, issue-linked
PR creation, Name.com Student Pack domain redemption, and independent DNS
verification; document all completed work and its evidence.\\
\textbf{Human actions:} Victor created the Inviscid organization, assigned both
authors to Owners, pushed the existing main history, created issue 1, committed
and pushed documentation, and opened PR 2 requesting Payton's review. Victor
reported successful Overleaf compilation/review of the earlier draft and
Payton's working SSH/Gitea access. Victor registered inviscid.dev and executed
the supplied DNS lookups; the assistant did not perform purchases or DNS writes.\\
\textbf{Output used:} Procedural guidance, saved non-secret screenshots,
updated chapter, renewal/account records, and explicit handoff/pending status.\\
\textbf{Evidence handling and limits:} The order screenshot containing billing
and contact details was excluded from the repository; free-checkout and domain
management screenshots provide non-sensitive evidence instead. Public HTTPS,
renewal tests, external exposure, persistence replacement, Payton's PR review,
merge/closure, remaining issues/README, and final compilation are not claimed.
\subsection{Record 10 --- HTTPS, Firewall, Persistence, and Report Completion}
\textbf{Date:} October 13--15, 2026.\\
\textbf{Tool and model:} Anthropic Claude (Opus 5.5).\\
\textbf{Task:} Plan Payton's remaining Chapter~2 work; guide security-group
changes, Caddy and Compose configuration, the renewal, persistence, and
external exposure tests; draft README edits, the architecture diagram,
and this chapter's completed sections.\\
\textbf{Human actions:} Payton ran every console action and command, captured
all screenshots, chose Caddy, wrote and edited the issues, opened and merged
PR 7, and reviewed PR 2. Victor reviewed PR 7. The assistant viewed the AWS
console read-only; it made no AWS, DNS, or Gitea changes.\\
\textbf{Output used:} Configuration (Caddyfile, Compose additions), README
edits, TikZ diagram, and chapter text based on Payton's screenshots and
command output.\\
\textbf{Evidence handling and limits:} Values in this chapter come from the
saved screenshots. Team IP addresses are redacted in the text.
+13 -1
View File
@@ -2,11 +2,23 @@
\newacronym{ami}{AMI}{Amazon Machine Image} \newacronym{ami}{AMI}{Amazon Machine Image}
\newacronym{iam}{IAM}{identity and access management} \newacronym{iam}{IAM}{identity and access management}
\newacronym{mfa}{MFA}{multi-factor authentication} \newacronym{mfa}{MFA}{multi-factor authentication}
\newacronym{acme}{ACME}{Automatic Certificate Management Environment}
\newacronym{ssh}{SSH}{secure shell} \newacronym{ssh}{SSH}{secure shell}
\newacronym{tls}{TLS}{Transport Layer Security}
\newacronym{wip}{WIP}{work in progress} \newacronym{wip}{WIP}{work in progress}
\newacronym{wsl}{WSL}{Windows Subsystem for Linux}
\newglossaryentry{evidence}{name={evidence},description={A retained record of an \newglossaryentry{evidence}{name={evidence},description={A retained record of an
actual execution, observation, or review}} actual execution, observation, or review}}
\newglossaryentry{host}{name={host},description={A named machine in the \newglossaryentry{host}{name={host},description={A named machine in the
development environment, physical or virtual, with an assigned role and owner}} development environment, physical or virtual, with an assigned role and owner}}
\newglossaryentry{kanban}{name={Kanban board},description={A visual board that \newglossaryentry{kanban}{name={Kanban board},description={A visual board that
represents each unit of work as a card in a column denoting its state}} represents each unit of work as a card in a column denoting its state}}
\newglossaryentry{texlive}{name={TeX Live},description={A cross-platform
distribution of the \TeX\ engines, \LaTeX\ packages, fonts, and tools; the
distribution Overleaf compiles with}}
\newglossaryentry{caddy}{name={Caddy},description={A web server and reverse
proxy that obtains and renews TLS certificates automatically; the team's HTTPS endpoint for Gitea}}
\newglossaryentry{sqlite}{name={SQLite},description={An embedded database stored
in a local file; Gitea's selected database for the initial two-person deployment}}
\newglossaryentry{elasticip}{name={Elastic IP},description={An allocated AWS
public IPv4 address that can remain associated with the deployment across instance restarts}}
+157
View File
@@ -0,0 +1,157 @@
\chapter{Digital Ocean Docker Deployment}
\ChapterLabel{CloudDockerDeployment}
\index{Docker}\index{Amazon EC2}\index{JavaScript!classes}
\section{Scope and Platform}
This chapter records Victor's deployment of the group Color Buttons website.
The assignment's chapter title is retained; Amazon EC2 replaces the DigitalOcean
Droplet in this group's implementation. Payton handles the separate TeX Live
compiler and its chapter. UML diagrams are omitted under the instructor's
clarification that deployment diagrams are encouraged but optional.
The website is available at \url{http://54.221.175.174} while the instance is
running. This is an HTTP demonstration site without TLS. Its automatically
assigned address can change after the instance is stopped and started.
\section{AWS Configuration}
Victor created the paid AWS account for the group and configured a monthly
budget alert. No credits were reported. IAM Identity Center provides separate
Victor and Payton identities in the developers group, assigned an EC2 permission
set using \texttt{AmazonEC2FullAccess}. Root remains the
owner's administrative identity; a separate administrator role was not created
and MFA setup was deferred. The budget is an alert, not a spending cap.
\begin{tabularx}{\linewidth}{L{4cm}Y}
\toprule \textbf{Setting} & \textbf{Recorded configuration}\\ \midrule
Instance name & \texttt{ssw590-group-web}\\
Region & N. Virginia (\texttt{us-east-1})\\
Instance type & \texttt{t3.micro}\\
Operating system & Ubuntu 26.04 LTS, amd64\\
Networking & Default VPC, public IPv4 enabled\\
Inbound SSH & TCP 22 restricted to Victor's detected IP\\
Inbound website & TCP 80 from \texttt{0.0.0.0/0}\\
Storage & Launch configuration used an 8 GiB gp3 root volume with encryption and delete-on-termination selected\\
Metadata & IMDSv2 required, confirmed in instance summary\\
Application port & Container TCP 3000 published as host TCP 80\\
\bottomrule
\end{tabularx}
\section{Class-Based Website}
The original two click handlers were reorganized into a
\texttt{ColorButtonsApp} class. Its constructor retains the background and
status elements; \texttt{setColor} changes both; \texttt{connectButton} binds
click events; and \texttt{initialize} connects Blue and Red. Express serves the
static public directory. Both outcomes were verified locally and on AWS.
\subsection{Application Sources}
\paragraph{Browser class: public/app.js}
\inputminted{javascript}{docker/color-buttons/public/app.js}
\paragraph{Page: public/index.html}
\inputminted{html}{docker/color-buttons/public/index.html}
\paragraph{Web server: server.js}
\inputminted{javascript}{docker/color-buttons/server.js}
\paragraph{Dependencies: package.json}
\inputminted{json}{docker/color-buttons/package.json}
\paragraph{Dockerfile}
\inputminted{docker}{docker/color-buttons/Dockerfile}
\paragraph{.dockerignore}
\begin{minted}{text}
node_modules
npm-debug.log
.git
.DS_Store
\end{minted}
\section{Deployment Procedure}
The commands below reproduce the recorded setup and successful configuration.
The local folder remained under \path{HW1/HW3/Group-Project} for this assignment;
relocating it to the course root is deferred. The private SSH key is stored
outside the project and is excluded from the report and repository.
\subsection{Connect and Install Docker}
From Windows Git Bash:
\begin{minted}{bash}
ssh -i "$HOME/Documents/_SIT/_Fall2026/SSW 590 - DevOps/ssw590-victor.pem" ubuntu@54.221.175.174
\end{minted}
On the EC2 host:
\begin{minted}{bash}
cat /etc/os-release
uname -m
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod 0644 /etc/apt/keyrings/docker.asc
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: resolute
Components: stable
Architectures: amd64
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo systemctl is-active docker
sudo docker version
sudo docker run --rm hello-world
\end{minted}
Victor reported successful installation after resolving the repository-key error.
The procedure follows \url{https://docs.docker.com/engine/install/ubuntu/}.
\subsection{Transfer, Build, and Run}
In a separate local Git Bash terminal, copy the application:
\begin{minted}{bash}
scp -i "$HOME/Documents/_SIT/_Fall2026/SSW 590 - DevOps/ssw590-victor.pem" -r "$HOME/Documents/_SIT/_Fall2026/SSW 590 - DevOps/HW1/HW3/Group-Project/color-buttons" ubuntu@54.221.175.174:
\end{minted}
On EC2:
\begin{minted}{bash}
cd ~/color-buttons
sudo docker build -t ssw590-group-buttons .
sudo docker run -d --name ssw590-group-buttons --restart unless-stopped -p 80:3000 ssw590-group-buttons
sudo docker ps --filter name=ssw590-group-buttons
sudo docker logs ssw590-group-buttons
curl -I http://127.0.0.1
\end{minted}
The create command assumes the container name is unused. The restart policy
allows the running service to resume after a host restart. The application binds
to all container interfaces, and the AWS security group permits public HTTP.
Since October 15, 2026, the container no longer publishes port 80; Caddy serves
it by IP over the private Docker network (\ChapterRef{GiteaFoundation}).
\section{Verification Evidence}
The terminal screenshot shows container \texttt{70ed56f7fae8} running, the
\texttt{80->3000/tcp} mapping, the server startup message, and
\texttt{HTTP/1.1 200 OK}. Public-browser screenshots record the Blue and Red outcomes.
These figures are supplied execution evidence, not generated illustrations.
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth]{figures/aws-container.png}
\caption{EC2 container status, startup logs, and successful local HTTP check.}
\label{fig:aws-container}
\end{figure}
\begin{figure}[p]
\centering
\includegraphics[width=\linewidth]{figures/aws-buttons-blue.png}
\caption{Public AWS website after clicking Blue.}
\label{fig:aws-blue}
\vspace{1em}
\includegraphics[width=\linewidth]{figures/aws-buttons-red.png}
\caption{Public AWS website after clicking Red.}
\label{fig:aws-red}
\end{figure}
\section{Ownership and Continued Operation}
Victor owns the AWS account and billing responsibility; both authors share the
course project. Compute, storage, and public IPv4 usage are chargeable under
this account. The instance is retained for the working assignment link and
future labs. Closing SSH does not stop the detached container. Stopping the EC2
instance takes the website offline and preserves its disk, which remains
billable; restarting can change the public address. No completed shutdown or
termination is claimed. Future cleanup and any folder relocation will be
recorded separately.
+457
View File
@@ -0,0 +1,457 @@
\chapter{Startup Foundation with Gitea}
\ChapterLabel{GiteaFoundation}
\index{Gitea}\index{SQLite}\index{Docker Compose}\index{Elastic IP}
This chapter records the Inviscid work hub on the existing Amazon EC2 server.
AWS is used in place of the assignment's DigitalOcean deployment. Victor
completed the domain, DNS, Gitea deployment, and account setup; Payton completed
HTTPS, the firewall, persistence testing, and the remaining workflow evidence.
\section{Student Pack Domain and DNS}
After GitHub Education approved Victor's Student Developer Pack application on
October 12, partner benefits activated on October 15. Victor then redeemed
Name.com's Student Pack offer. It covered selected extensions rather than the
preferred \texttt{inviscid.net}, so he registered \texttt{inviscid.dev} for one
year. No registrar-sold SSL product was needed, because Caddy obtains the
certificate (Table~\ref{Table:GiteaCertOptions}).
\begin{tabularx}{\linewidth}{L{3.8cm}Y}
\caption{Student Domain and DNS Record\label{Table:GiteaDomain}}\\
\toprule \textbf{Field} & \textbf{Recorded value}\\ \midrule
Provider and eligibility & Name.com, redeemed through the activated GitHub Student Developer Pack benefit\\
Registered domain & \texttt{inviscid.dev}\\
Registration date / term & October 15, 2026 / one year\\
Initial charge & \$0.00\\
Displayed renewal price & \$22.99; verify the current price before renewal\\
Renewal date & October 15, 2027\\
Renewal controls & Automatic renewal ON; WHOIS privacy ON; transfer lock enabled; Victor tracks renewal and keeps payment details current\\
DNS record & A, host \texttt{git}, answer \texttt{100.49.80.94} (Elastic IP), TTL 300 seconds\\
Public service URL & \url{https://git.inviscid.dev/}\\
\bottomrule
\end{tabularx}
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/domain-free-checkout.png}
\caption{October 15: one-year inviscid.dev registration at a checkout total of zero.}
\label{Figure:GiteaDomainCheckout}
\end{figure}
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/domain-renewal-details.png}
\caption{October 15: registered domain, renewal date and price, auto-renewal, privacy, and transfer-lock settings.}
\label{Figure:GiteaDomainRenewal}
\end{figure}
Victor added the A record in Name.com's DNS manager
(Figure~\ref{Figure:GiteaDNSRecord}) and verified it from outside EC2 at 19:17 EDT
on October 15: both Cloudflare (1.1.1.1) and Google (8.8.8.8) resolvers returned
\texttt{100.49.80.94} (Figure~\ref{Figure:GiteaDNSLookup}).
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/dns-a-record.png}
\caption{October 15: saved A record for git.inviscid.dev pointing to 100.49.80.94 with a 300-second TTL.}
\label{Figure:GiteaDNSRecord}
\end{figure}
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/dns-independent-lookups.png}
\caption{October 15: independent Cloudflare and Google resolver lookups both return the expected Elastic IP.}
\label{Figure:GiteaDNSLookup}
\end{figure}
\section{Pinned Gitea Deployment}
Gitea runs on the existing \texttt{ssw590-group-web} instance (Ubuntu 26.04 LTS,
\texttt{t3.micro}, \texttt{us-east-1}), whose Elastic IP keeps the DNS record
valid across restarts. Victor deployed Gitea privately on October 12. On
October 15, Payton added Caddy as the HTTPS endpoint and recreated the existing
Color Buttons container without a published port on \texttt{inviscid\_services},
so that Caddy could take port 80; the recreate command is in the deployment
README.
\begin{tabularx}{\linewidth}{L{3.5cm}Y}
\caption{Recorded Gitea Deployment\label{Table:GiteaDeployment}}\\
\toprule \textbf{Setting} & \textbf{Value}\\ \midrule
Instance & \texttt{i-025a57432604c8d4b}, \texttt{ssw590-group-web}\\
Public / private address & Elastic IP \texttt{100.49.80.94} / \texttt{172.31.19.91}\\
Official image & \texttt{docker.gitea.com/gitea:28.1.0}\\
Container & \texttt{inviscid-gitea-1}\\
Remote working directory & \path{/home/ubuntu/gitea}\\
Persistent volume & \texttt{inviscid\_gitea\_data}, mounted at \path{/data}\\
Docker network & \texttt{inviscid\_services}\\
Restart policy & \texttt{unless-stopped}\\
Published backend & \texttt{127.0.0.1:3000:3000}; no public backend mapping\\
Public URL setting & \url{https://git.inviscid.dev/}\\
TLS proxy & \texttt{caddy:2.10.2}, container \texttt{inviscid-caddy-1}; the only service publishing host ports 80 and 443\\
\bottomrule
\end{tabularx}
Both images are pinned to version tags rather than \texttt{latest}. The recorded
Gitea repository digest is:
\begin{minted}[fontsize=\scriptsize]{text}
docker.gitea.com/gitea@sha256:f505a0d3a41323b786e05942fc7a4075e07ae7c169fd9485649aa55ee543a21a
\end{minted}
The non-secret configuration is maintained in
\href{https://git.inviscid.dev/Inviscid/SSW590-DevOps/src/branch/main/docker/gitea}{the team's versioned deployment directory}
in the Gitea repository. The deployed Compose file and Caddyfile follow.
\inputminted{yaml}{docker/gitea/compose.yaml}
\inputminted{text}{docker/gitea/Caddyfile}
To repeat the installation, copy the directory to the server and run, from it:
\begin{minted}{bash}
cd ~/gitea
sudo docker compose config --quiet
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
\end{minted}
Validation returned without an error and the pinned image pulled
(Figure~\ref{Figure:GiteaPull}).
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/compose-validation-pull.png}
\caption{October 12: successful Compose validation and pull of the pinned official image.}
\label{Figure:GiteaPull}
\end{figure}
\section{Database and Persistence}
SQLite was selected for the small two-person deployment. It requires no
separate database service or network port. The database is at
\path{/data/gitea/gitea.db}, within the same persistent named volume that holds
application configuration and repository data. The volume exists outside the
disposable container layer. Generated configuration, database contents, and
secrets are not stored in the Git repository. SQLite suits this team's size but
may not fit a larger, higher-concurrency deployment.
\subsection{Container-Replacement Test}
On October 15 at 21:34 EDT, Payton replaced only the Gitea container, using
repository \texttt{Inviscid/SSW590-DevOps} and issue \#3 as the recorded data.
Volumes were not removed:
\begin{minted}{bash}
sudo docker compose up -d --force-recreate --no-deps gitea
\end{minted}
The container ID changed from \texttt{915c6b6d88d2} to \texttt{82ca41970439},
confirming that the container and its writable layer were replaced. The same
volume remained mounted at \path{/data}, the 2.2 MiB database file was still
present (Figure~\ref{Figure:GiteaPersistence}), and the repository and issue \#3
were unchanged and usable (Figure~\ref{Figure:GiteaPersistenceIssue}). The data
survived because Compose reattached the named volume. This is a persistence
test, not a backup; see the Architecture and Operation section.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/persistencetest.png}
\caption{October 15: container ID, mount, replacement command, new container ID, and database file.}
\label{Figure:GiteaPersistence}
\end{figure}
\begin{figure}[htbp]
\centering
\begin{minipage}{0.49\linewidth}\centering
\includegraphics[width=\linewidth]{figures/gitea/beforeissue.png}\\ \small Before
\end{minipage}\hfill
\begin{minipage}{0.49\linewidth}\centering
\includegraphics[width=\linewidth]{figures/gitea/afterissue.png}\\ \small After
\end{minipage}
\caption{October 15: issue \#3 before and after the Gitea container replacement.}
\label{Figure:GiteaPersistenceIssue}
\end{figure}
\section{HTTPS and Certificate Renewal}
\index{Caddy}\index{Let's Encrypt}
\subsection{Certificate Options}
Both options below use Let's Encrypt, a free certificate authority that issues
90-day certificates through \gls{acme}. A paid or registrar-sold certificate was
rejected because it needs manual installation and renewal.
\begin{tabularx}{\linewidth}{L{2.8cm}YY}
\caption{Certificate Options\label{Table:GiteaCertOptions}}\\
\toprule \textbf{Criterion} & \textbf{Caddy automatic HTTPS (selected)} & \textbf{certbot with nginx}\\ \midrule
Issuance and renewal & Built into the proxy; no extra job & Separate certbot client and renewal timer\\
Configuration & One Caddyfile site block; HTTP-to-HTTPS redirect is automatic & nginx server blocks plus certbot plugin\\
Containers & One proxy container & Proxy plus certbot, or certbot on the host\\
Renewal test & No dry-run command; forced reissue used instead & \texttt{certbot renew --dry-run}\\
\bottomrule
\end{tabularx}
\Gls{caddy} was selected for fewer moving parts and secure defaults. Its main
drawback is the lack of a dry-run renewal command.
\subsection{TLS Termination and Evidence}
\gls{tls} terminates at Caddy. Caddy forwards plain HTTP to \texttt{gitea:3000}
over the private Docker network \texttt{inviscid\_services}; that traffic never
leaves the instance. Port 80 is open for HTTP-to-HTTPS redirects and ACME
validation.
Caddy obtained the Let's Encrypt certificate for \texttt{git.inviscid.dev} at
19:52 EDT on October 15; the current certificate is valid until January 13, 2027
(90 days). From Payton's laptop, \texttt{http://git.inviscid.dev} returned
\texttt{308 Permanent Redirect} to \url{https://git.inviscid.dev/}, and the HTTPS
URL returned HTTP/2 200 (Figure~\ref{Figure:GiteaExternalTests}). The
\texttt{.dev} top-level domain is on browsers' HSTS preload list, so browsers
never request plain HTTP for this host; the redirect matters for non-browser
clients and was tested with \texttt{curl}.
\subsection{Renewal Automation and Test}
Caddy renews managed certificates automatically in the background, by default
when about one third of the lifetime remains (around mid-December 2026 for
this certificate). No cron job or manual command is required. Certificate and
ACME account state persist in \texttt{inviscid\_caddy\_data}, so replacing the
Caddy container does not force reissuance.
Because Caddy has no dry-run command, Payton forced a reissue by deleting the
stored certificate and restarting Caddy. This exercises the same unattended
ACME path that renewal uses:
\begin{minted}{bash}
sudo docker compose exec caddy rm -rf \
/data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/git.inviscid.dev
sudo docker compose restart caddy
sudo docker compose logs --since 1m caddy | grep -i obtained
\end{minted}
Caddy logged \texttt{certificate obtained successfully} without manual
intervention, and the serial changed from \texttt{06ADFF\ldots F6A7} to
\texttt{06CA6A\ldots 034F} with a new validity window
(Figure~\ref{Figure:GiteaRenewal}). This was a forced reissue, not an observed
natural renewal, and was run once to stay within Let's Encrypt's
duplicate-certificate limit.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/caddy.png}
\caption{October 15: certificate serial and validity before and after the forced reissue, with Caddy's log entry.}
\label{Figure:GiteaRenewal}
\end{figure}
\section{Firewall and External Exposure}
The only security group attached to the instance is \texttt{ssw590-web-sg}
(\texttt{sg-0a9eb6dcfc906e78b}); Figure~\ref{Figure:GiteaArchitecture} shows the
resulting network paths. Team source addresses are partially redacted.
\begin{tabularx}{\linewidth}{L{1.6cm}L{3.4cm}Y}
\toprule \textbf{Inbound TCP} & \textbf{Source} & \textbf{Purpose}\\ \midrule
443 & \texttt{0.0.0.0/0} & HTTPS to Caddy for \texttt{git.inviscid.dev}\\
80 & \texttt{0.0.0.0/0} & HTTP-to-HTTPS redirect, ACME validation, Color Buttons by IP\\
22 & \texttt{100.8.x.x/32} & Victor, home\\
22 & \texttt{155.246.x.x/32} & Victor, campus\\
22 & \texttt{108.35.x.x/32} & Payton\\
\bottomrule
\end{tabularx}
Outbound allows all traffic to \texttt{0.0.0.0/0}. The server needs outbound
access for package and image downloads, DNS, time synchronization, and Let's
Encrypt; the group is stateful, so replies to allowed inbound connections are
permitted. The instance has no IPv6 address, so there are no IPv6 rules. Docker
also binds ports 80 and 443 on IPv6, but they are unreachable without an IPv6
address. The subnet's network ACL (\texttt{acl-06bdf6081fd8b9d5d}) is the default
allow-all in both directions, so the security group is the effective filter
(Figures~\ref{Figure:GiteaFirewall} and~\ref{Figure:GiteaNACL}).
The Color Buttons app from an earlier assignment is still served over plain
HTTP by IP, which does not meet the hostname-based HTTPS rule. It is kept
because the instructor expects the instance and this app to stay reachable; it
no longer publishes its own port, and giving it an HTTPS hostname is future work.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/security.png}
\caption{October 15: the single attached security group with final inbound and outbound rules.}
\label{Figure:GiteaFirewall}
\end{figure}
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/subnets.png}
\caption{October 15: default network ACL on the instance's subnet.}
\label{Figure:GiteaNACL}
\end{figure}
\subsection{Host and Container Exposure}
Docker-published ports can bypass host firewall (ufw) rules, so the security
group is the public boundary. Figure~\ref{Figure:GiteaPorts} shows that only
\texttt{docker-proxy} (Caddy) listens on 80 and 443 and \texttt{sshd} on 22.
Gitea's port 3000 listens on \texttt{127.0.0.1} only, and the Color Buttons
container and Caddy's admin port 2019 have no host mapping.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/ports.png}
\caption{October 15: Docker port mappings and listening sockets on EC2.}
\label{Figure:GiteaPorts}
\end{figure}
\subsection{External Exposure Test}
Tests ran from Payton's laptop on October 15, outside EC2, using
\texttt{nc -vz -w 5} and \texttt{curl}:
\begin{tabularx}{\linewidth}{L{1.5cm}L{3.3cm}L{2.6cm}Y}
\toprule \textbf{Port} & \textbf{Source (EDT)} & \textbf{Expected} & \textbf{Actual}\\ \midrule
443 & \texttt{108.35.x.x}, 21:53 & open & connected; HTTP/2 200\\
80 & \texttt{108.35.x.x}, 21:53 & open, redirect & connected; 308 to HTTPS; IP returns 200 (Color Buttons)\\
22 & \texttt{108.35.x.x}, 21:53 & open (allowed) & connected\\
3000 & \texttt{108.35.x.x}, 21:53 & blocked & timed out\\
222 & \texttt{108.35.x.x}, 21:53 & blocked & timed out\\
2019 & \texttt{108.35.x.x}, 21:53 & blocked & timed out\\
22 & \texttt{174.206.x.x} (phone hotspot), 21:53--21:56 & blocked & timed out\\
\bottomrule
\end{tabularx}
All results matched expectations, so no corrections were needed. The laptop was
switched to the hotspot before the final SSH test; \texttt{checkip} afterwards
confirmed the hotspot address. Timeouts, rather than refusals, show that the
security group drops the traffic before it reaches the instance.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/laptop.png}
\caption{October 15: external port, redirect, and HTTPS tests from an allowed source and from a non-allowed hotspot.}
\label{Figure:GiteaExternalTests}
\end{figure}
HTTPS encrypts connections that the firewall admits; it does not close any
port. Without the security group, Gitea's port, SSH, or any accidentally
published container port could be reached directly.
\section{Startup Team Workflow}
Victor initialized Gitea with site title \texttt{Inviscid}, created the
\texttt{victor} site administrator, and disabled public self-registration. He
created a separate regular \texttt{payton} account with a temporary password,
which Payton changed after first login; no shared Gitea account is used.
\subsection{Organization, Repository, and Roles}
Victor created organization \texttt{Inviscid} and added \texttt{payton} to its
Owners team, so both members administer the organization and its repositories
(Figure~\ref{Figure:GiteaOwners}). Victor leads domain, DNS, and deployment
work; Payton leads HTTPS, firewall, persistence, and repository workflow.
Victor then pushed the existing project history to the product repository,
\texttt{Inviscid/SSW590-DevOps}. Its root \texttt{README.md}, added in PR 7,
describes the product, repository layout, team roles, and contribution workflow
(Figure~\ref{Figure:GiteaReadme}). The organization and repository are at
\url{https://git.inviscid.dev/Inviscid} and
\url{https://git.inviscid.dev/Inviscid/SSW590-DevOps}.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/organization-owners.png}
\caption{October 15: victor and payton are members of the Inviscid Owners team.}
\label{Figure:GiteaOwners}
\end{figure}
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/readme.png}
\caption{October 15: product repository README.}
\label{Figure:GiteaReadme}
\end{figure}
\subsection{Backlog}
The backlog contains five actionable issues, each with acceptance criteria:
\begin{tabularx}{\linewidth}{L{0.6cm}YL{2.1cm}L{2.5cm}}
\toprule \textbf{\#} & \textbf{Issue} & \textbf{Type} & \textbf{Status}\\ \midrule
1 & Document the initial Gitea deployment and team access & Operations & Closed (PR 2)\\
3 & Finalize README.md (product and deployment READMEs) & Product & Closed (PR 7)\\
4 & Enable HTTPS for git.inviscid.dev & Operations & Closed (PR 7)\\
5 & Configure EC2 firewall & Operations & Closed after tests\\
6 & Version the Color Buttons app in the repository & Product & Open\\
\bottomrule
\end{tabularx}
\subsection{Issue-Linked Pull Requests}
Gitea numbers issues and pull requests from one sequence, so the two PRs are
2 and 7.
\paragraph{PR 2 (Victor, reviewed by Payton).} Victor documented the initial
deployment on branch \texttt{codex/gitea-foundation-docs} and opened PR 2 with
\texttt{Closes \#1}, requesting Payton's review. Payton reviewed the changes,
approved, and merged the PR, which closed issue 1
(Figure~\ref{Figure:GiteaPR2}).
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/pr2merge.png}
\caption{October 15: PR 2 approved by Payton and merged, closing issue 1.}
\label{Figure:GiteaPR2}
\end{figure}
\paragraph{PR 7 (Payton, reviewed by Victor).} Payton authored PR 7 from
\texttt{payton/caddy-https}. It adds the root README, the Caddy service and
Caddyfile, the HTTPS URL settings, and the updated deployment README, with
\texttt{Closes \#3} and \texttt{Closes \#4}. Victor reviewed and approved it,
and Payton merged it as commit \texttt{1d7c528795}, which closed both issues
(Figure~\ref{Figure:GiteaPR7}). The initial \texttt{main} push is not counted
as a reviewed merge.
\begin{figure}[htbp]
\centering
\includegraphics[width=\linewidth]{figures/gitea/mypr.png}
\caption{October 15: PR 7 linked to issues 3 and 4, approved by Victor, and merged.}
\label{Figure:GiteaPR7}
\end{figure}
\section{Architecture and Operation}
Figure~\ref{Figure:GiteaArchitecture} shows the deployed request and data paths.
\begin{figure}[htbp]
\centering
\resizebox{\linewidth}{!}{\input{figures/devopsGiteaFoundation/giteaArchitecture}}
\caption{October 15: deployed architecture. The security group admits 443 and 80 from any source and 22 only from approved team addresses. Caddy is the only container publishing host ports; Gitea, the Color Buttons app, and both data volumes are private.}
\label{Figure:GiteaArchitecture}
\end{figure}
\paragraph{Where data lives.} Repositories, attachments, Gitea configuration,
and the SQLite database (\path{/data/gitea/gitea.db}) live in the named volume
\texttt{inviscid\_gitea\_data}. Certificates and the ACME account live in
\texttt{inviscid\_caddy\_data} (with \texttt{inviscid\_caddy\_config}). All
three volumes are on the instance's EBS disk, outside the disposable container
layers. Deployment files are versioned in \path{docker/gitea/} of the repository.
\paragraph{Public and private interfaces.} Public: TCP 443 (HTTPS to Caddy) and
TCP 80 (redirect to HTTPS, ACME validation, and the Color Buttons app by IP),
both from any source. Restricted: TCP 22 (administrative SSH) from approved team
addresses only. Private: Gitea's port 3000 (reachable only on the Compose network
and EC2 loopback, for SSH-tunnel administration), the Color Buttons container's
port 3000, Caddy's admin port 2019, and the SQLite file, which has no network port.
\paragraph{Restart and troubleshooting.} From \path{~/gitea} on EC2:
\begin{minted}{bash}
sudo docker compose ps
sudo docker compose logs --tail=40 caddy
sudo docker compose logs --tail=40 gitea
sudo docker compose restart gitea # or caddy; volumes are kept
\end{minted}
If \url{https://git.inviscid.dev} fails, check in order: (1) both services are
running; (2) the Caddy log shows no certificate or upstream errors; (3) the Gitea
log shows it listening on port 3000; (4) \texttt{curl -I http://127.0.0.1:3000/}
on EC2 returns 200, which isolates Gitea from Caddy; (5) DNS still resolves to
the Elastic IP and the security group still admits 443.
\paragraph{Persistence test versus backup/restore.} The container-replacement
test shows that data survives when the Gitea \emph{container} is discarded,
because the named volume is reattached. It does not protect against loss of the
volume, the EBS disk, or the instance, and it creates no copy elsewhere. A
backup/restore test would copy the volume and database to separate storage and
prove recovery into a different instance; that optional exercise was not
performed.
\paragraph{Costs and cleanup.} Victor owns the class AWS environment; compute,
EBS, and public IPv4 are billable and covered by budget alerts rather than a
spending cap. End-of-course cleanup must include the instance, unneeded storage,
and release of the Elastic IP; stopping the instance alone is not full cleanup.
The domain renews automatically on October 15, 2027 unless auto-renewal is
turned off.
\section{Manual Quality and Controls}
This version updates the Hosts and Passwords tables, as recorded in the
document history. No passwords, private keys, database contents, or generated
\texttt{app.ini} are included, and team IP addresses are redacted. Material AI
help is recorded in \ChapterRef{AIPromptLog}.
\paragraph{Failed attempts and corrective actions.}
\begin{itemize}[nosep]
\item Victor's SSH from campus timed out after his public IP changed; a
campus-specific rule was added, and his later access succeeded.
\item Payton's first SSH login failed (\texttt{Permission denied (publickey)});
Victor authorized Payton's public key.
\item EC2 Instance Connect failed because the team's AWS role lacks
\texttt{ec2-instance-connect:SendSSHPublicKey}; the temporary security-group
rule added for it was removed immediately.
\item Caddy could not use port 80 while Color Buttons published it; the app was
moved behind Caddy on the private network.
\item The server's \texttt{compose.yaml} differed from the repository copy by a
stray Windows line ending; the file was rewritten with LF endings, and the
checksums now match.
\end{itemize}
\paragraph{Technical references.}
\href{https://docs.gitea.com/installation/install-with-docker/}{Gitea Docker installation};
\href{https://docs.gitea.com/administration/config-cheat-sheet/}{Gitea configuration};
\href{https://caddyserver.com/docs/running\#docker-compose}{Caddy with Docker Compose};
\href{https://caddyserver.com/docs/automatic-https}{Caddy automatic HTTPS};
\href{https://education.github.com/pack}{GitHub Student Developer Pack};
\href{https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html}{AWS Elastic IP documentation}.
+30 -42
View File
@@ -7,11 +7,11 @@
\index{Amazon EC2} \index{Amazon EC2}
\section{Purpose} \section{Purpose}
This chapter names the hosts that will make up the development environment for This chapter names the hosts that make up the development environment for
\CourseNumber\ and records the role, platform, and ownership of each. It is an \CourseNumber\ and records the role, platform, owner, and status of each.
inventory of intent: no machine listed here has been provisioned, and the chapter therefore Hosts in service are described here at inventory level; their configuration
presents no execution \gls{evidence}. It is expected to grow as later and execution \gls{evidence} are recorded in the chapter for the activity that
assignments add services to the environment. uses them. The inventory may grow or change depending on assignment requirements.
\section{Naming Convention} \section{Naming Convention}
\index{hosts!naming} \index{hosts!naming}
@@ -29,55 +29,43 @@ The \textbf{Status} column distinguishes machines already in use from those that
will be configured in a later assignment. A host is marked \emph{In service} will be configured in a later assignment. A host is marked \emph{In service}
only once it exists and is reachable. only once it exists and is reachable.
\begin{tabularx}{\linewidth}{L{3.0cm}YL{3.3cm}L{2.0cm}} \begin{tabularx}{\linewidth}{L{3.5cm}YL{3.0cm}L{2.2cm}}
\caption{Planned Host Inventory\label{Table:HostInventory}}\\ \caption{Host Inventory\label{Table:HostInventory}}\\
\toprule \textbf{Host name} & \textbf{Role} & \textbf{Platform} & \textbf{Status}\\ \midrule \toprule \textbf{Host name} & \textbf{Role} & \textbf{Platform} & \textbf{Status}\\ \midrule
\endfirsthead \endfirsthead
\multicolumn{4}{l}{\small\itshape Planned Host Inventory (continued)}\\ \multicolumn{4}{l}{\small\itshape Host Inventory (continued)}\\
\toprule \textbf{Host name} & \textbf{Role} & \textbf{Platform} & \textbf{Status}\\ \midrule \toprule \textbf{Host name} & \textbf{Role} & \textbf{Platform} & \textbf{Status}\\ \midrule
\endhead \bottomrule \endlastfoot \endhead \bottomrule \endlastfoot
\texttt{ssw590-group-web} & Group deployment target. Runs Color Buttons and the Gitea work hub in \ChapterRef{GiteaFoundation}; Elastic IP \texttt{100.49.80.94}; DNS \texttt{git.inviscid.dev}; HTTPS through Caddy. & Ubuntu 26.04 LTS on Amazon EC2, \texttt{t3.micro} & In service\\
\texttt{ssw590-dev-01} & Primary Linux host for the course. Target for service configuration, deployment exercises, and command-line work. & Ubuntu Server LTS on Amazon EC2 & Planned\\ \texttt{ssw590-dev-01} & Primary Linux host for the course. Target for service configuration, deployment exercises, and command-line work. & Ubuntu Server LTS on Amazon EC2 & Planned\\
\texttt{ssw590-build-01} & Build and integration host, kept separate from the deployment target so that a failed build cannot disturb a running service. To be provisioned only if a later assignment requires it. & Ubuntu Server LTS on Amazon EC2 & Conditional\\
\end{tabularx} \end{tabularx}
\section{Access and Ownership} \section{Access and Ownership}
\index{hosts!access model} \index{hosts!access model}
\index{least privilege} \index{least privilege}
All hosts listed above are owned and administered by the author. The following \texttt{ssw590-group-web} is shared by both authors and runs under Victor's AWS account,
rules govern access to the Linux hosts once they are provisioned. where each author has a separate IAM identity. For the Gitea deployment, both authors currently connect to the Linux
\texttt{ubuntu} account using separate public keys. Victor is a Gitea site
\begin{itemize}[leftmargin=*] administrator; Payton has a separate regular Gitea account. Both are members
\item Interactive access is by \gls{ssh} public key only. of the Inviscid organization Owners team. Linux account-level
Password authentication is disabled in the server configuration. attribution is shared; per-person Linux accounts remain a future improvement.
The local account password recorded in \ChapterRef{Passwords} authenticates \texttt{sudo} only. Administrative SSH is restricted to approved source IPv4 addresses. The
\item Inbound \gls{ssh} is restricted by source address to the workstation observed rules and connection limitations are in \ChapterRef{GiteaFoundation}.
listed above rather than opened to the public Internet. This inventory does not claim that SSH password authentication or sudo policy
\item Day-to-day work is performed by an unprivileged account. Administrative was independently audited during this exercise.
rights are granted per command rather than by general membership in the
\texttt{sudo} group.
\item Should a teammate require access after teams are formalized, that person
receives a separate named account rather than a shared login, so that
access can be revoked individually and actions remain attributable.
\end{itemize}
\section{Cost and Cleanup} \section{Cost and Cleanup}
\index{hosts!cost} \index{hosts!cost}
\index{hosts!teardown} \index{hosts!teardown}
The Linux hosts are expected to run on the smallest instance size that supports \texttt{ssw590-group-web} runs on a \texttt{t3.micro} instance and is covered
the coursework, under whatever free-tier or credit allowance the account by a monthly AWS budget alert. Compute is billed per
provides, with a billing alarm configured before the first instance is instance-hour, and attached storage and the public IPv4 address are billed as
launched. Compute is billed per instance-hour, and attached storage whether or well, so each additional host adds cost independently of how heavily it is
not the instance is running, so each additional host adds cost independently of used: provisioning \texttt{ssw590-build-01} would approximately double the
how heavily it is used: provisioning \texttt{ssw590-build-01} alongside compute cost of the environment. It is therefore left unprovisioned unless
\texttt{ssw590-dev-01} would approximately double the compute cost of the required in the future, and would be stopped between uses rather than run continuously.
environment. It is therefore left unprovisioned until an assignment requires
builds to be isolated from the deployment target, and would be stopped between
uses rather than run continuously; only \texttt{ssw590-dev-01} needs to remain
available at all times.
Each planned host is disposable. Teardown consists of terminating the instance, Course hosts are disposable. Teardown consists of terminating the instance and
deleting the attached storage volume, and releasing any static address reserved deleting its storage volume. \texttt{ssw590-group-web} uses Elastic IP \texttt{100.49.80.94}, associated on October 12, 2026.
for it; a static address left reserved after the instance is gone continues to Release that allocation when no longer required; stopping or terminating an
be billed. Because the \gls{ami} and the configuration steps are recorded, a instance alone does not release the Elastic IP.
terminated host can be rebuilt rather than preserved, which is why teardown is
treated as routine rather than exceptional.
+29 -24
View File
@@ -7,34 +7,30 @@
\section{Purpose} \section{Purpose}
This document records the configuration of the development environment used for This document records the configuration of the development environment used for
\CourseNumber\ and the working practices followed to produce it. For \CourseNumber\ and the working practices followed to produce it.
Assignment~\AssignmentNumber\ it covers three subjects: the \gls{kanban} that Each chapter covers one subject, and each assignment adds or revises chapters
plans and tracks the assignment work, the hosts the environment will comprise, as recorded in the document history. The latest revision for Assignment~\AssignmentNumber\
and the passwords that will govern accounts on those hosts. adds \ChapterRef{GiteaFoundation}, recording the Gitea deployment, HTTPS, firewall,
persistence testing, and team workflow.
It is a working record rather than a one-time report. Each subsequent Earlier coursework chapters are retained.
assignment extends it, and the document history records what changed and when,
so that the state of the environment at any point in the term can be recovered
from this document alone.
\section{Scope and Constraints} \section{Scope and Constraints}
\index{scope!constraints} \index{scope!constraints}
The following constraints apply to this revision. The following constraints apply to this revision.
\begin{itemize}[leftmargin=*] \begin{itemize}[leftmargin=*]
\item The environment is \emph{specified} here, not provisioned. The hosts \item The hosts chapter inventories the hosts in service and those still planned.
chapter names the machines to be configured and their intended roles; it The cloud deployment chapter records the newly provisioned EC2 website and its actual configuration.
does not claim that any host exists, and it therefore presents no \item Linux Commands evidence was recorded locally under WSL. Assignment 3
execution \gls{evidence}. deployment commands were executed on Ubuntu 26.04 on EC2. Supplied screenshots
distinguish those environments.
\item No credential, access token, private key, or account secret appears in \item No credential, access token, private key, or account secret appears in
this document. Password rules are documented by derivation hint only, as this document. Password rules are documented by derivation hint only, as
described in \ChapterRef{Passwords}. described in \ChapterRef{Passwords}.
\item This is an individual submission. Teams have not yet been formalized, so \item This is a group submission. Chapters written before teams were formed
the board described in \ChapterRef{KanbanSetup} is operated by a single were prepared individually and are identified as such in the document history.
person and every card is assigned to the author.
\item The document is authored in \LaTeX\ and compiled with pdf\LaTeX\ on \item The document is authored in \LaTeX\ and compiled with pdf\LaTeX\ on
Overleaf. The board is hosted in GitHub Projects. The planned environment Overleaf. The board is hosted in GitHub Projects. The group deployment runs on Amazon EC2.
targets Amazon EC2.
\end{itemize} \end{itemize}
\section{Deliverables and Due Date} \section{Deliverables and Due Date}
@@ -42,24 +38,33 @@ The following constraints apply to this revision.
\toprule \textbf{Item} & \textbf{Expectation}\\ \midrule \toprule \textbf{Item} & \textbf{Expectation}\\ \midrule
Due date & \DueDate\\ Due date & \DueDate\\
Instructor & \Instructor\\ Instructor & \Instructor\\
Team & \Author\\
Board & GitHub Projects, \texttt{SSW 590} (private; captured as figures in \ChapterRef{KanbanSetup})\\ Board & GitHub Projects, \texttt{SSW 590} (private; captured as figures in \ChapterRef{KanbanSetup})\\
Required deliverables & Compiled document; Kanban Setup, Passwords, and Hosts chapters; updated document history\\ Required deliverables & Compiled group manual with the eight Gitea rubric sections, configuration and test evidence, updated history, Hosts, and Passwords hints\\
Team policy & Individual submission pending team formalization\\ \bottomrule Team policy & Group submission\\ \bottomrule
\end{tabularx} \end{tabularx}
\section{Document Organization} \section{Document Organization}
\index{document organization} \index{document organization}
\ChapterRef{KanbanSetup} describes the tool selected for the board, the steps \ChapterRef{KanbanSetup} describes the tool selected for the board, the steps
taken to configure it, the policy governing each column, and the inventory of taken to configure it, the policy governing each column, and the inventory of cards.
cards. \ChapterRef{Hosts} names the hosts to be configured and \ChapterRef{Hosts} inventories the hosts in service and those planned, with their roles.
their roles. \ChapterRef{Passwords} defines the account and password rules for those hosts. \ChapterRef{AIPromptLog} records AI assistance used in preparing \ChapterRef{Passwords} defines the account and password rules for those hosts.
this document. \ChapterRef{LinuxCommands} records the Linux working-directory setup and the problem-set solutions.
\ChapterRef{AIPromptLog} records AI assistance used in preparing this document.
\ChapterRef{CloudDockerDeployment} records the class-based Color Buttons website,
its container, and its deployment to Amazon EC2.
\ChapterRef{LatexDocker} records a TeX Live container that compiles \LaTeX\ documents
as Overleaf does.
\ChapterRef{ProjectProposal} proposes the course project and the DevSecOps tools it will compare.
This document is derived from the \CourseNumber\ assignment template credited on This document is derived from the \CourseNumber\ assignment template credited on
the Template Attribution page. The template's instructional example chapter and the Template Attribution page. The template's instructional example chapter and
its template-maintenance appendices have been removed, since they describe how its template-maintenance appendices have been removed, since they describe how
to author the template rather than the environment this document records. to author the template rather than the environment this document records.
\ChapterRef{GiteaFoundation} records the persistent work hub, domain and DNS, HTTPS, firewall, persistence testing, and team workflow.
\section{Tools and AI Assistance} \section{Tools and AI Assistance}
\index{AI assistance} \index{AI assistance}
AI assistance was used in preparing this document for drafting, review, and AI assistance was used in preparing this document for drafting, review, and
+87
View File
@@ -0,0 +1,87 @@
\chapter{LaTeX Docker}
\ChapterLabel{LatexDocker}
\index{Docker}\index{TeX Live}\index{LaTeX Docker}
\section{Purpose and Scope}
This chapter records a Docker container that compiles a
\LaTeX\ document with \gls{texlive}, the distribution Overleaf uses to compile
projects.\footnote{\url{https://www.overleaf.com/learn/latex/Overleaf_and_TeX_Live}}
Like Overleaf, it drives pdf\LaTeX\ with \texttt{latexmk}, which reruns the
engine until cross-references settle.
The container is a command-line compiler. It reads a
\texttt{.tex} file from a mounted folder, writes the PDF back to that folder,
and exits. It opens no network ports. The image is not deployed to a course
host or published to a registry; it was built and tested locally under WSL on
Payton's workstation.
\section{Design}
\begin{tabularx}{\linewidth}{L{3.6cm}Y}
\toprule \textbf{Decision} & \textbf{Choice and rationale}\\ \midrule
Base image & \texttt{debian:trixie-slim}. Debian packages its TeX Live release, so the image needs no separate TeX Live installer or mirror.\\
TeX Live packages & \texttt{texlive-latex-base}, \texttt{texlive-latex-recommended}, \texttt{texlive-fonts-recommended}, and \texttt{lmodern}, installed without recommended extras. This covers common packages such as \texttt{amsmath}, \texttt{hyperref}, and \texttt{booktabs} while avoiding the multi-gigabyte full distribution.\\
Build driver & \texttt{latexmk} with pdf\LaTeX, nonstop mode, and halt on the first error, so a failed compile returns a nonzero exit status.\\
Interface & The entrypoint is the compiler; the default argument is \texttt{hello.tex}, and any other file name can be passed to \texttt{docker run}.\\
Source and output & The document folder is bind-mounted at \path{/work}, so sources are not baked into the image and the PDF is written to the host.\\
Privilege & The image defaults to an unprivileged \texttt{latex} user; at run time the container uses the invoking user's IDs so output files are owned by that user. The font cache is kept in \path{/tmp}, outside the mounted folder.\\
\bottomrule
\end{tabularx}
\section{Container Sources}
\paragraph{Dockerfile}
\inputminted{docker}{docker/latex-compiler/Dockerfile}
\paragraph{.dockerignore}
The sample folder is mounted at run time, so it is excluded from the build
context.
\begin{minted}{text}
sample
*.pdf
\end{minted}
\paragraph{Sample document: sample/hello.tex}
The document exercises T1 fonts, mathematics, a \texttt{booktabs} table,
hyperlinks, and a forward equation reference that resolves only on a second
pdf\LaTeX\ pass.
\inputminted{latex}{docker/latex-compiler/sample/hello.tex}
\section{Build and Run}
All commands run in a WSL shell from \path{docker/latex-compiler}.
\subsection{Build the Image}
\begin{minted}{bash}
docker build -t ssw590-latex .
docker image ls ssw590-latex
\end{minted}
\subsection{Compile the Sample Document}
\begin{minted}{bash}
docker run --rm --user "$(id -u):$(id -g)" -v "$PWD/sample:/work" ssw590-latex
ls -l sample/hello.pdf
grep "Output written" sample/hello.log
\end{minted}
\texttt{-{}-rm} deletes the container when compilation ends. \texttt{-{}-user}
makes the generated files belong to the WSL user instead of the container
user.
\section{Test Evidence}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth]{figures/latex-docker-build.png}
\caption{TeX Live image build under WSL.}
\label{fig:latex-docker-build}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth]{figures/latex-docker-output.png}
\caption{Information about the produced PDF.}
\label{fig:latex-docker-output}
\end{figure}
\begin{figure}[H]
\centering
\fbox{\includegraphics[width=0.8\linewidth,page=1]{figures/latex-docker-hello.pdf}}
\caption{First page of the PDF produced by the container.}
\label{fig:latex-docker-hello}
\end{figure}
+579
View File
@@ -0,0 +1,579 @@
\chapter{Linux Commands}
\ChapterLabel{LinuxCommands}
\index{Linux}
\index{Linux!command line}
\index{Linux!working directory}
\index{bash}
\index{symbolic link}
\index{file timestamps}
\section{Purpose}
This chapter records a set of Linux shell commands run in the authors' working
user directories, and the solutions to the accompanying problem set.
The setup figures reproduce Payton's terminal session. Section~\ref{Section:LinuxEvidence}
adds Victor's actual problem-set execution screenshots as \gls{evidence}.
The command answers in Section~\ref{Section:LinuxSolutions} describe solutions;
where Victor used a different command, the difference is identified rather than
presented as an execution of the exact answer shown.
\section{Environment}
\index{Linux!environment}
The setup figures were recorded by Payton as user \texttt{payton} on
Ubuntu~24.04 under \gls{wsl}, in a \texttt{bash} shell.
Victor independently performed the setup and problem set on September 27, 2026,
as user \texttt{victor} on host \texttt{Victori7}, running Ubuntu~24.04.3 LTS
under WSL~2. His working directory was \texttt{/home/victor/lx-test}.
The session was recorded with \texttt{script}; its original transcript is retained
with the evidence files. Normal commands ran without elevated privileges;
\texttt{sudo} was used for the append-only attribute in Q20.
\section{Working Directory Setup}
\index{Linux!working directory setup}
Figure~\ref{Figure:LxTestSetup} shows the executed setup commands in the order given by
the assignment. Figure~{\ref{Figure:LxTestDirectories}} shows the resulting directory structure,
and Figure~{\ref{Figure:LxTestFiles}} shows the resulting file contents.
\subsection{What the Commands Do}
\index{Linux!command explanations}
\begin{sloppypar}
\begin{itemize}[leftmargin=*]
\item \texttt{mkdir -p \textasciitilde/lx-test \&\& cd \textasciitilde/lx-test}
creates the directory, along with any missing parent directories, and does
not fail if it already exists. The \texttt{\&\&} operator runs \texttt{cd}
only if \texttt{mkdir} succeeds.
\item \texttt{printf "\ldots" > \textit{file}} writes the string to a file,
turning each \texttt{\textbackslash n} into a newline. The \texttt{>}
redirection creates the file or overwrites it if it already exists.
\texttt{printf} is used instead of \texttt{echo} for predictable formatting of these strings and newlines, avoiding
shell-dependent behavior of \texttt{echo}.
\item \texttt{dd} with \texttt{if=/dev/zero}, \texttt{of=blob.bin},
\texttt{bs=1K}, and \texttt{count=48} copies $48$ blocks of
$1\,\mathrm{KiB}$ from \texttt{/dev/zero}, producing a file of
$48 \times 1024 = 49152$ zero bytes.
\item \texttt{mkdir -p src/lib tmp archive} creates three directories in one
command; \texttt{-p} creates \texttt{src} as the parent of \texttt{lib}.
\item \texttt{ln -s src/file1.txt link-to-file1} creates a symbolic link. The
stored target is the relative path \texttt{src/file1.txt}, which is
resolved relative to the directory that contains the link.
\item \texttt{touch -t 202401020304 old.txt} creates \texttt{old.txt} with a
modification time of 2024-01-02 03:04 in local time.
\item \texttt{ls -lA} lists each named directory in long format, including
hidden entries but omitting \texttt{.} and \texttt{..}.
\texttt{head} prints the first ten lines of each file under a
file name header; every file here is shorter than ten
lines, so each is shown in full.
\end{itemize}
\end{sloppypar}
\subsection{Interpretation of Results}
\index{Linux!result interpretation}
Figure~\ref{Figure:LxTestDirectories} confirms each step of the setup:
\begin{itemize}[leftmargin=*]
\item \textbf{File sizes:} \texttt{blob.bin} is $49152$ bytes, as computed
above. Each text file's size equals its characters plus one byte per newline.
\item \textbf{Symbolic link:} \texttt{link-to-file1} has type \texttt{l} and
points to \texttt{src/file1.txt}.
\item \textbf{Timestamp:} \texttt{old.txt} is empty and shows the modified timestamp (\texttt{Jan~2~2024}).
\item \textbf{Directories:} \texttt{archive}, \texttt{tmp}, and
\texttt{src/lib} show no entries in the \texttt{ls -lA} listing, confirming they are empty
(\texttt{total 0} alone would not prove this).
\item \textbf{Permissions:} New files are \texttt{rw-r--r--} ($644$) and new
directories are \texttt{rwxr-xr-x} ($755$).
\end{itemize}
\begin{figure}[htbp]
\begin{lstlisting}[numbers=none, columns=fullflexible, keepspaces=true]
payton@paytonpc:~$ mkdir -p ~/lx-test && cd ~/lx-test
payton@paytonpc:~/lx-test$ printf "alpha\nbeta\nGamma\ngamma\nbeta\n" > words.txt
payton@paytonpc:~/lx-test$ printf "id,name,dept\n1,Ada,EE\n2,Linus,CS\n3,Grace,EE\n4,Dennis,CS\n" > people.csv
payton@paytonpc:~/lx-test$ printf "INFO boot ok\nWARN disk low\nERROR fan fail\nINFO shutdown\n" > sys.log
payton@paytonpc:~/lx-test$ dd if=/dev/zero of=blob.bin bs=1K count=48 status=none
payton@paytonpc:~/lx-test$ mkdir -p src/lib tmp archive
payton@paytonpc:~/lx-test$ printf "one two three four\n" > src/file1.txt
payton@paytonpc:~/lx-test$ printf "two three four five\n" > src/file2.txt
payton@paytonpc:~/lx-test$ ln -s src/file1.txt link-to-file1
payton@paytonpc:~/lx-test$ touch -t 202401020304 old.txt
\end{lstlisting}
\caption{Terminal input for setting up the \texttt{lx-test} working directory.}
\label{Figure:LxTestSetup}
\end{figure}
\begin{figure}[htbp]
\begin{lstlisting}[numbers=none, columns=fullflexible, keepspaces=true]
payton@paytonpc:~/lx-test$ ls -lA . src src/lib archive tmp
.:
total 72
drwxr-xr-x 2 payton payton 4096 Sep 26 15:39 archive
-rw-r--r-- 1 payton payton 49152 Sep 26 15:39 blob.bin
lrwxrwxrwx 1 payton payton 13 Sep 26 15:39 link-to-file1 -> src/file1.txt
-rw-r--r-- 1 payton payton 0 Jan 2 2024 old.txt
-rw-r--r-- 1 payton payton 56 Sep 26 15:39 people.csv
drwxr-xr-x 3 payton payton 4096 Sep 26 15:39 src
-rw-r--r-- 1 payton payton 56 Sep 26 15:39 sys.log
drwxr-xr-x 2 payton payton 4096 Sep 26 15:39 tmp
-rw-r--r-- 1 payton payton 28 Sep 26 15:39 words.txt
archive:
total 0
src:
total 12
-rw-r--r-- 1 payton payton 19 Sep 26 15:39 file1.txt
-rw-r--r-- 1 payton payton 20 Sep 26 15:39 file2.txt
drwxr-xr-x 2 payton payton 4096 Sep 26 15:39 lib
src/lib:
total 0
tmp:
total 0
\end{lstlisting}
\caption{Terminal output showing the resulting directories after \texttt{lx-test} setup.}
\label{Figure:LxTestDirectories}
\end{figure}
\begin{figure}[htbp]
\begin{lstlisting}[numbers=none, columns=fullflexible, keepspaces=true]
payton@paytonpc:~/lx-test$ head words.txt people.csv sys.log src/file1.txt src/file2.txt
==> words.txt <==
alpha
beta
Gamma
gamma
beta
==> people.csv <==
id,name,dept
1,Ada,EE
2,Linus,CS
3,Grace,EE
4,Dennis,CS
==> sys.log <==
INFO boot ok
WARN disk low
ERROR fan fail
INFO shutdown
==> src/file1.txt <==
one two three four
==> src/file2.txt <==
two three four five
\end{lstlisting}
\caption{Terminal output showing the resulting file contents after \texttt{lx-test} setup.}
\label{Figure:LxTestFiles}
\end{figure}
\clearpage
\section{Problem Set Solutions}
\label{Section:LinuxSolutions}
\index{Linux!problem set}
\subsection{Navigation and File Operations}
\begin{enumerate}[label=\arabic*., leftmargin=*]
\item Show your present working directory path only.
\begin{lstlisting}[style=cmd]
$ pwd
\end{lstlisting}
\nopagebreak[4]
\texttt{pwd} prints only the current directory; Victor's result was \texttt{/home/victor/lx-test}.
\item List all entries in the current directory, one per line, including dotfiles.
\begin{lstlisting}[style=cmd]
$ ls -1a
\end{lstlisting}
\nopagebreak[4]
\texttt{-1} places each entry on a separate line; \texttt{-a} includes dotfiles and the \texttt{.} and \texttt{..} entries.
\item Copy \texttt{src/file1.txt} to \texttt{tmp/} only if \texttt{tmp} exists; do it verbosely.
\begin{lstlisting}[style=cmd]
$ [ -d tmp ] && cp -v src/file1.txt tmp/
\end{lstlisting}
\nopagebreak[4]
The directory test prevents copying when \texttt{tmp} is absent; \texttt{-v} reports the source and destination. Victor's output confirms the copy.
\item Move \texttt{old.txt} into \texttt{archive/} and keep its original timestamp.
\begin{lstlisting}[style=cmd]
$ mv old.txt archive/
\end{lstlisting}
\nopagebreak[4]
Moving within this working tree preserves the file's modification time. Victor verified that the timestamp remained January 2, 2024 at 03:04 local time.
\item Create a new empty file \texttt{notes.md} only if it doesn't already exist.
\begin{lstlisting}[style=cmd]
$ [ -e notes.md ] || touch notes.md
\end{lstlisting}
\nopagebreak[4]
The existence test avoids touching an existing file. Victor used the equivalent conditional \texttt{if} form and verified that the newly created file was zero bytes.
\item Show disk usage (human-readable) for the \texttt{src} directory only (not total FS).
\begin{lstlisting}[style=cmd]
$ du -sh src
\end{lstlisting}
\nopagebreak[4]
\texttt{-s} reports one directory total and \texttt{-h} uses readable units. Victor observed \texttt{16K}; allocated disk usage includes directory storage and is not simply text length.
\end{enumerate}
\subsection{Viewing and Searching}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Print line numbers while displaying \texttt{sys.log}.
\begin{lstlisting}[style=cmd]
$ cat -n sys.log
\end{lstlisting}
\nopagebreak[4]
\texttt{cat -n} numbers displayed lines. Victor used \texttt{nl -ba}, which also numbered all four lines in this file.
\item Show only the lines in \texttt{sys.log} that contain \texttt{ERROR} (case-sensitive).
\begin{lstlisting}[style=cmd]
$ grep 'ERROR' sys.log
\end{lstlisting}
\nopagebreak[4]
Without \texttt{-i}, matching is case-sensitive. The result was \texttt{ERROR fan fail}.
\item Count how many distinct words appear in \texttt{words.txt} (case-insensitive).
\begin{lstlisting}[style=cmd]
$ tr '[:upper:]' '[:lower:]' < words.txt | sort -u | wc -l
\end{lstlisting}
\nopagebreak[4]
Each input line contains one word. Lowercasing before sorting and deduplication treats \texttt{Gamma} and \texttt{gamma} as one value; the resulting count was three.
\item From \texttt{words.txt}, show lines that start with \texttt{g} or \texttt{G}.
\begin{lstlisting}[style=cmd]
$ grep '^[gG]' words.txt
\end{lstlisting}
\nopagebreak[4]
The anchor \texttt{\^{}} restricts the match to the start of the line, and \texttt{[gG]} accepts either case. The two matches were \texttt{Gamma} and \texttt{gamma}.
\item Display the first 2 lines of \texttt{people.csv} without using an editor.
\begin{lstlisting}[style=cmd]
$ head -n 2 people.csv
\end{lstlisting}
\nopagebreak[4]
The first two lines are the header and the first data row: \texttt{id,name,dept} and \texttt{1,Ada,EE}.
\item Show the last 3 lines of \texttt{sys.log} and keep following if the file grows.
\begin{lstlisting}[style=cmd]
$ tail -n 3 -f sys.log
\end{lstlisting}
\nopagebreak[4]
\texttt{-n 3} selects the last three lines; \texttt{-f} keeps following. Victor captured the initial three lines, then stopped following with Ctrl+C. No later appended-line test is claimed.
\end{enumerate}
\subsection{Text Processing}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item From \texttt{people.csv}, print only the name column (2nd), excluding the header.
\begin{lstlisting}[style=cmd]
$ tail -n +2 people.csv | cut -d, -f2
\end{lstlisting}
\nopagebreak[4]
\texttt{tail} skips the header and \texttt{cut} selects the comma-separated second field. Victor's equivalent \texttt{awk} command returned Ada, Linus, Grace, and Dennis.
\item Sort \texttt{words.txt} case-insensitively and remove duplicates.
\begin{lstlisting}[style=cmd]
$ sort -fu words.txt
\end{lstlisting}
\nopagebreak[4]
\texttt{-f} ignores case during comparison and \texttt{-u} removes duplicates. Victor's output was alpha, beta, and Gamma; the retained spelling need not be lowercased.
\item Replace every \texttt{three} with \texttt{3} in all files under \texttt{src/} in-place, creating \texttt{.bak} backups.
\begin{lstlisting}[style=cmd]
$ find src/ -type f ! -name '*.bak' -exec sed -i.bak 's/three/3/g' {} +
\end{lstlisting}
\nopagebreak[4]
\texttt{find} visits regular files recursively, excluding existing backups; \texttt{sed}'s \texttt{g} replaces every occurrence on each line. The original files are saved with the \texttt{.bak} suffix. Victor verified both edited content and original backups. Run once to avoid replacing the original backups on a later run.
\item Print the number of lines, words, and bytes for every \texttt{*.txt} file in \texttt{src/}.
\begin{lstlisting}[style=cmd]
$ wc src/*.txt
\end{lstlisting}
\nopagebreak[4]
By default, \texttt{wc} reports lines, words, and bytes. After Q15, Victor observed \texttt{1 4 15} and \texttt{1 4 16}, totaling \texttt{2 8 31}. Backup filenames do not match \texttt{*.txt}.
\end{enumerate}
\subsection{Permissions and Ownership}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Make \texttt{tmp/} readable, writable, and searchable only by the owner.
\begin{lstlisting}[style=cmd]
$ chmod 700 tmp/
\end{lstlisting}
\nopagebreak[4]
Mode \texttt{700} gives the owner read, write, and directory-search permissions, with none for group or others. Victor verified \texttt{drwx------}.
\item Give group execute permission to \texttt{src/lib} recursively without touching others/owner bits.
\begin{lstlisting}[style=cmd]
$ chmod -R g+x src/lib
\end{lstlisting}
\nopagebreak[4]
\texttt{g+x} adds only group execute; \texttt{-R} applies recursively. Victor's before/after value remained \texttt{755} because group execute was already enabled.
\item Show the numeric (octal) permissions of \texttt{src/file2.txt}.
\begin{lstlisting}[style=cmd]
$ stat -c '%a' src/file2.txt
\end{lstlisting}
\nopagebreak[4]
\texttt{\%a} requests octal permissions. Victor observed \texttt{644}: owner read/write, group and others read-only.
\item Make \texttt{notes.md} append-only for the owner via file attributes (if supported).
\begin{lstlisting}[style=cmd]
$ chmod 600 notes.md
$ sudo chattr +a notes.md
\end{lstlisting}
\nopagebreak[4]
Mode \texttt{600} limits ordinary access to the owner. The \texttt{a} attribute separately makes the file append-only; it is a file attribute, not an owner-specific permission bit. Victor's filesystem supported it: \texttt{lsattr} showed \texttt{a}, and \texttt{stat} showed \texttt{600}.
\end{enumerate}
\subsection{Links and Find}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Verify whether \texttt{link-to-file1} is a symlink and show its target path.
\begin{lstlisting}[style=cmd]
$ [ -L link-to-file1 ] && readlink link-to-file1
\end{lstlisting}
\nopagebreak[4]
\texttt{-L} checks the link itself and \texttt{readlink} prints its stored target. Victor confirmed \texttt{src/file1.txt}.
\item Find all regular files under the current tree larger than 40~KiB.
\begin{lstlisting}[style=cmd]
$ find . -type f -size +40k
\end{lstlisting}
\nopagebreak[4]
The size test selects regular files larger than 40 KiB. Victor used the equivalent byte threshold \texttt{+40960c}, printing \texttt{./blob.bin} with 49,152 bytes.
\item Find files modified in the last 10 minutes under \texttt{tmp/} and print their sizes.
\begin{lstlisting}[style=cmd]
$ find tmp/ -type f -mmin -10 -printf '%p %s\n'
\end{lstlisting}
\nopagebreak[4]
\texttt{-mmin -10} selects recent modifications; \texttt{\%s} prints bytes. Victor's command returned no matches because no regular file under \texttt{tmp/} met that time window.
\end{enumerate}
\subsection{Processes and Job Control}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Show your processes in a tree view.
\begin{lstlisting}[style=cmd]
$ pstree -p "$USER"
\end{lstlisting}
\nopagebreak[4]
\texttt{pstree -p} shows process relationships and PIDs for the specified user. Victor instead recorded his own processes with \texttt{ps -u} and \texttt{--forest}, exposing the shell, recording process, and child shell hierarchy.
\item Start \texttt{sleep 120} in the background and show its PID.
\begin{lstlisting}[style=cmd]
$ sleep 120 & echo $!
\end{lstlisting}
\nopagebreak[4]
The ampersand starts a background job; \texttt{\$!} is its PID. Victor captured and printed PID 2476 in his session; PIDs vary between executions.
\item Send a TERM signal to all \texttt{sleep} processes owned by you (don't use \texttt{kill -9}).
\begin{lstlisting}[style=cmd]
$ pkill -TERM -u "$USER" -x sleep
\end{lstlisting}
\nopagebreak[4]
\texttt{-u} restricts matches to the current user's processes and \texttt{-x} requires the exact name. Victor first checked for unrelated sleep processes, then terminated the test process. Waiting for it returned status 143 (128 + SIGTERM 15), and a final process search returned no matches.
\item Show the top 5 processes by memory usage (one-shot, not interactive).
\begin{lstlisting}[style=cmd]
$ ps aux --sort=-%mem | head -n 6
\end{lstlisting}
\nopagebreak[4]
Descending memory percentage puts the largest consumers first; six output lines include one header and five processes. Victor used descending resident-memory size (RSS in KiB) instead; his largest displayed process was \texttt{snapd} at 41,208 KiB.
\end{enumerate}
\subsection{Archiving and Compression}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Create a gzipped tar archive \texttt{src.tgz} from \texttt{src/} with relative paths.
\begin{lstlisting}[style=cmd]
$ tar -czf src.tgz src/
\end{lstlisting}
\nopagebreak[4]
\texttt{-c} creates, \texttt{-z} compresses with gzip, and \texttt{-f} names the archive. The relative \texttt{src/} operand stores relative member paths.
\item List the contents of \texttt{src.tgz} without extracting.
\begin{lstlisting}[style=cmd]
$ tar -tzf src.tgz
\end{lstlisting}
\nopagebreak[4]
\texttt{-t} lists members without extraction. Victor's archive contained the two text files, their backups, and the \texttt{src/lib/} directory.
\item Extract only \texttt{file2.txt} from \texttt{src.tgz} into \texttt{tmp/}.
\begin{lstlisting}[style=cmd]
$ tar -xzvf src.tgz -C tmp/ --strip-components=1 src/file2.txt
\end{lstlisting}
\nopagebreak[4]
The named member limits extraction to one file, \texttt{-C} selects the destination, and stripping one path component produces \texttt{tmp/file2.txt}. Victor verified matching content with \texttt{cmp}.
\end{enumerate}
\subsection{Networking and System Information}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Show all listening TCP sockets with associated PIDs (no root assumptions).
\begin{lstlisting}[style=cmd]
$ ss -ltnp
\end{lstlisting}
\nopagebreak[4]
The flags select listening TCP sockets, numeric addresses, and process details when permitted. Victor saw two loopback listeners on port 53; the process column was blank in his unprivileged invocation, so no associated PIDs are claimed.
\item Print your default route (gateway) in a concise form.
\begin{lstlisting}[style=cmd]
$ ip route show default
\end{lstlisting}
\nopagebreak[4]
Victor's default route used gateway \texttt{172.23.224.1} through \texttt{eth0}. This is a local environment result, not a fixed gateway for every machine.
\item Display kernel name, release, and machine architecture.
\begin{lstlisting}[style=cmd]
$ uname -srm
\end{lstlisting}
\nopagebreak[4]
The requested fields identify the kernel and architecture. Victor observed Linux, release \texttt{6.18.33.2-microsoft-standard-WSL2}, and \texttt{x86\_64}.
\item Show the last 5 successful logins (or last sessions) on the system.
\begin{lstlisting}[style=cmd]
$ last -n 5
\end{lstlisting}
\nopagebreak[4]
Victor's last five available records were reboot/system-boot sessions rather than individual user logins. The record type is retained as shown; no successful user authentications are inferred.
\end{enumerate}
\subsection{Packages and Services (Debian/Ubuntu)}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Show the installed version of package \texttt{coreutils}.
\begin{lstlisting}[style=cmd]
$ dpkg-query -W -f='${Version}\n' coreutils
\end{lstlisting}
\nopagebreak[4]
The query reads installed-package metadata. Victor also printed the package name and observed \texttt{coreutils 9.4-3ubuntu6.3}.
\item Search available packages whose names contain \texttt{ripgrep}.
\begin{lstlisting}[style=cmd]
$ apt-cache search --names-only ripgrep
\end{lstlisting}
\nopagebreak[4]
The search checks package names in the local catalog. It found \texttt{ripgrep}; availability in the catalog does not establish that the package is installed.
\item Check whether service \texttt{cron} is active and print its status line only.
\begin{lstlisting}[style=cmd]
$ systemctl status cron | grep 'Active:'
\end{lstlisting}
\nopagebreak[4]
Filtering the detailed status selects its Active line. Victor used \texttt{systemctl is-active cron}, a concise active-state query, and recorded \texttt{active}.
\end{enumerate}
\subsection{Bash and Scripting}
\begin{enumerate}[label=\arabic*., leftmargin=*, resume]
\item Write a one-liner that loops over \texttt{*.txt} in \texttt{src/} and prints: : .
\textbf{Assumption:} the intended output is each file's name and line count, since
the supplied problem PDF displays an incomplete output format.
\begin{lstlisting}[style=cmd]
$ for f in src/*.txt; do echo "$f: $(wc -l < "$f")"; done
\end{lstlisting}
\nopagebreak[4]
For the stated line-count assumption, \texttt{wc -l} counts each file's lines and the loop prefixes its name. Victor's captured command instead printed filename and contents under a different interpretation of the incomplete prompt; Figure~\ref{Figure:HW2-J} documents that alternative.
\item Write a command that exports CSV rows where \texttt{dept == "CS"} to \texttt{cs.txt} (exclude header).
\begin{lstlisting}[style=cmd]
$ awk -F, 'NR>1 && $3=="CS"' people.csv > cs.txt
\end{lstlisting}
\nopagebreak[4]
The field separator is a comma; \texttt{NR>1} skips the header and field three selects CS. Victor verified that \texttt{cs.txt} contained \texttt{2,Linus,CS} and \texttt{4,Dennis,CS}.
\item Create a variable \texttt{X} with value 42, print it, then remove it from the environment.
\begin{lstlisting}[style=cmd]
$ export X=42; echo "$X"; unset X
\end{lstlisting}
\nopagebreak[4]
\texttt{export} adds the variable to the shell's exported environment; \texttt{unset} removes it. Victor printed \texttt{X=42}, then verified its absence using \texttt{printenv}.
\end{enumerate}
\section{Problem Set Execution Evidence}
\label{Section:LinuxEvidence}
These screenshots were supplied by Victor from his recorded September 27th
session. They show actual commands and outputs, grouped by problem-set section.
They supplement Payton's setup figures. Captions identify the question ranges;
the explanations above distinguish equivalent commands.
Empty output is retained where it is the observed result.
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/03-section-a-q01-q06.png}
\caption{Q1--Q6: navigation and file operations. The moved file retains its original modification time, and the new notes file is empty.}
\label{Figure:HW2-A}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/04-section-b-q07-q12.png}
\caption{Q7--Q12: viewing and searching. The final command remains in follow mode at capture; Victor subsequently stopped it with Ctrl+C.}
\label{Figure:HW2-B}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/05-section-c-q13-q16.png}
\caption{Q13--Q16: text processing. Original text is retained in the backups, while the edited files total 31 bytes.}
\label{Figure:HW2-C}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/06-section-d-q17-q20.png}
\caption{Q17--Q20: permissions and attributes. Group execute was already set; the append-only attribute and owner-only notes-file access are verified.}
\label{Figure:HW2-D}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/07-section-e-q21-q23.png}
\caption{Q21--Q23: links and file searches. The recent-modification search returns no matches.}
\label{Figure:HW2-E}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/08-section-f-q24-precheck.png}
\caption{Q24 and precheck: Victor records his process tree and confirms no existing user-owned sleep processes before the demonstration.}
\label{Figure:HW2-F1}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/09-section-f-q25-q27.png}
\caption{Q25--Q27: background process, SIGTERM, exit status 143, and the five largest processes by resident-memory usage.}
\label{Figure:HW2-F2}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/10-section-g-q28-q30.png}
\caption{Q28--Q30: archive creation, member listing, selected extraction, and successful content comparison.}
\label{Figure:HW2-G}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/11-section-h-q31-q34.png}
\caption{Q31--Q34: sockets, route, kernel, and boot-session history. The unprivileged socket listing contains no process details.}
\label{Figure:HW2-H}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/12-section-i-q35-q37.png}
\caption{Q35--Q37: installed coreutils version, available ripgrep package, and active cron state.}
\label{Figure:HW2-I}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[width=\linewidth,height=0.77\textwidth,keepaspectratio]{figures/13-section-j-q38-q40.png}
\caption{Q38--Q40: Victor uses the filename-and-contents interpretation for Q38, then verifies the CS export and removal of X from the environment.}
\label{Figure:HW2-J}
\end{figure}
+7 -3
View File
@@ -42,14 +42,18 @@ only where a generated secret cannot be retrieved.
\multicolumn{4}{l}{\small\itshape Account, Server, and Password Rule Register (continued)}\\ \multicolumn{4}{l}{\small\itshape Account, Server, and Password Rule Register (continued)}\\
\toprule \textbf{User} & \textbf{Server or service} & \textbf{Class} & \textbf{Rule and hint}\\ \midrule \toprule \textbf{User} & \textbf{Server or service} & \textbf{Class} & \textbf{Rule and hint}\\ \midrule
\endhead \bottomrule \endlastfoot \endhead \bottomrule \endlastfoot
Victor & Name.com registrar & Private account & Domain inviscid.dev; credentials held outside Git/report. Victor manages renewal; auto-renewal enabled.\\
\texttt{victor} & Gitea (Inviscid) & Unique & Site administrator; password kept outside this report and repository.\\
\texttt{payton} & Gitea (Inviscid) & Unique & Temporary password changed by Payton after first login; new password kept outside this report and repository.\\
Both authors & EC2 \texttt{ubuntu} & SSH key & Separate private keys remain on each author's workstation; only public keys authorized on EC2. No private keys in Git.\\
\texttt{root} & \texttt{ssw590-dev-01} & None & No password. Administrative access is obtained through \texttt{sudo} by a named account, so that privileged actions are attributable.\\ \texttt{root} & \texttt{ssw590-dev-01} & None & No password. Administrative access is obtained through \texttt{sudo} by a named account, so that privileged actions are attributable.\\
\texttt{payton} & \texttt{ssw590-dev-01}, \texttt{ssw590-build-01} & Derived & Used only to authenticate \texttt{sudo}; \gls{ssh} password authentication is disabled, so this secret never crosses the network. \texttt{payton} & \texttt{ssw590-dev-01}, \texttt{ssw590-build-01} & Derived & Used only to authenticate \texttt{sudo}; \gls{ssh} password authentication is disabled, so this secret never crosses the network.
\textit{Psst... sudo is a name, no?}\\ \textit{Psst... sudo is a name, no?}\\
\texttt{ssw-app} & \texttt{ssw590-dev-01} & Generated & Unprivileged service account for course applications. Secret held in a file readable only by that account, excluded from the repository, and rotated per deployment.\\ \texttt{ssw-app} & \texttt{ssw590-dev-01} & Generated & Unprivileged service account for course applications. Secret held in a file readable only by that account, excluded from the repository, and rotated per deployment.\\
Master & Password manager vault & Derived & A memorized password that isn't stored in its own vault. Master & Password manager vault & Derived & A memorized password that isn't stored in its own vault.
\textit{Surely it's secure, no lie?}\\ \textit{Surely it's secure, no lie?}\\
Root user & AWS account & Generated & Reserved for account recovery and billing; \gls{mfa} enforced, not used for routine work.\\ Root user & AWS account & Generated & Existing account policy: recovery and billing. Victor previously reported administrative root use and deferred MFA; current MFA state not verified.\\
\texttt{payton-admin} & AWS console, \gls{iam} & Generated & Day-to-day console account. \gls{mfa} enforced. Programmatic access uses short-lived credentials rather than a stored key pair.\\ Payton & AWS console, Identity Center & Separate identity & EC2 permission set configured previously; current MFA state not verified.\\
\end{tabularx} \end{tabularx}
\section{Handling and Storage} \section{Handling and Storage}
@@ -60,7 +64,7 @@ The following rules apply to every entry above.
\begin{itemize}[leftmargin=*] \begin{itemize}[leftmargin=*]
\item No password, private key, access token, or recovery code is recorded anywhere non-secure. \item No password, private key, access token, or recovery code is recorded anywhere non-secure.
A secret is referred to by the location that holds it. A secret is referred to by the location that holds it.
\item \gls{mfa} is enabled on every account that supports it. \item Target policy: enable \gls{mfa} wherever supported. This is not a claim that every current account has been verified as compliant.
\item A generated password is rotated by replacement. Any password is \item A generated password is rotated by replacement. Any password is
rotated immediately if it is disclosed, and knowing one password rotated immediately if it is disclosed, and knowing one password
does not provide insight into other passwords. does not provide insight into other passwords.
+111
View File
@@ -0,0 +1,111 @@
\chapter{Project Proposal}
\ChapterLabel{ProjectProposal}
\index{Inviscid}\index{Prometheus}\index{VictoriaMetrics}\index{observability}
\section{Inviscid: Simple Monitoring for Small Web Services}
Inviscid is a proposed customer-facing startup that helps small businesses
monitor the health and performance of their web applications. Customers would
use a dashboard to view availability, response times, request volume, and
error rates. The service would pair these measurements with plain-language
status summaries, such as ``Your website is responding slowly,'' to help
customers recognize problems without requiring extensive monitoring expertise.
The semester prototype will focus on one demonstration customer application,
building on our existing Node.js website. The broader startup concept is a
future direction; customer account management and support for multiple
customers are outside the initial prototype. Our current proof of concept is
the Dockerized Color Buttons website deployed on AWS, documented in
\ChapterRef{CloudDockerDeployment}. Monitoring and deployment automation are
proposed work rather than completed features.
\subsection{Sample Customer Task}
A customer opens the Inviscid dashboard to check their website's health.
During a controlled demonstration, we will generate normal traffic and then
introduce slow responses or failed requests in the sample application. The
dashboard should display the resulting changes in response times and error
rates, accompanied by a short status summary explaining the problem.
\section{Proposed Development and Operations Tools}
GitHub will provide source control and collaboration, and AWS will provide
the deployment environment. Table~\ref{Table:InviscidTools} lists the planned
stack. Entries marked undecided are candidates, not commitments; the team
may revise them as implementation requirements become clearer.
\begin{tabularx}{\linewidth}{L{3.2cm}Y}
\caption{Tentative Inviscid Tool Selection\label{Table:InviscidTools}}\\
\toprule \textbf{Purpose} & \textbf{Tool and status}\\ \midrule
\endfirsthead
\toprule \textbf{Purpose} & \textbf{Tool and status}\\ \midrule
\endhead \bottomrule \endlastfoot
Source control & GitHub; selected.\\
Application & Node.js and Express; proposed, building on the existing website.\\
Container packaging & Docker; selected.\\
Deployment & AWS EC2; selected for the prototype.\\
Metrics backend & Prometheus and single-node VictoriaMetrics; selected for comparison, final backend undecided.\\
Dashboard & Grafana or a simple custom dashboard; undecided.\\
Automated testing & Node.js's built-in test runner and a traffic-generation script; undecided.\\
Deployment automation & GitHub Actions for a manually triggered build, test, and deployment workflow; undecided.\\
Customer/settings database & Amazon DynamoDB, if persistent customer configuration is needed; undecided. Metrics will be stored in the selected metrics backend.\\
Security checks & GitHub Dependabot and automated dependency checks; undecided.\\
\end{tabularx}
Our longer-term goal is a deployment pipeline that can be initiated with one
button press. The proposal does not claim that this pipeline is implemented.
\section{Research Question and Planned Comparison}
\begin{quote}
How do Prometheus and VictoriaMetrics compare as metrics backends for
Inviscid's prototype in terms of configuration effort, dashboard integration,
query response time, and CPU, memory, and storage usage?
\end{quote}
We selected these tools because both can collect and query application metrics,
and VictoriaMetrics can scrape Prometheus-compatible endpoints. This allows
us to reuse the instrumented application while investigating different
backend implementations. Prometheus includes metric scraping, PromQL queries,
and alert-rule evaluation. VictoriaMetrics provides metric collection and
storage with MetricsQL queries; alert-rule evaluation uses a separate component
such as \texttt{vmalert}. Supported configuration options and query behavior
will be checked rather than assumed identical.
We plan to compare Prometheus with single-node VictoriaMetrics using the same
application, metrics, traffic workload, collection interval, retention period,
and equivalent AWS resources. Configuration effort will be documented through
setup steps and difficulties; dashboard integration through the work required
to display equivalent measurements. Repeated equivalent queries and controlled
traffic will support measurements of query response time and CPU, memory,
and storage usage. Results will apply to the tested prototype and workload;
we will not assume that either tool is faster or more resource-efficient.
\section{Preliminary SWOT Analysis}
Table~\ref{Table:InviscidSWOT} evaluates each tool independently in the context
of Inviscid. Strengths and weaknesses describe tool characteristics;
opportunities and threats describe possible circumstances affecting its use.
These are preliminary assessments, not measured experimental results.
\begingroup
\small
\begin{tabularx}{\linewidth}{L{2.2cm}YY}
\caption{Preliminary SWOT for Inviscid's Metrics Backends\label{Table:InviscidSWOT}}\\
\toprule \textbf{Category} & \textbf{Prometheus} & \textbf{VictoriaMetrics}\\ \midrule
\endfirsthead
\multicolumn{3}{l}{\small\itshape Preliminary SWOT (continued)}\\
\toprule \textbf{Category} & \textbf{Prometheus} & \textbf{VictoriaMetrics}\\ \midrule
\endhead \bottomrule \endlastfoot
Strengths & Established open-source ecosystem; built-in metric collection and alert-rule evaluation; expressive PromQL query language. & Simple single-node deployment; built-in metric collection and storage; flexible MetricsQL query language and multiple supported ingestion formats.\\
Weaknesses & Local storage is neither clustered nor replicated; operating at larger scales requires additional architecture. & Alert-rule evaluation requires a separate component; the single-node deployment depends on one server for availability.\\
Opportunities & Inviscid could extend monitoring to containers and infrastructure through its existing exporter ecosystem. & Inviscid could accept metrics from different customer environments through its supported ingestion formats.\\
Threats & Rapid growth in customer metrics could exceed available resources and increase operating costs. & Customers requiring uninterrupted monitoring could necessitate a cluster deployment, increasing infrastructure and maintenance costs.\\
\end{tabularx}
\endgroup
\section{Technical Sources}
The technical characteristics above are based on the following official
documentation, consulted October 6, 2026. Opportunities and threats are the
team's project-specific assessments. Specific tool versions will be recorded
when the comparison is implemented.
\begin{itemize}
\item \href{https://prometheus.io/docs/introduction/overview/}{Prometheus overview}: collection, queries, exporters, and alerting components.
\item \href{https://prometheus.io/docs/prometheus/latest/storage/}{Prometheus storage}: local storage limitations and remote storage interfaces.
\item \href{https://docs.victoriametrics.com/victoriametrics/}{VictoriaMetrics documentation}: single-node deployment, scraping, ingestion, MetricsQL, and supporting components.
\end{itemize}
+22 -7
View File
@@ -8,6 +8,7 @@
\usepackage{xspace} \usepackage{xspace}
\usepackage{xcolor} \usepackage{xcolor}
\usepackage{graphicx} \usepackage{graphicx}
\usepackage{pdflscape} % Full-page terminal evidence
\usepackage{fancyhdr} \usepackage{fancyhdr}
\usepackage{amsmath,amssymb} \usepackage{amsmath,amssymb}
\usepackage{array} \usepackage{array}
@@ -15,10 +16,15 @@
\usepackage{longtable} \usepackage{longtable}
\usepackage{tabularx} \usepackage{tabularx}
\usepackage{ltablex} \usepackage{ltablex}
\usepackage{float}
\keepXColumns \keepXColumns
\usepackage{enumitem} \usepackage{enumitem}
\usepackage{listings} \usepackage{listings}
\usepackage{minted}
\setminted{fontsize=\footnotesize,breaklines,frame=single}
\usepackage{tcolorbox} \usepackage{tcolorbox}
\usepackage{tikz}
\usetikzlibrary{positioning,arrows.meta,fit,backgrounds}
\usepackage{makeidx} \usepackage{makeidx}
\usepackage[colorlinks=true,citecolor=blue,linkcolor=blue,urlcolor=blue]{hyperref} \usepackage[colorlinks=true,citecolor=blue,linkcolor=blue,urlcolor=blue]{hyperref}
\usepackage[acronym,toc]{glossaries} \usepackage[acronym,toc]{glossaries}
@@ -34,17 +40,17 @@
\definecolor{devopsLightBlue}{RGB}{232,240,247} \definecolor{devopsLightBlue}{RGB}{232,240,247}
% Replace these values for each assignment. % Replace these values for each assignment.
\newcommand{\AssignmentTitle}{Compile Overleaf Document and Detail Kanban Setup} \newcommand{\AssignmentTitle}{Startup Foundation with Gitea}
\newcommand{\AssignmentNumber}{1} \newcommand{\AssignmentNumber}{4}
\newcommand{\DocumentVersion}{1.0} \newcommand{\DocumentVersion}{4.2}
\newcommand{\DocumentStatus}{Submitted Document} \newcommand{\DocumentStatus}{Submitted Document}
\newcommand{\CourseNumber}{SSW 590} \newcommand{\CourseNumber}{SSW 590}
\newcommand{\CourseName}{DevOps} \newcommand{\CourseName}{DevOps}
\newcommand{\Author}{Payton Bates} \newcommand{\Author}{Payton Bates and Victor Del Rio Foces}
\newcommand{\Instructor}{Darian Muresan, Ph.D.} \newcommand{\Instructor}{Darian Muresan, Ph.D.}
\newcommand{\Organization}{Stevens Institute of Technology} \newcommand{\Organization}{Stevens Institute of Technology}
\newcommand{\DocumentDate}{September 18, 2026} \newcommand{\DocumentDate}{October 15, 2026}
\newcommand{\DueDate}{September 21, 2026 by 11:59pm} \newcommand{\DueDate}{October 15, 2026 (submission time: see Canvas)}
\newcommand{\VERSION}{Version \DocumentVersion} \newcommand{\VERSION}{Version \DocumentVersion}
\hypersetup{ \hypersetup{
@@ -52,12 +58,14 @@
pdfauthor={\Author}, pdfauthor={\Author},
pdfcreator={Darian Muresan, Ph.D., with OpenAI Codex and Anthropic Claude Code}, pdfcreator={Darian Muresan, Ph.D., with OpenAI Codex and Anthropic Claude Code},
pdfsubject={Development environment configuration record for \CourseNumber\ Assignment \AssignmentNumber\ }, pdfsubject={Development environment configuration record for \CourseNumber\ Assignment \AssignmentNumber\ },
pdfkeywords={DevOps, Kanban, password, host, development environment, Amazon EC2, GitHub Projects} pdfkeywords={DevOps, Kanban, password, host, development environment, Amazon EC2, GitHub Projects, Docker, TeX Live}
} }
\newcolumntype{Y}{>{\raggedright\arraybackslash}X} \newcolumntype{Y}{>{\raggedright\arraybackslash}X}
\newcolumntype{L}[1]{>{\raggedright\arraybackslash}p{#1}} \newcolumntype{L}[1]{>{\raggedright\arraybackslash}p{#1}}
\setlength{\tabcolsep}{4pt} \setlength{\tabcolsep}{4pt}
\setlength{\headheight}{16pt}
\setlength{\emergencystretch}{2em}
\renewcommand{\arraystretch}{1.12} \renewcommand{\arraystretch}{1.12}
\setlength{\LTpre}{6pt} \setlength{\LTpre}{6pt}
\setlength{\LTpost}{6pt} \setlength{\LTpost}{6pt}
@@ -88,6 +96,8 @@
stringstyle=\color{devopsRed}, tabsize=2 stringstyle=\color{devopsRed}, tabsize=2
} }
\lstdefinestyle{cmd}{numbers=none, columns=fullflexible, upquote=true, belowskip=0pt, xleftmargin=5pt, xrightmargin=0pt}
\changenormalspacing{\singlespacing} \changenormalspacing{\singlespacing}
\begin{document} \begin{document}
\pagenumbering{roman} \pagenumbering{roman}
@@ -112,6 +122,11 @@
\include{chapters/devopsKanbanSetup} \include{chapters/devopsKanbanSetup}
\include{chapters/devopsHosts} \include{chapters/devopsHosts}
\include{chapters/devopsPasswords} \include{chapters/devopsPasswords}
\include{chapters/devopsLinuxCommands}
\include{chapters/devopsCloudDockerDeployment}
\include{chapters/devopsLatexDocker}
\include{chapters/devopsProjectProposal}
\include{chapters/devopsGiteaFoundation}
\appendix \appendix
\include{backmatter/devopsAIPromptLog} \include{backmatter/devopsAIPromptLog}
+4
View File
@@ -0,0 +1,4 @@
node_modules
npm-debug.log
.git
.DS_Store
+13
View File
@@ -0,0 +1,13 @@
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --omit=dev
COPY server.js ./
COPY public ./public
EXPOSE 3000
CMD ["npm", "start"]
+12
View File
@@ -0,0 +1,12 @@
{
"name": "ssw590-group-buttons",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
+27
View File
@@ -0,0 +1,27 @@
class ColorButtonsApp {
constructor(backgroundElement, statusElement) {
this.backgroundElement = backgroundElement;
this.statusElement = statusElement;
}
setColor(color) {
this.backgroundElement.style.backgroundColor = color;
this.statusElement.textContent = `Current background: ${color}`;
}
connectButton(button, color) {
button.addEventListener("click", () => this.setColor(color));
}
initialize() {
this.connectButton(document.getElementById("blueBtn"), "blue");
this.connectButton(document.getElementById("redBtn"), "red");
}
}
const application = new ColorButtonsApp(
document.body,
document.getElementById("status"),
);
application.initialize();
+43
View File
@@ -0,0 +1,43 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SSW590 Group — Color Buttons</title>
<style>
body {
margin: 0;
min-height: 100vh;
background-color: white;
font-family: Arial, sans-serif;
transition: background-color 0.3s ease;
}
main {
max-width: 600px;
margin: 50px auto;
padding: 24px;
text-align: center;
background-color: white;
border-radius: 12px;
}
button {
padding: 12px 24px;
margin: 10px;
font-size: 18px;
cursor: pointer;
}
</style>
<script src="app.js" defer></script>
</head>
<body>
<main>
<h1>Color Buttons</h1>
<p>Victor Del Rio Foces and Payton Bates — SSW590</p>
<button id="blueBtn" type="button">Blue</button>
<button id="redBtn" type="button">Red</button>
<p id="status" aria-live="polite">Current background: white</p>
</main>
</body>
</html>
+13
View File
@@ -0,0 +1,13 @@
import express from "express";
import path from "node:path";
import { fileURLToPath } from "node:url";
const directory = path.dirname(fileURLToPath(import.meta.url));
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static(path.join(directory, "public")));
app.listen(port, "0.0.0.0", () => {
console.log(`Group buttons website listening on port ${port}`);
});
+7
View File
@@ -0,0 +1,7 @@
git.inviscid.dev {
reverse_proxy gitea:3000
}
http://100.49.80.94, http://ec2-100-49-80-94.compute-1.amazonaws.com {
reverse_proxy ssw590-group-buttons:3000
}
+53
View File
@@ -0,0 +1,53 @@
# Inviscid Gitea foundation
This directory deploys Gitea at https://git.inviscid.dev/ behind a Caddy reverse proxy.
The official image version is pinned.
SQLite is `/data/gitea/gitea.db`; repositories and other application data share the
persistent named volume `inviscid_gitea_data`.
Never run `docker compose down --volumes` against the working service.
Only Caddy publishes host ports (80 and 443). Caddy terminates TLS and forwards to
`gitea:3000` over the private `inviscid_services` network. Gitea's port 3000 is also
bound to EC2 loopback only, for SSH-tunnel administration.
The Color Buttons container (`ssw590-group-buttons`) no longer publishes port 80; it
runs on `inviscid_services` and Caddy serves it at http://100.49.80.94.
No public backend or Git SSH firewall rule is needed.
From this directory on EC2:
```sh
sudo docker compose config --quiet
sudo docker compose pull
sudo docker compose up -d
sudo docker compose ps
```
`Caddyfile` must be in this directory before `up`; if it is missing, Docker creates a
directory in its place and Caddy fails to start. Caddy obtains and renews the
Let's Encrypt certificate automatically; certificate state persists in the
`inviscid_caddy_data` and `inviscid_caddy_config` volumes.
The Color Buttons container is managed outside Compose. To recreate it without a
published port:
```sh
sudo docker run -d --name ssw590-group-buttons --restart unless-stopped \
--network inviscid_services ssw590-group-buttons
```
For private browser access, use your own authorized key:
```sh
ssh -i /path/to/your-private-key -o ExitOnForwardFailure=yes -N -L 127.0.0.1:3000:127.0.0.1:3000 ubuntu@100.49.80.94
```
Open http://localhost:3000 while the tunnel stays open. Each team member must
have their public key authorized and their source IP allowed for administrative SSH.
Keep passwords, app.ini, database files, and volume contents out of Git and screenshots.
Sources:
- https://docs.gitea.com/installation/install-with-docker/
- https://docs.gitea.com/administration/config-cheat-sheet/
- https://caddyserver.com/docs/running#docker-compose
- https://caddyserver.com/docs/automatic-https
+49
View File
@@ -0,0 +1,49 @@
name: inviscid
services:
gitea:
image: docker.gitea.com/gitea:28.1.0
restart: unless-stopped
environment:
USER_UID: "1000"
USER_GID: "1000"
GITEA__database__DB_TYPE: sqlite3
GITEA__database__PATH: /data/gitea/gitea.db
GITEA__server__DOMAIN: git.inviscid.dev
GITEA__server__ROOT_URL: https://git.inviscid.dev/
GITEA__server__HTTP_PORT: "3000"
GITEA__server__DISABLE_SSH: "true"
GITEA__service__DISABLE_REGISTRATION: "true"
ports:
- "127.0.0.1:3000:3000"
volumes:
- gitea_data:/data
networks:
- services
caddy:
image: caddy:2.10.2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- services
depends_on:
- gitea
volumes:
gitea_data:
name: inviscid_gitea_data
caddy_data:
name: inviscid_caddy_data
caddy_config:
name: inviscid_caddy_config
networks:
services:
name: inviscid_services
+2
View File
@@ -0,0 +1,2 @@
sample
*.pdf
+20
View File
@@ -0,0 +1,20 @@
FROM debian:trixie-slim
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
texlive-latex-base \
texlive-latex-recommended \
texlive-fonts-recommended \
lmodern \
latexmk \
&& rm -rf /var/lib/apt/lists/*
# Run as an unprivileged user; keep TeX's font cache out of the mounted folder.
RUN useradd --create-home --uid 1000 latex
ENV TEXMFVAR=/tmp/texmf-var
USER latex
WORKDIR /work
ENTRYPOINT ["latexmk", "-pdf", "-interaction=nonstopmode", "-halt-on-error"]
CMD ["hello.tex"]
+44
View File
@@ -0,0 +1,44 @@
\documentclass[11pt]{article}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{amsmath}
\usepackage{booktabs}
\usepackage[colorlinks=true]{hyperref}
\title{SSW 590 --- LaTeX Docker Test}
\author{Payton Bates and Victor Del Rio Foces}
\date{\today}
\begin{document}
\maketitle
\section{Purpose}
This document was compiled by pdf\LaTeX\ from TeX Live inside a Docker
container. Section~\ref{sec:checks} lists its features.
\section{Checks}\label{sec:checks}
\begin{itemize}
\item Mathematics: $e^{i\pi} + 1 = 0$ and
\begin{equation}
\sum_{k=1}^{n} k = \frac{n(n+1)}{2}.
\end{equation}
\item Cross-reference: Equation~\eqref{eq:area} resolves.
\item Table:
\end{itemize}
\begin{center}
\begin{tabular}{lr}
\toprule
Item & Value \\
\midrule
Engine & pdf\LaTeX \\
Distribution & TeX Live \\
\bottomrule
\end{tabular}
\end{center}
\begin{equation}\label{eq:area}
A = \pi r^2
\end{equation}
\end{document}
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

@@ -0,0 +1,60 @@
\begin{tikzpicture}[
font=\footnotesize,
>={Stealth[length=2mm]},
box/.style={draw, rounded corners=2pt, align=center, fill=white, inner sep=3pt},
ctr/.style={box, fill=devopsLightBlue, text width=34mm},
vol/.style={box, fill=devopsGray!12, text width=36mm},
ext/.style={box, text width=30mm},
pub/.style={->, thick, devopsBlue},
priv/.style={->, thick, devopsGreen},
adm/.style={->, thick, devopsGray, dashed},
blk/.style={->, thick, devopsRed, densely dotted},
lbl/.style={font=\scriptsize, fill=white, inner sep=1pt}
]
% External clients
\node[ext] (browser) at (0, 2.55) {Browser / Git client (any source)};
\node[ext] (ip) at (0, 1.5) {Instructor browser\\\texttt{\scriptsize http://100.49.80.94}};
\node[ext] (admin) at (0,-0.8) {Team admin (approved /32 sources)};
\node[ext] (scan) at (0,-2.5) {Any other source};
% EC2 contents
\node[vol] (cdata) at (6.6, 4.8) {\textbf{Volume} \texttt{\scriptsize inviscid\_caddy\_data}\\certificates, ACME account};
\node[ctr] (caddy) at (6.6, 2.15) {\textbf{Caddy} \texttt{\scriptsize caddy:2.10.2}\\TLS termination;\\only 80/443 publisher};
\node[ctr] (gitea) at (11.4, 3.0) {\textbf{Gitea} \texttt{\scriptsize 28.1.0}\\\texttt{\scriptsize gitea:3000}};
\node[ctr, text width=40mm] (buttons) at (11.4, 1.3) {\textbf{Color Buttons}\\\texttt{\scriptsize ssw590-group-buttons:3000}};
\node[vol] (gdata) at (16.0, 3.0) {\textbf{Volume} \texttt{\scriptsize inviscid\_gitea\_data}\\repositories, config,\\SQLite \texttt{\scriptsize gitea.db}};
\node[box, text width=34mm] (sshd) at (6.6,-0.8) {\textbf{sshd} (EC2 host), TCP 22};
\begin{scope}[on background layer]
\node[draw=devopsGreen, dashed, rounded corners, fill=devopsGreen!5, inner sep=3mm,
fit=(caddy)(gitea)(buttons),
label={[font=\scriptsize, text=devopsGreen, anchor=north west, yshift=-1pt]south west:Docker network \texttt{inviscid\_services} (private)}] (net) {};
\node[draw=devopsBlue, thick, rounded corners, inner sep=4mm,
fit=(net)(sshd)(gdata)(cdata),
label={[font=\scriptsize\bfseries, text=devopsBlue]above:EC2 \texttt{ssw590-group-web} (\texttt{us-east-1}) --- Elastic IP 100.49.80.94, no IPv6}] (ec2) {};
\end{scope}
% Security group wall
\coordinate (sgTop) at (3.25, 5.9);
\coordinate (sgBot) at (3.25,-3.0);
\draw[line width=2pt, devopsRed] (sgTop) -- (sgBot);
\node[font=\scriptsize, text=devopsRed, align=center, anchor=north, fill=white, inner sep=1pt] at ([yshift=-1mm]sgTop) {Security group\\\texttt{ssw590-web-sg}};
% Public flows (allowed by the security group)
\draw[pub] (browser.east) -- node[lbl, above, pos=0.72]{443; 80$\to$308} (caddy.west |- browser.east);
\draw[pub] (ip.east) -- node[lbl, above, pos=0.72]{80 HTTP} (caddy.west |- ip.east);
% Private flows
\draw[priv] (caddy.east |- gitea.west) -- node[lbl, above]{\texttt{\scriptsize git.inviscid.dev}} (gitea.west);
\draw[priv] (caddy.east |- buttons.west) -- node[lbl, below, pos=0.4]{by IP} (buttons.west);
\draw[priv] (caddy.north) -- (cdata.south);
\draw[priv] ([yshift=2mm]gitea.east) -- ([yshift=2mm]gdata.west |- gitea.east);
% Administrative path
\draw[adm] (admin.east) -- node[lbl, above, pos=0.72]{22 SSH} (sshd.west);
\draw[adm] (sshd.east) -- node[lbl, below]{SSH tunnel to \texttt{\scriptsize 127.0.0.1:3000}} (13.75,-0.8) |- ([yshift=-3mm]gitea.east);
% Blocked
\draw[blk] (scan.east) -- node[lbl, above, pos=0.62]{22, 3000, 222, 2019} ([xshift=-1.2mm]sgBot |- scan.east);
\node[text=devopsRed, font=\large\bfseries] at (sgBot |- scan.east) {$\times$};
\end{tikzpicture}
Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

+20 -7
View File
@@ -1,7 +1,20 @@
This document records the development-environment configuration for the first This document records the development environment and working practices used
SSW 590 assignment. It describes the Kanban board used to plan and track the in SSW 590. It is extended with each assignment, and the document history
work, the hosts the environment will comprise, and the password rules that identifies what each revision changed. Its chapters currently cover planning and tracking
will govern accounts on those hosts. The environment is specified here as a the work, the hosts and accounts that make up the environment, and the
plan; provisioning and its supporting evidence are deferred to a later command-line and operational work performed in it. The host inventory
submission. Password rules are recorded as derivation hints, so that no distinguishes machines in service from those still planned.
credential appears in this document. Assignment 3 adds the class-based Color Buttons application deployed in Docker
on Amazon EC2, with public browser and terminal verification evidence;
a Docker container that compiles \LaTeX\ documents as Overleaf does;
and the Inviscid monitoring-service proposal, including a planned comparison
of Prometheus and VictoriaMetrics.
Assignment 4 records the persistent Gitea service on the existing EC2 host,
Elastic IP association, and separate team access. It also records Student Pack
domain redemption, inviscid.dev registration and independently verified DNS,
organization ownership, repository import, and the first open issue-linked PR.
It also records Caddy-terminated HTTPS with a Let's Encrypt certificate and
renewal test, the EC2 security group with external exposure tests, a
container-replacement persistence test, the product backlog, and two reviewed,
issue-linked pull requests.
+8
View File
@@ -11,5 +11,13 @@ link each entry to the changed chapter, table, figure, or controlled artifact.
\multicolumn{4}{l}{\small\itshape Document Update History (continued)}\\ \multicolumn{4}{l}{\small\itshape Document Update History (continued)}\\
\toprule \textbf{Date / Version} & \textbf{Author Initials} & \textbf{Change} & \textbf{Changed At}\\ \midrule \toprule \textbf{Date / Version} & \textbf{Author Initials} & \textbf{Change} & \textbf{Changed At}\\ \midrule
\endhead \bottomrule \endlastfoot \endhead \bottomrule \endlastfoot
October 15, 2026 / 4.2 & PCB & Added Caddy HTTPS with certificate, redirect, and forced-reissue renewal evidence; certificate-option comparison; security-group, network ACL, port-mapping, and external exposure tests; the container-replacement persistence test; architecture diagram and operations notes; issues 3--6 and reviewed PR 7. Updated the Hosts entry (HTTPS verified), Payton's Passwords hint, abstract, introduction, Cloud Docker note, glossary, and AI log. & \ChapterRef{GiteaFoundation}, \ChapterRef{Hosts}, \ChapterRef{Passwords}, \ChapterRef{CloudDockerDeployment}, \ChapterRef{AIPromptLog}\\
October 15, 2026 / 4.1 & VDRF & Recorded Student Pack redemption, inviscid.dev registration/renewal, DNS record and two independent lookups, organization ownership, repository import, issue 1, and open PR 2. Updated Hosts domain entry, Passwords registrar/account hints, abstract, AI log, and deployment handoff notes; HTTPS and reviewed merges remain pending. & \ChapterRef{GiteaFoundation}, \ChapterRef{Hosts}, \ChapterRef{Passwords}, \ChapterRef{AIPromptLog}\\
October 15, 2026 / 4.0 & VDRF & Added the initial Gitea foundation chapter with deployment, SQLite, Elastic IP, access evidence, and explicit pending DNS/HTTPS/workflow items. Updated Hosts inventory and cleanup, Passwords account/hint table and policy-versus-observation wording, metadata, abstract, introduction, and AI log. & \ChapterRef{GiteaFoundation}, \ChapterRef{Hosts}, \ChapterRef{Passwords}, \ChapterRef{AIPromptLog}\\
October 6, 2026 / 3.2 & VDRF & Replaced the preliminary tool proposal with Inviscid, its sample customer task, tentative stack, research question, and independent SWOT assessments of Prometheus and VictoriaMetrics. Updated the abstract and AI log. & \ChapterRef{ProjectProposal}, \ChapterRef{AIPromptLog}\\
October 5, 2026 / 3.1 & PCB & Added the LaTeX Docker chapter for compiling a sample document with its sources, build and run commands, and test evidence. Updated the document title, keywords, glossary, abstract, introduction, and hosts. & \ChapterRef{LatexDocker}, \ChapterRef{Hosts}, \ChapterRef{Introduction}, \ChapterRef{CloudDockerDeployment}\\
October 5, 2026 / 3.0 & VDRF & Added AWS Docker deployment, class-based website sources, execution evidence, and a draft tool-comparison proposal. Updated Assignment 3 metadata and AI log. & \ChapterRef{CloudDockerDeployment}, \ChapterRef{ProjectProposal}, \ChapterRef{AIPromptLog}\\
September 27, 2026 / 2.1 & VDRF & Added Victor's Q1--Q40 execution screenshots and result interpretation; distinguished the two environments and command variants. & \ChapterRef{LinuxCommands}, \ChapterRef{AIPromptLog}\\
September 26, 2026 / 2.0 & PCB, VDRF & Added the Linux Commands chapter: working-directory setup with terminal input/output figures, command explanations and interpretation of results, and solutions to the Linux problem set. Updated document metadata and authorship for Assignment 2 and the two-person team. & \ChapterRef{LinuxCommands}, \ChapterRef{Introduction}, \ChapterRef{Hosts}, \ChapterRef{AIPromptLog}\\
September 18, 2026 / 1.0 & PCB & Created this document from the \CourseNumber\ assignment template. Removed instructional/example chapters, set document metadata and authorship, and rewrote the abstract and introduction to describe this environment rather than the template. Added the Kanban Setup, Passwords, and Hosts chapters, the account and password rules, and the planned hosts. & \ChapterRef{Introduction}, \ChapterRef{KanbanSetup}, \ChapterRef{Passwords}, \ChapterRef{Hosts}, \ChapterRef{AIPromptLog}\\ September 18, 2026 / 1.0 & PCB & Created this document from the \CourseNumber\ assignment template. Removed instructional/example chapters, set document metadata and authorship, and rewrote the abstract and introduction to describe this environment rather than the template. Added the Kanban Setup, Passwords, and Hosts chapters, the account and password rules, and the planned hosts. & \ChapterRef{Introduction}, \ChapterRef{KanbanSetup}, \ChapterRef{Passwords}, \ChapterRef{Hosts}, \ChapterRef{AIPromptLog}\\
\end{tabularx} \end{tabularx}
+17
View File
@@ -0,0 +1,17 @@
# Allow minted to call its external highlighter (Overleaf enables this by default).
$pdflatex = 'pdflatex -shell-escape %O %S';
# Run makeglossaries whenever the glossary/acronym input files change.
# Overleaf does this automatically; local latexmk (e.g. VS Code LaTeX Workshop) does not.
add_cus_dep('glo', 'gls', 0, 'run_makeglossaries');
add_cus_dep('acn', 'acr', 0, 'run_makeglossaries');
sub run_makeglossaries {
my ($base_name, $path) = fileparse($_[0]);
my @args = ('-d', $path, $base_name);
unshift @args, '-q' if $silent;
return system('makeglossaries', @args);
}
push @generated_exts, 'glo', 'gls', 'glg', 'acn', 'acr', 'alg';
$clean_ext .= ' %R.ist %R.xdy';