11 Commits
Author SHA1 Message Date
Payton Bates e1e37fea4a Merge GitHub main (PR #1 merge) into main 2026-10-15 23:42:04 -04:00
Payton Bates e3f07d0f84 Merge payton/gitea-report: complete Assignment 4 report 2026-10-15 23:39:36 -04:00
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
Victor Javier Del Rio Foces 52880f3d85 Merge pull request #1 from byterand/codex/gitea-foundation-docs
Codex/gitea foundation docs
2026-10-15 20:04:44 -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
44 changed files with 742 additions and 63 deletions

No files matched your search

+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.
+57
View File
@@ -139,3 +139,60 @@ explicitly marked undecided.\\
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.
+9 -1
View File
@@ -2,7 +2,9 @@
\newacronym{ami}{AMI}{Amazon Machine Image}
\newacronym{iam}{IAM}{identity and access management}
\newacronym{mfa}{MFA}{multi-factor authentication}
\newacronym{acme}{ACME}{Automatic Certificate Management Environment}
\newacronym{ssh}{SSH}{secure shell}
\newacronym{tls}{TLS}{Transport Layer Security}
\newacronym{wip}{WIP}{work in progress}
\newacronym{wsl}{WSL}{Windows Subsystem for Linux}
\newglossaryentry{evidence}{name={evidence},description={A retained record of an
@@ -13,4 +15,10 @@
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}}
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}}
+2
View File
@@ -117,6 +117,8 @@ curl -I http://127.0.0.1
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
+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}.
+13 -21
View File
@@ -36,7 +36,7 @@ only once it exists and is reachable.
\multicolumn{4}{l}{\small\itshape Host Inventory (continued)}\\
\toprule \textbf{Host name} & \textbf{Role} & \textbf{Platform} & \textbf{Status}\\ \midrule
\endhead \bottomrule \endlastfoot
\texttt{ssw590-group-web} & Group deployment target. Runs the website in \ChapterRef{CloudDockerDeployment}. & Ubuntu 26.04 LTS on Amazon EC2, \texttt{t3.micro} & In service\\
\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\\
\end{tabularx}
@@ -44,22 +44,15 @@ only once it exists and is reachable.
\index{hosts!access model}
\index{least privilege}
\texttt{ssw590-group-web} is shared by both authors and runs under Victor's AWS account,
where each author has a separate IAM identity. The following rules are the target access policy
for course Linux hosts:
\begin{itemize}[leftmargin=*]
\item Interactive access is by \gls{ssh} public key only.
Password authentication is disabled in the server configuration.
The local account password recorded in \ChapterRef{Passwords} authenticates \texttt{sudo} only.
\item Inbound \gls{ssh} is restricted by source address to the local workstation
rather than opened to the public Internet.
\item Day-to-day work is performed by an unprivileged account. Administrative
rights are granted per command rather than by general membership in the
\texttt{sudo} group.
\item Teammates who require access after teams are formalized
receive a separate named account rather than a shared login, so that
access can be revoked individually and actions remain attributable.
\end{itemize}
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
administrator; Payton has a separate regular Gitea account. Both are members
of the Inviscid organization Owners team. Linux account-level
attribution is shared; per-person Linux accounts remain a future improvement.
Administrative SSH is restricted to approved source IPv4 addresses. The
observed rules and connection limitations are in \ChapterRef{GiteaFoundation}.
This inventory does not claim that SSH password authentication or sudo policy
was independently audited during this exercise.
\section{Cost and Cleanup}
\index{hosts!cost}
@@ -73,7 +66,6 @@ compute cost of the environment. It is therefore left unprovisioned unless
required in the future, and would be stopped between uses rather than run continuously.
Course hosts are disposable. Teardown consists of terminating the instance and
deleting its storage volume. \texttt{ssw590-group-web} uses an automatically
assigned public address rather than a reserved static address, so there is no
address to release; its address changes if the instance is stopped and
started.
deleting its storage volume. \texttt{ssw590-group-web} uses Elastic IP \texttt{100.49.80.94}, associated on October 12, 2026.
Release that allocation when no longer required; stopping or terminating an
instance alone does not release the Elastic IP.
+5 -2
View File
@@ -10,7 +10,8 @@ This document records the configuration of the development environment used for
\CourseNumber\ and the working practices followed to produce it.
Each chapter covers one subject, and each assignment adds or revises chapters
as recorded in the document history. The latest revision for Assignment~\AssignmentNumber\
adds \ChapterRef{CloudDockerDeployment}, \ChapterRef{LatexDocker}, and \ChapterRef{ProjectProposal}.
adds \ChapterRef{GiteaFoundation}, recording the Gitea deployment, HTTPS, firewall,
persistence testing, and team workflow.
Earlier coursework chapters are retained.
\section{Scope and Constraints}
@@ -39,7 +40,7 @@ Due date & \DueDate\\
Instructor & \Instructor\\
Team & \Author\\
Board & GitHub Projects, \texttt{SSW 590} (private; captured as figures in \ChapterRef{KanbanSetup})\\
Required deliverables & Compiled group manual; cloud deployment, LaTeX Docker exercise, tool proposal, and updated history\\
Required deliverables & Compiled group manual with the eight Gitea rubric sections, configuration and test evidence, updated history, Hosts, and Passwords hints\\
Team policy & Group submission\\ \bottomrule
\end{tabularx}
@@ -62,6 +63,8 @@ the Template Attribution page. The template's instructional example chapter and
its template-maintenance appendices have been removed, since they describe how
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}
\index{AI assistance}
AI assistance was used in preparing this document for drafting, review, and
+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)}\\
\toprule \textbf{User} & \textbf{Server or service} & \textbf{Class} & \textbf{Rule and hint}\\ \midrule
\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{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?}\\
\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.
\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.\\
\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.\\
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.\\
Payton & AWS console, Identity Center & Separate identity & EC2 permission set configured previously; current MFA state not verified.\\
\end{tabularx}
\section{Handling and Storage}
@@ -60,7 +64,7 @@ The following rules apply to every entry above.
\begin{itemize}[leftmargin=*]
\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.
\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
rotated immediately if it is disclosed, and knowing one password
does not provide insight into other passwords.
+8 -5
View File
@@ -23,6 +23,8 @@
\usepackage{minted}
\setminted{fontsize=\footnotesize,breaklines,frame=single}
\usepackage{tcolorbox}
\usepackage{tikz}
\usetikzlibrary{positioning,arrows.meta,fit,backgrounds}
\usepackage{makeidx}
\usepackage[colorlinks=true,citecolor=blue,linkcolor=blue,urlcolor=blue]{hyperref}
\usepackage[acronym,toc]{glossaries}
@@ -38,17 +40,17 @@
\definecolor{devopsLightBlue}{RGB}{232,240,247}
% Replace these values for each assignment.
\newcommand{\AssignmentTitle}{Cloud Docker Deployment, LaTeX Docker, and Project Proposal}
\newcommand{\AssignmentNumber}{3}
\newcommand{\DocumentVersion}{3.2}
\newcommand{\AssignmentTitle}{Startup Foundation with Gitea}
\newcommand{\AssignmentNumber}{4}
\newcommand{\DocumentVersion}{4.2}
\newcommand{\DocumentStatus}{Submitted Document}
\newcommand{\CourseNumber}{SSW 590}
\newcommand{\CourseName}{DevOps}
\newcommand{\Author}{Payton Bates and Victor Del Rio Foces}
\newcommand{\Instructor}{Darian Muresan, Ph.D.}
\newcommand{\Organization}{Stevens Institute of Technology}
\newcommand{\DocumentDate}{October 6, 2026}
\newcommand{\DueDate}{October 6, 2026 by 11:59pm}
\newcommand{\DocumentDate}{October 15, 2026}
\newcommand{\DueDate}{October 15, 2026 (submission time: see Canvas)}
\newcommand{\VERSION}{Version \DocumentVersion}
\hypersetup{
@@ -124,6 +126,7 @@
\include{chapters/devopsCloudDockerDeployment}
\include{chapters/devopsLatexDocker}
\include{chapters/devopsProjectProposal}
\include{chapters/devopsGiteaFoundation}
\appendix
\include{backmatter/devopsAIPromptLog}
+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
}
+26 -28
View File
@@ -1,13 +1,16 @@
# Inviscid Gitea foundation
This is the temporary, Gitea-only deployment while Student Pack registration is pending.
The official image version is pinned; verify that it pulls before starting the service.
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`. Container replacement must preserve it.
persistent named volume `inviscid_gitea_data`.
Never run `docker compose down --volumes` against the working service.
Only EC2 loopback port 3000 is published. Initial browser access uses an SSH tunnel.
The existing Color Buttons container remains unchanged on port 80.
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:
@@ -19,31 +22,18 @@ sudo docker compose up -d
sudo docker compose ps
```
This initial configuration is NOT the final hostname-based HTTPS deployment.
After domain registration, replace DOMAIN and ROOT_URL with the actual hostname
and HTTPS URL. Payton can attach Caddy to `inviscid_services` and forward to
`gitea:3000`, publish the proxy web ports, persist certificate state, and resolve
the existing website's port-80 mapping. Remove the loopback mapping if no longer needed.
`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.
Self-registration is disabled. Create the initial administrator through the installer;
create Payton's separate account using administration after initialization.
Keep passwords, app.ini, database files, and volume contents out of Git and screenshots.
The Color Buttons container is managed outside Compose. To recreate it without a
published port:
Sources:
- https://docs.gitea.com/installation/install-with-docker/
- https://docs.gitea.com/administration/config-cheat-sheet/
## Recorded deployment (October 12, 2026)
- EC2: `ssw590-group-web`, `i-025a57432604c8d4b`, us-east-1.
- Current public Elastic IP: `100.49.80.94` (replaces `54.221.175.174`).
- Remote configuration directory: `/home/ubuntu/gitea`.
- Observed image digest: `docker.gitea.com/gitea@sha256:f505a0d3a41323b786e05942fc7a4075e07ae7c169fd9485649aa55ee543a21a`.
- Gitea initialized with site title Inviscid; victor is administrator and payton is a regular user.
- Local HTTP check returned 200; named-volume mount and SQLite file were verified.
- Both containers remained running following Elastic IP association.
- Domain registration, public HTTPS, renewal verification, external firewall checks,
container-replacement persistence test, and team PR workflow remain incomplete.
```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:
@@ -53,3 +43,11 @@ ssh -i /path/to/your-private-key -o ExitOnForwardFailure=yes -N -L 127.0.0.1:300
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
+21 -3
View File
@@ -9,9 +9,8 @@ services:
USER_GID: "1000"
GITEA__database__DB_TYPE: sqlite3
GITEA__database__PATH: /data/gitea/gitea.db
# Temporary URLs for SSH-tunnel initialization; replace after registration.
GITEA__server__DOMAIN: localhost
GITEA__server__ROOT_URL: http://localhost:3000/
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"
@@ -22,9 +21,28 @@ services:
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:
@@ -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.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 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.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

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.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+9
View File
@@ -9,3 +9,12 @@ 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.
+3
View File
@@ -11,6 +11,9 @@ link each entry to the changed chapter, table, figure, or controlled artifact.
\multicolumn{4}{l}{\small\itshape Document Update History (continued)}\\
\toprule \textbf{Date / Version} & \textbf{Author Initials} & \textbf{Change} & \textbf{Changed At}\\ \midrule
\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}\\