update parts of the practical part
This commit is contained in:
parent
8319a9c07f
commit
dea1a6f4fe
@ -1,7 +1,15 @@
|
||||
% =========================================================================== %
|
||||
\part{Practical part}
|
||||
|
||||
\n{1}{Kudos}
|
||||
\n{1}{Introduction}
|
||||
|
||||
As a part of the task was to build an actual Password Compromise Monitoring
|
||||
Tool, the development process, the tools used and the outcome are all described
|
||||
in the following sections. There is also a section concerned with application
|
||||
architecture, whereby relevant choices and decision motifs are explained.
|
||||
|
||||
|
||||
\n{2}{Kudos}
|
||||
|
||||
The program that has been developed as part of this thesis used and utilised a
|
||||
great deal of free (as in \textit{freedom}) and open-source software in the
|
||||
@ -63,7 +71,8 @@ the following commands (the \% sign denotes the shell prompt):
|
||||
|
||||
\vspace{\parskip}
|
||||
\begin{lstlisting}[language=bash, caption={Verifying signature of a git commit},
|
||||
label=gitverif, basicstyle=\linespread{0.9}\footnotesize\ttfamily]
|
||||
label=gitverif, basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg}]
|
||||
% cd <cloned project dir>
|
||||
% git show --show-signature <commit>
|
||||
% # alternatively:
|
||||
@ -85,7 +94,8 @@ from inside of the cloned repository:
|
||||
|
||||
\vspace{\parskip}
|
||||
\begin{lstlisting}[language=bash, caption={Prepare allowed signers file and signature format for git},
|
||||
label=gitsshprep, basicstyle=\linespread{0.9}\footnotesize\ttfamily]
|
||||
label=gitsshprep, basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg}]
|
||||
% # set the signature format for the local repository.
|
||||
% git config --local gpg.format ssh
|
||||
% # save the public key.
|
||||
@ -168,26 +178,25 @@ kernel version 6.3.x.
|
||||
|
||||
\n{2}{Source code repositories}\label{sec:repos}
|
||||
|
||||
The git repository containing source code of the \texttt{pcmt} project:\\
|
||||
\url{https://git.dotya.ml/mirre-mt/pcmt.git}.
|
||||
The git repository hosting the \texttt{pcmt} configuration schema:\\
|
||||
\url{https://git.dotya.ml/mirre-mt/pcmt-config-schema.git}.
|
||||
The repository containing the \LaTeX{} source code of this thesis:\\
|
||||
\url{https://git.dotya.ml/mirre-mt/masters-thesis.git}.
|
||||
|
||||
All of the pertaining source code was published in repositories on a publicly
|
||||
available git server operated by the author, the reasoning \emph{pro}
|
||||
self-hosting being that it is the preferred way of guaranteed autonomy over
|
||||
one's source code, as opposed to large silos owned by big corporations having a
|
||||
track record of arguably not always deciding with user's best interest in mind
|
||||
(although recourse has been observed~\cite{ytdl}). When these providers act on
|
||||
impulse or under public pressure they can potentially at least temporarily
|
||||
disrupt their user's operations, thus not only beholding their user to their
|
||||
lengthy \emph{terms of service} that \emph{can change at any time}, but also
|
||||
factors outside their control. Granted, decentralisation can take a toll on
|
||||
discoverability of the project, but that is not a concern here.
|
||||
|
||||
The git repository containing source code of the \texttt{pcmt} project:\\
|
||||
\url{https://git.dotya.ml/mirre-mt/pcmt.git}.
|
||||
|
||||
The git repository hosting the \texttt{pcmt} configuration schema:\\
|
||||
\url{https://git.dotya.ml/mirre-mt/pcmt-config-schema.git}.
|
||||
|
||||
The repository containing the \LaTeX{} source code of this thesis:\\
|
||||
\url{https://git.dotya.ml/mirre-mt/masters-thesis.git}.
|
||||
impulse or under public pressure they can potentially (at least temporarily)
|
||||
disrupt operations of their users. Thus they are not only beholding their users
|
||||
to lengthy \emph{terms of service} that \emph{are subject to change at any
|
||||
given moment}, but also outside factors beyond their control. Granted,
|
||||
decentralisation can take a toll on discoverability of the project, but that is
|
||||
only a concern if rapid market penetration is a goal.
|
||||
|
||||
|
||||
\n{2}{Toolchain}
|
||||
@ -195,19 +204,21 @@ The repository containing the \LaTeX{} source code of this thesis:\\
|
||||
Throughout the creation of this work, the \emph{then-current} version of the Go
|
||||
programming language was used, i.e. \texttt{go1.20}.
|
||||
|
||||
To read more on why Go was chosen, see Appendix~\ref{appendix:whygo}.
|
||||
Nix/\texttt{devenv} tools have also aided heavily during development, see
|
||||
Appendix~\ref{appendix:whynix} to learn more.
|
||||
To read more on why Go was chosen in particular, see
|
||||
Appendix~\ref{appendix:whygo}. Equally, Nix and Nix-based tools such as
|
||||
\texttt{devenv} have also aided heavily during development, more on those is
|
||||
written in Appendix~\ref{appendix:whynix}.
|
||||
|
||||
\tab{Tool/Library-Usage Matrix}{tab:toolchain}{1.0}{ll}{
|
||||
\textbf{Name} & \textbf{Usage} \\
|
||||
\textbf{Tool/Library} & \textbf{Usage} \\
|
||||
Go programming language & program core \\
|
||||
Dhall configuration language & program configuration \\
|
||||
Echo & HTTP handlers, controllers, web server \\
|
||||
Echo & HTTP handlers, controllers \\
|
||||
ent & ORM using graph-based modelling \\
|
||||
pq & Pure-Go Postgres drivers \\
|
||||
bluemonday & sanitising HTML \\
|
||||
TailwindCSS & stylesheets using a utility-first approach \\
|
||||
PostgreSQL & persistently storing data \\
|
||||
TailwindCSS & utility-first approach to cascading style sheets \\
|
||||
PostgreSQL & persistent data storage \\
|
||||
}
|
||||
|
||||
Table~\ref{tab:depsversionmx} contains the names and versions of the most
|
||||
@ -219,27 +230,35 @@ application.
|
||||
\texttt{echo} (\url{https://echo.labstack.com/}) & 4.10.2 \\
|
||||
\texttt{go-dhall} (\url{https://github.com/philandstuff/dhall-golang}) & 6.0.2\\
|
||||
\texttt{ent} (\url{https://entgo.io/}) & 0.11.10 \\
|
||||
\texttt{pq} (\url{https://github.com/lib/pq/}) & 1.10.7 \\
|
||||
\texttt{bluemonday} (\url{https://github.com/microcosm-cc/bluemonday}) & 1.0.23 \\
|
||||
\texttt{tailwindcss} (\url{https://tailwindcss.com/}) & 3.3.0 \\
|
||||
\texttt{PostgreSQL} (\url{https://www.postgresql.org/}) & 15.2 \\
|
||||
}
|
||||
|
||||
Additionally, dependency-version mapping can be inferred from looking at the
|
||||
\texttt{go.mod} file's first \textit{require} block at any point in time.
|
||||
|
||||
|
||||
\n{1}{Application architecture}
|
||||
|
||||
The application is written in Go and uses \textit{gomodules}. The full name of
|
||||
the module is \texttt{git.dotya.ml/mirre-mt/pcmt}.
|
||||
|
||||
\n{2}{Package structure}
|
||||
|
||||
The source code of the main module is organised into smaller, self-contained Go
|
||||
The source code of the module is organised into smaller, self-contained Go
|
||||
\emph{packages} appropriately along a couple of domains: logging, core
|
||||
application, web routers, configuration and settings, etc. In Go, packages are
|
||||
delimited by folder structure -- each folder can be a package.
|
||||
|
||||
Generally speaking, the program aggregates decision points into central places,
|
||||
such as \texttt{run.go}, which then imports child packages that facilitate each
|
||||
of the task of loading the configuration, connecting to the database and
|
||||
of the tasks of loading the configuration, connecting to the database and
|
||||
running migrations, consolidating flag, environment variable and
|
||||
configuration-based values into canonical \emph{settings}, setting up routes
|
||||
and handling graceful shutdown.
|
||||
configuration-based values into canonical \emph{settings} \texttt{struct},
|
||||
setting up web routes, authenticating requests, or handling \texttt{signals}
|
||||
and performing graceful shutdowns.
|
||||
|
||||
\n{3}{Internal package}
|
||||
|
||||
@ -250,10 +269,11 @@ package to prevent accidental imports.
|
||||
|
||||
\n{2}{Logging}
|
||||
|
||||
The program uses dependency injection to share a single logger instance,
|
||||
The program uses \emph{dependency injection} to share a single logger instance,
|
||||
similar applies to the database client. These are passed around as a pointer,
|
||||
so the underlying data stays the same. As a rule of thumb, every larger
|
||||
\texttt{struct} that needs to be passed around is passed around as a pointer.
|
||||
so the underlying data stays the same. As a rule of thumb throughout the
|
||||
application, every larger \texttt{struct} that needs to be passed around is
|
||||
passed around as a pointer.
|
||||
|
||||
|
||||
\n{2}{Authentication}
|
||||
@ -446,7 +466,10 @@ application whenever the application is determined to be ready for it.
|
||||
\smallskip
|
||||
% \vspace{\baselineskip}
|
||||
\begin{lstlisting}[language=Haskell, caption={Dhall configuration schema version 0.0.1-rc.2},
|
||||
label=dhallschema, basicstyle=\linespread{0.9}\footnotesize\ttfamily]
|
||||
label=dhallschema, basicstyle=\linespread{0.9}\footnotesize\ttfamily,
|
||||
backgroundcolor=\color{lstbg},
|
||||
morekeywords={Text, Natural, Optional, Type}
|
||||
]
|
||||
let Schema =
|
||||
{ Type =
|
||||
{ Host : Text
|
||||
@ -508,26 +531,31 @@ allow for grouping of related functionality. For instance, configuration
|
||||
settings pertaining mailserver setup are grouped in a record named
|
||||
\textbf{Mailer}. Its attribute \textbf{Enabled} is annotated as \textbf{Bool},
|
||||
which was deemed appropriate for a on-off switch-like functionality, with the
|
||||
only permissible values being either \emph{True} or \emph{False}. Do note that
|
||||
in Dhall $true != True$, since \textbf{True} is internally a Bool constant,
|
||||
which is built into Dhall (check out ``The Prelude''~\cite{dhallprelude}),
|
||||
while \textbf{true} is evaluated as an \emph{unbound} variable, that is, a
|
||||
variable \emph{not} defined in the current \emph{scope} and thus not
|
||||
\emph{present} in the current scope.
|
||||
only permissible values being either \emph{True} or \emph{False}.
|
||||
|
||||
Another one of Dhall specialties is that `$==$' and `$!=$' (in)equality
|
||||
Do note that in Dhall $true\ != True$, since internally \textbf{True} is a
|
||||
\texttt{Bool} constant built directly into Dhall (see ``The
|
||||
Prelude''~\cite{dhallprelude} for reference), while \textbf{true} is evaluated
|
||||
as an \emph{unbound} variable, that is, a variable \emph{not} defined in the
|
||||
current \emph{scope} and thus not \emph{present} in the current scope.
|
||||
|
||||
Another one of Dhall's specialties is that `$==$' and `$!=$' (in)equality
|
||||
operators \textbf{only} work on values of type \texttt{Bool}, which for example
|
||||
means that variables of type \texttt{Natural} (\texttt{uint}) or \texttt{Text}
|
||||
(\texttt{string}) cannot be compared directly as in other languages, which
|
||||
either leaves the work for a higher-level language (such as Go), or from the
|
||||
perspective of the Dhall authors, \emph{enums} are promoted when the value
|
||||
matters.
|
||||
(\texttt{string}) cannot be compared directly as is the case in other
|
||||
languages. That either leaves the comparing work for a higher-level language
|
||||
(such as Go). Alternatively, from the perspective of the Dhall authors
|
||||
\emph{enums} are the promoted way to solve this when the value matters, i.e.\
|
||||
derive a custom \emph{named} type from a primitive type and compare
|
||||
\emph{that}.
|
||||
|
||||
\newpage
|
||||
% \vspace{\parskip}
|
||||
\begin{lstlisting}[language=Haskell, caption={Dhall configuration defaults for
|
||||
schema version 0.0.1-rc.2},
|
||||
label=dhallschemadefaults, basicstyle=\linespread{0.9}\footnotesize\ttfamily]
|
||||
label=dhallschemadefaults, basicstyle=\linespread{0.9}\footnotesize\ttfamily,
|
||||
backgroundcolor=\color{lstbg},
|
||||
]
|
||||
, default =
|
||||
-- | have sane defaults.
|
||||
{ Host = ""
|
||||
@ -668,18 +696,22 @@ following:
|
||||
These methods can further be imported into other packages and this makes
|
||||
working with the database a morning breeze.
|
||||
|
||||
TODO: save admin search queries in application
|
||||
|
||||
|
||||
|
||||
\n{1}{Deployment}
|
||||
|
||||
\textbf{TODO}: mention how \texttt{systemd} aids in running the pod.
|
||||
|
||||
\textbf{TODO}: mention how \texttt{systemd} aids in running the pod.\\
|
||||
\\
|
||||
A deployment setup as suggested in Section~\ref{sec:deploymentRecommendations}
|
||||
is already partially covered by the multi-stage \texttt{Containerfile} that is
|
||||
available in the main sources. Once built, the resulting container image only
|
||||
contains a handful of things it absolutely needs:
|
||||
|
||||
\begin{itemize}
|
||||
\item a statically linked copy of the program
|
||||
\item a statically linked copy of the program (contains parts of the Go
|
||||
stdlib)
|
||||
\item a default configuration file and corresponding Dhall expressions cached
|
||||
at build time
|
||||
\item a recent CA certs bundle
|
||||
@ -687,13 +719,11 @@ contains a handful of things it absolutely needs:
|
||||
|
||||
Since the program also needs a database for proper functioning, an example
|
||||
scenario includes the application container being run in a Podman \textbf{pod}
|
||||
together with the database. That results in not having to expose the database
|
||||
to the entire host or out of the pod at all, it is only be available over pod's
|
||||
\texttt{localhost}.
|
||||
|
||||
It goes without saying that the default values of any configuration secrets
|
||||
should be substituted by the application operator with new, securely generated
|
||||
ones.
|
||||
(as in a pea pod or pod of whales) together with the database. That results in
|
||||
not having to expose the database to the entire host or out of the pod at all,
|
||||
it is only be available over pod's \texttt{localhost}. Hopefully it goes
|
||||
without saying that the default values of any configuration secrets should be
|
||||
substituted by the application operator with new, securely generated ones.
|
||||
|
||||
|
||||
\n{2}{Rootless Podman}
|
||||
@ -717,67 +747,91 @@ Podman, i.e.\ it has \texttt{UID}/\texttt{GID} mapping entries in
|
||||
Podman commands.
|
||||
|
||||
% \newpage
|
||||
|
||||
\begin{lstlisting}[language=bash, caption={Example application deployment using
|
||||
rootless Podman},
|
||||
label=podmanDeployment, basicstyle=\linespread{0.9}\small\ttfamily]
|
||||
label=podmanDeployment, basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg}, commentcolor=\color{Gray},
|
||||
morekeywords={mkdir,podman,just},
|
||||
]
|
||||
# From inside the project folder, build the image locally using kaniko.
|
||||
just kaniko
|
||||
|
||||
# Create a pod.
|
||||
podman pod create --userns=keep-id -p3005:3000 --name pcmt
|
||||
# Create a pod, limit the amount of memory/CPU available to its containers.
|
||||
podman pod create --replace --name pcmt \
|
||||
--memory=100m --cpus=2 \
|
||||
--userns=keep-id -p3005:3000
|
||||
|
||||
# Run the database in the pod.
|
||||
# Create the database folder and run the database in the pod.
|
||||
mkdir -pv ./tmp/db
|
||||
podman run --pod pcmt --replace -d --name "pcmt-pg" --rm \
|
||||
-e POSTGRES_INITDB_ARGS="--auth-host=scram-sha-256 \
|
||||
--auth-local=scram-sha-256" \
|
||||
-e POSTGRES_PASSWORD=postgres -v $PWD/tmp/db:/var/lib/postgresql/data \
|
||||
-e POSTGRES_PASSWORD=postgres \
|
||||
-v $PWD/tmp/db:/var/lib/postgresql/data:Z \
|
||||
--health-cmd "sh -c 'pg_isready -U postgres -d postgres'" \
|
||||
--health-on-failure kill \
|
||||
--health-retries 3 \
|
||||
--health-interval 10s \
|
||||
--health-timeout 1s \
|
||||
--health-start-period=5s \
|
||||
docker.io/library/postgres:15.2-alpine3.17
|
||||
|
||||
# Run the application in the pod.
|
||||
# Run the application itself in the pod.
|
||||
podman run --pod pcmt --replace --name pcmt-og -d --rm \
|
||||
-e PCMT_LIVE=False \
|
||||
-e PCMT_DBTYPE="postgres" \
|
||||
-e PCMT_CONNSTRING="host=pcmt-pg port=5432 sslmode=disable \
|
||||
user=postgres dbname=postgres password=postgres"
|
||||
-v $PWD/config.dhall:/config.dhall:ro \
|
||||
-v $PWD/config.dhall:/config.dhall:Z,ro \
|
||||
docker.io/immawanderer/pcmt:testbuild -config /config.dhall
|
||||
\end{lstlisting}
|
||||
\vspace*{-\baselineskip}
|
||||
|
||||
To summarise Listing~\ref{podmanDeployment}, first, the application
|
||||
container is built from inside the project folder using \texttt{kaniko}.
|
||||
Alternatively, the container image could be pulled from the container
|
||||
repository, but it makes more sense showing the image being built from sources
|
||||
since the listing depicts a \texttt{:testbuild} tag being used.
|
||||
To summarise Listing~\ref{podmanDeployment}, first, the application container
|
||||
is built from inside the project folder using \texttt{kaniko}. Alternatively,
|
||||
the container image could be pulled from a container repository, but it makes
|
||||
more sense showing the image being built from sources with the listing
|
||||
depicting a \texttt{:testbuild} tag being used.
|
||||
|
||||
Next, a \emph{pod} is created and given a name, setting the port binding for
|
||||
the application. Then, the database container is started inside the pod.
|
||||
the application. Then, the database container is started inside the pod,
|
||||
configured with a healthchecking mechanism.
|
||||
|
||||
As a final step, the application container itself is run inside the pod. The application configuration named \texttt{config.dhall} located in
|
||||
\texttt{\$PWD} is mounted as a volume into container's \texttt{/config.dhall},
|
||||
providing the application with a default configuration. The default container
|
||||
does contain a default configuration for reference, however, running the
|
||||
container as is without additional configuration would fail as it does not
|
||||
contain the necessary secrets.
|
||||
As a final step, the application container itself is run inside the pod. The
|
||||
application configuration named \texttt{config.dhall} located in \texttt{\$PWD}
|
||||
is mounted as a volume into container's \texttt{/config.dhall}, providing the
|
||||
application with a default configuration. The default container does contain a
|
||||
default configuration for reference, however, running the container without
|
||||
additionally providing the necessary secretes would fail.
|
||||
|
||||
\n{3}{Sanity checks}
|
||||
|
||||
Do also note that the application connects to the database using its
|
||||
\emph{container} name, i.e.\ not the IP address. That is possible thanks to
|
||||
Podman setting up DNS inside the pod in such a way that all containers in the
|
||||
pod can reach each other using their (container) names. Interestingly,
|
||||
connecting via \texttt{localhost} would also work, as from inside the pod, any
|
||||
container in the pod can reach any other container in the same pod via pod's
|
||||
\texttt{localhost}.
|
||||
In fact, \emph{pinging} the database or application containers from an ad-hoc
|
||||
\texttt{alpine} container added to the pod yields:
|
||||
Also do note that the application connects to the database using its
|
||||
\emph{container} name, i.e.\ not the IP address. This is possible thanks to
|
||||
Podman setting up DNS resolution inside pods using default networks in such a
|
||||
way that all containers in the pod can reach each other using their (container)
|
||||
names.
|
||||
|
||||
Interestingly, connecting via \texttt{localhost} from containers inside the pod
|
||||
would also work. Inside the pod, any container in the pod can reach any other
|
||||
container in the same pod via \emph{pod's} own \texttt{localhost}, thanks to a
|
||||
shared network name space~\cite{podmanNet}.
|
||||
|
||||
In fact, \emph{pinging} (sending ICMP packets using the \texttt{ping} command)
|
||||
the database and application containers from an ad-hoc Alpine Linux container
|
||||
that just joined the pod temporarily yields:
|
||||
|
||||
\vspace{\parskip}
|
||||
\begin{lstlisting}[language=bash, caption={Pinging pod containers using their
|
||||
names}, label=podmanPing, basicstyle=\linespread{0.9}\small\ttfamily]
|
||||
user@containerHost % podman run --rm -it --user=0 --pod=pcmt \
|
||||
names}, label=podmanPing, basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg},
|
||||
morekeywords={podman,ping}
|
||||
]
|
||||
user@containerHost % podman run --rm -it \
|
||||
--user=0 \
|
||||
--pod=pcmt \
|
||||
docker.io/library/alpine:3.18
|
||||
/ # ping -c2 pcmt-og
|
||||
/ % ping -c2 pcmt-og
|
||||
PING pcmt-og (127.0.0.1): 56 data bytes
|
||||
64 bytes from 127.0.0.1: seq=0 ttl=42 time=0.072 ms
|
||||
64 bytes from 127.0.0.1: seq=1 ttl=42 time=0.118 ms
|
||||
@ -785,7 +839,7 @@ PING pcmt-og (127.0.0.1): 56 data bytes
|
||||
--- pcmt-og ping statistics ---
|
||||
2 packets transmitted, 2 packets received, 0% packet loss
|
||||
round-trip min/avg/max = 0.072/0.095/0.118 ms
|
||||
/ # ping -c2 pcmt-pg
|
||||
/ % ping -c2 pcmt-pg
|
||||
PING pcmt-pg (127.0.0.1): 56 data bytes
|
||||
64 bytes from 127.0.0.1: seq=0 ttl=42 time=0.045 ms
|
||||
64 bytes from 127.0.0.1: seq=1 ttl=42 time=0.077 ms
|
||||
@ -793,18 +847,27 @@ PING pcmt-pg (127.0.0.1): 56 data bytes
|
||||
--- pcmt-pg ping statistics ---
|
||||
2 packets transmitted, 2 packets received, 0% packet loss
|
||||
round-trip min/avg/max = 0.045/0.061/0.077 ms
|
||||
/ #
|
||||
/ %
|
||||
\end{lstlisting}
|
||||
\vspace*{-\baselineskip}
|
||||
|
||||
The pod created in Listing~\ref{podmanDeployment} only set the binding for a
|
||||
port used by the application (\texttt{5005/tcp}). The Postgres default port
|
||||
Was the application deployed in a traditional manner instead of using Podman,
|
||||
the use of FQDNs or IPs would be probably be necessary, as there would be no
|
||||
magic resolution of container names happening transparently in the background.
|
||||
|
||||
\n{3}{Database isolation from the host}
|
||||
|
||||
A keen observer has undoubtedly noticed that the pod constructed in
|
||||
Listing~\ref{podmanDeployment} did only create the binding for a port used by
|
||||
the application (\texttt{5005/tcp}). The Postgres default port
|
||||
\texttt{5432/tcp} is not among pod's port bindings, as can be seen in the pod
|
||||
creation command. This can also easily be verified using the command in
|
||||
Listing~\ref{podmanPortBindings}:
|
||||
creation command in the said listing. This can also easily be verified using
|
||||
the command in Listing~\ref{podmanPortBindings}:
|
||||
|
||||
\begin{lstlisting}[language=bash, caption={Podman pod port bindings},
|
||||
label=podmanPortBindings, basicstyle=\linespread{0.9}\small\ttfamily]
|
||||
\begin{lstlisting}[language=bash, caption={Podman pod port binding inspection},
|
||||
label=podmanPortBindings, basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg},
|
||||
morekeywords={podman},
|
||||
]
|
||||
user@containerHost % podman pod inspect pcmt \
|
||||
--format="Port bindings: {{.InfraConfig.PortBindings}}\n\
|
||||
Host network: {{.InfraConfig.HostNetwork}}"
|
||||
@ -813,20 +876,100 @@ Host network: false
|
||||
\end{lstlisting}
|
||||
\vspace*{-\baselineskip}
|
||||
|
||||
To be absolutely sure, trying to connect to the database from outside of the
|
||||
pod (i.e. from the container host) should \emph{fail}, unless, of course, there
|
||||
is another process listening on that port:
|
||||
To be absolutely sure that the database is available only internally in the pod
|
||||
(unless, of course, there is another process listening on the subject port),
|
||||
and that connecting to the database from outside of the pod (i.e. from the
|
||||
container host) really \emph{does} fail, the following commands can be issued:
|
||||
|
||||
\begin{lstlisting}[language=bash, caption={In-pod database is unreachable from
|
||||
the host}, breaklines=true, label=podDbUnreachable,
|
||||
basicstyle=\linespread{0.9}\small\ttfamily]
|
||||
basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg},
|
||||
]
|
||||
user@containerHost % curl localhost:5432
|
||||
--> curl: (7) Failed to connect to localhost port 5432 after 0 ms: Couldn't connect to server
|
||||
\end{lstlisting}
|
||||
\vspace*{-\baselineskip}
|
||||
|
||||
The error in Listing~\ref{podDbUnreachable} is expected, as it is the result of
|
||||
the database port not been exposed from the pod.
|
||||
The error in Listing~\ref{podDbUnreachable} is indeed expected, as it is the
|
||||
result of the database port not been exposed from the pod.
|
||||
|
||||
Of course, since a volume (essentially a bind mount) from the host is used, the
|
||||
actual data is still accessible on the host, both to privileged users and the
|
||||
user running the pod. On the host with SELinux support, the \texttt{:Z} volume
|
||||
addendum at least ensures that the content of the volume is directly
|
||||
inaccessible to other containers, including the application container running
|
||||
inside the same pod, via SELinux labelling.
|
||||
|
||||
\n{3}{Health checks}
|
||||
|
||||
Running the containers with health checks can be counted among the few crucial
|
||||
settings. That way
|
||||
the container runtime can periodically \emph{check} that the application
|
||||
running inside the container is behaving correctly and instructions can be
|
||||
provided on what action should be taken, should the health of the application
|
||||
evaluate unsatisfyingly. Furthermore, different sets of health checking
|
||||
commands can be passed with Podman for start-up and runtime.
|
||||
|
||||
|
||||
\n{2}{Reverse proxy configuration}
|
||||
|
||||
If the application is deployed behind a reverse proxy, such as NGINX, the
|
||||
configuration snippet in Listing~ref{nginxSnip} might apply. Kindly do note how the
|
||||
named upstream server \texttt{pcmt} references the port that was exposed from
|
||||
the pod created in Listing~\ref{podmanDeployment}
|
||||
|
||||
\begin{lstlisting}[caption={Example reverse proxy configuration snippet},
|
||||
breaklines=true, label=nginxSnip, basicstyle=\linespread{0.9}\small\ttfamily,
|
||||
backgroundcolor=\color{lstbg},
|
||||
morekeywords={upstream,server,return,listen,server_name,add_header,access_log,error_log,location,proxy_pass,proxy_set_header,allow,include,more_set_headers,ssl_buffer_size,ssl_dhparam,ssl_certificate,ssl_certificate_key},
|
||||
]
|
||||
upstream pcmt {
|
||||
server 127.0.0.1:5005;
|
||||
}
|
||||
server {
|
||||
return 301 https://<pcmt domain>$request_uri;
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name: <pcmt domain> www.<pcmt domain>;
|
||||
return 404;
|
||||
add_header Referrer-Policy "no-referrer, origin-when-cross-origin";
|
||||
}
|
||||
server {
|
||||
server_name <pcmt domain>;
|
||||
access_log /var/log/nginx/<pcmt domain>.access.log;
|
||||
error_log /var/log/nginx/<pcmt domain>.error.log;
|
||||
location / {
|
||||
proxy_pass http://pcmt;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_forwarded_for;
|
||||
}
|
||||
location /robots.txt {
|
||||
allow all;
|
||||
add_header Content-Type "text/plain; charset=utf-8";
|
||||
add_header X-Robots-Tag "all, noarchive, notranslate";
|
||||
return 200 "User-agent: *\nDisallow: /";
|
||||
}
|
||||
include sec-headers.conf;
|
||||
|
||||
add_header X-Real-IP $remote_addr;
|
||||
add_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
add_header X-Forwarded-Proto $scheme;
|
||||
more_set_headers 'Early-Data: $ssl_early_data';
|
||||
|
||||
listen [::]:443 ssl http2;
|
||||
listen 443 ssl http2;
|
||||
ssl_certificate /etc/letsencrypt/live/<pcmt domain>/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/<pcmt domain>/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
# reduce TTFB
|
||||
# ssl_buffer_size 4k;
|
||||
ssl_buffer_size 4k;
|
||||
}
|
||||
\end{lstlisting}
|
||||
\vspace*{-\baselineskip}
|
||||
|
||||
|
||||
\n{1}{Validation}
|
||||
@ -835,8 +978,8 @@ the database port not been exposed from the pod.
|
||||
|
||||
Unit testing is a hot topic for many people and the author does not count
|
||||
himself to be a staunch supporter of neither extreme. The ``no unit tests''
|
||||
seems to discount any benefit there is to unit testing, while a ``
|
||||
TDD-only''\footnotemark{} approach can be a little too much for some people's
|
||||
opinion seems to discount any benefit there is to unit testing, while a
|
||||
``TDD-only''\footnotemark{} approach can be a little too much for some people's
|
||||
taste. The author tends to prefer a \emph{middle ground} approach in this
|
||||
particular case, i.e. writing enough tests where meaningful but not necessarily
|
||||
testing everything or writing tests prior to business logic code. Arguably,
|
||||
@ -930,7 +1073,11 @@ function is again checked and if everything goes well, the value of the
|
||||
\smallskip
|
||||
\smallskip
|
||||
\begin{lstlisting}[language=Go, caption={Example integration test.},
|
||||
label=integrationtest,basicstyle=\linespread{0.9}\scriptsize\ttfamily]
|
||||
label=integrationtest,basicstyle=\linespread{0.9}\scriptsize\ttfamily,
|
||||
% numbers=left, numberstyle=\tiny\ttfamily\color{codegray},
|
||||
backgroundcolor=\color{lstbg},
|
||||
morekeywords=any
|
||||
]
|
||||
// modules/user/user_test.go
|
||||
package user
|
||||
|
||||
@ -1025,8 +1172,13 @@ a testing instance; therefore, limits to prevent abuse might be imposed.
|
||||
|
||||
\n{3}{Deployment validation}
|
||||
|
||||
TODO: show the results of testing the app in prod using
|
||||
\url{https://testssl.sh/}.
|
||||
TODO: show the results of testing the app in prod using:
|
||||
\begin{itemize}
|
||||
\item \url{https://testssl.sh/}.
|
||||
\item \url{https://www.ssllabs.com/ssltest/analyze.html?d=testpcmt.dotya.ml}
|
||||
\item \url{https://securityheaders.com/?q=https%3A%2F%2Ftestpcmt.dotya.ml}
|
||||
\item \url{https://cryptcheck.fr/https/testpcmt.dotya.ml}
|
||||
\end{itemize}
|
||||
|
||||
|
||||
% =========================================================================== %
|
||||
|
@ -86,6 +86,15 @@ used to validate the correctness of program's behaviour.}
|
||||
% Následující příkaz nastaví standard PDF/A-1b
|
||||
\aplikujpdfa
|
||||
|
||||
\definecolor{lstbg}{rgb}{.96,.96,.96}
|
||||
\lstdefinestyle{yaml}{
|
||||
keywords={true,false,null,y,n,---,...},
|
||||
rulecolor=\color{black},
|
||||
string=[s]{'}{'},
|
||||
string=[s]{"}{"},
|
||||
comment=[l]{#},
|
||||
commentstyle=\color{Gray},
|
||||
}
|
||||
|
||||
% =========================================================================== %
|
||||
\begin{document}
|
||||
|
Reference in New Issue
Block a user