From 89359a6ce378ae82efe9de0b06cff003abf84b6e Mon Sep 17 00:00:00 2001 From: Payton Bates Date: Thu, 15 Oct 2026 22:26:18 -0400 Subject: [PATCH] feat: architecture diagram and operations explanation --- chapters/devopsGiteaFoundation.tex | 47 +++++++++++++-- devopsAssignment.tex | 2 + .../giteaArchitecture.tex | 60 +++++++++++++++++++ 3 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 figures/devopsGiteaFoundation/giteaArchitecture.tex diff --git a/chapters/devopsGiteaFoundation.tex b/chapters/devopsGiteaFoundation.tex index ec350c4..1c14312 100644 --- a/chapters/devopsGiteaFoundation.tex +++ b/chapters/devopsGiteaFoundation.tex @@ -374,9 +374,16 @@ not confirmed. Payton has taken ownership of the remaining Gitea tasks. The initial main push is not counted as a reviewed PR. \section{Architecture and Operation} -The current private initialization path is shown in -Figure~\ref{Figure:GiteaInitialPath}. Payton will add a final deployment diagram -showing browser HTTPS to Caddy and private forwarding to Gitea. +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} + +The initial October 12 access path, before Caddy was deployed, is kept for +reference in Figure~\ref{Figure:GiteaInitialPath}. \begin{figure}[htbp] \centering \fbox{\parbox{0.92\linewidth}{\centering @@ -387,7 +394,7 @@ $\downarrow$ EC2: 100.49.80.94\\ EC2 loopback:3000 to Gitea container:3000\\ $\downarrow$ persistent named volume\\ \texttt{inviscid\_gitea\_data}: repositories, SQLite, configuration}} -\caption{Initial SSH-tunnel access path. Caddy and hostname-based HTTPS are not yet verified.} +\caption{October 12: initial SSH-tunnel-only access path, before Caddy and hostname-based HTTPS were deployed.} \label{Figure:GiteaInitialPath} \end{figure} @@ -406,8 +413,40 @@ The following are operational commands, not additional claimed test results: cd ~/gitea sudo docker compose ps sudo docker compose logs --tail=40 gitea +sudo docker compose logs --tail=40 caddy sudo docker compose restart gitea +sudo docker compose restart caddy \end{minted} + +\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), the Color Buttons container's port 3000, Caddy's admin port +2019, and the SQLite file, which has no network port. + +\paragraph{Troubleshooting.} If \url{https://git.inviscid.dev} fails, check in +order: (1) \texttt{docker compose ps} shows both services 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. Restarting one service leaves its volume intact. + +\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 +(Section~2.8 of the assignment) was not performed. + Closing a workstation tunnel does not stop Gitea on EC2. Reconnect the tunnel for browser access. Container replacement should retain the named volume; never delete that volume as part of the persistence check. Before destructive diff --git a/devopsAssignment.tex b/devopsAssignment.tex index 3a65f8f..4a574bb 100644 --- a/devopsAssignment.tex +++ b/devopsAssignment.tex @@ -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} diff --git a/figures/devopsGiteaFoundation/giteaArchitecture.tex b/figures/devopsGiteaFoundation/giteaArchitecture.tex new file mode 100644 index 0000000..e416720 --- /dev/null +++ b/figures/devopsGiteaFoundation/giteaArchitecture.tex @@ -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}