diff --git a/tex/part-practical.tex b/tex/part-practical.tex index cb78248..2a4bc9d 100644 --- a/tex/part-practical.tex +++ b/tex/part-practical.tex @@ -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 % git show --show-signature % # 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://$request_uri; + listen 80; + listen [::]:80; + server_name: www.; + return 404; + add_header Referrer-Policy "no-referrer, origin-when-cross-origin"; +} +server { + server_name ; + access_log /var/log/nginx/.access.log; + error_log /var/log/nginx/.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//fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live//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} % =========================================================================== % diff --git a/thesis.tex b/thesis.tex index 24dbd0b..97fd980 100644 --- a/thesis.tex +++ b/thesis.tex @@ -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}