Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1e37fea4a | ||
|
|
e3f07d0f84 | ||
|
|
da342d40dd | ||
|
|
3eac4a694f | ||
|
|
89359a6ce3 | ||
|
|
1d7c528795 | ||
|
|
0cd8561d44 | ||
|
|
52880f3d85 | ||
|
|
aeba4fe443 | ||
|
|
64fd3da0fd | ||
|
|
da6a369420 |
No files matched your search
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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}}
|
||||
@@ -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
|
||||
|
||||
@@ -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}.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -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}
|
||||
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 580 KiB |
|
Before Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 149 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 185 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 602 KiB |
|
After Width: | Height: | Size: 677 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 96 KiB |
@@ -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.
|
||||
@@ -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}\\
|
||||
|
||||