Fix errors shown by grammar and LaTeX checkers
This commit is contained in:
parent
40aa0d9f2f
commit
31803cd421
@ -9,7 +9,7 @@ based on the TFW architecture themselves.
|
||||
A \emph{very important} point to keep in mind is that most of this
|
||||
exercise-specific logic will be implemented in \textbf{FSM callbacks} and
|
||||
custom \textbf{event handlers}.
|
||||
The whole framework is built in a way to faciliate this process and developers
|
||||
The whole framework is built in a way to facilitate this process and developers
|
||||
who understand this mindset almost always find it a breeze to create great
|
||||
content using TFW\@.
|
||||
|
||||
@ -61,7 +61,7 @@ Some components emit or broadcast messages on specific events, for instance the
|
||||
"from_state": ...string...,
|
||||
"to_state": ...string...,
|
||||
"trigger": ...string...,
|
||||
"timestamp": ...unix timestamp...
|
||||
"timestamp": ...UNIX timestamp...
|
||||
}
|
||||
...
|
||||
}
|
||||
@ -81,7 +81,7 @@ The framework must allow content creators to communicate with the user,
|
||||
and provide some mechanism to enable ``talking'' to them.
|
||||
This is the responsibility of the \emph{messages} component, which
|
||||
provides a chatbox-like element on the frontend.
|
||||
The simplest form of communication it accomodates it the insertion of text
|
||||
The simplest form of communication it accommodates it the insertion of text
|
||||
into the chatbox through API messages.
|
||||
This component always expects messages it receives to be in Markdown%
|
||||
\footnote{\href{https://daringfireball.net/projects/markdown/}
|
||||
@ -111,7 +111,7 @@ Similarly to a real chat application, some
|
||||
The timing of pauses and messages is based on the \emph{WPM} --- or Words Per Minute ---
|
||||
set by developers according to their specific requirements.
|
||||
This creates an experience similar to chatting with someone in real time, as the time
|
||||
it takes for each message to be displayed depends on the lenght of the previous message.
|
||||
it takes for each message to be displayed depends on the length of the previous message.
|
||||
This illusion is made possible through appropriate \code{setTimeout()} calls in
|
||||
TypeScript and some elementary math to calculate the proper delays in milliseconds based on
|
||||
message lengths:
|
||||
@ -121,10 +121,10 @@ message lengths:
|
||||
\[ timeoutSeconds = lastMessageLength / charactersPerSeconds \]
|
||||
\[ timeoutMilliseconds = timeoutSeconds * 1000 \]
|
||||
|
||||
The value 5 comes from the fact that on average english words are 5
|
||||
The value 5 comes from the fact that on average English words are 5
|
||||
characters long according to some studies.
|
||||
This value could be made configurable in the future, but currently there
|
||||
are no plans to make non-english challenges.
|
||||
are no plans to make non-English challenges.
|
||||
|
||||
\section{IDE Component}\label{idecomponent}
|
||||
|
||||
@ -139,27 +139,27 @@ To implement this IDE%
|
||||
I have integrated the open source Monaco editor developed by Microsoft into the
|
||||
Angular web application TFW uses as a frontend, and added functionality to it
|
||||
that allows the editor to integrate with the framework.
|
||||
This involves commnication with an event handler dedicated to this feature,
|
||||
This involves communication with an event handler dedicated to this feature,
|
||||
which is capable of reading and writing files to disk, while sending and receiving
|
||||
editor content from the frontend component.
|
||||
The interaction of this event handler and the Monaco editor provides a seamless
|
||||
editing experience, featuring autosave at configurable intervals, code completion,
|
||||
editing experience, featuring auto-save at configurable intervals, code completion,
|
||||
automatic code coloring for several programming languages and more.
|
||||
|
||||
Perhaps the most ``magical'' feature of this editor is that if any process
|
||||
in the Docker container writes a file that is being displayed in the editor,
|
||||
the contents of that file are automatially refreshed without any user
|
||||
the contents of that file are automatically refreshed without any user
|
||||
interaction whatsoever.
|
||||
Besides that, if a file is created in the directory the editor is configured
|
||||
to display, that file is automatially displayed on a new tab in the IDE.
|
||||
to display, that file is automatically displayed on a new tab in the IDE\@.
|
||||
|
||||
This allows for really interesting demo opportunities.
|
||||
Lets say I create a file using the terminal on the frontend by executing the
|
||||
Let's say I create a file using the terminal on the frontend by executing the
|
||||
command \code{touch file.txt}. A new tab on the editor automatically
|
||||
appears. If I select it I can confirm that I have successfully created an
|
||||
empty file.
|
||||
After this let's run a \code{while} cycle in the command line which
|
||||
peroadically appends some text to \code{file.txt}:
|
||||
periodically appends some text to \code{file.txt}:
|
||||
\begin{lstlisting}[captionpos=b,caption={Bash while cycle writing to a file periodically},
|
||||
language=bash]
|
||||
while true
|
||||
@ -170,7 +170,7 @@ done
|
||||
\end{lstlisting}
|
||||
The results speak for themselves:
|
||||
\pic{figures/ide_demo.png}{The editor demo involving automatic file refreshing}
|
||||
As you can see, the file contents are automatially updated as the bash script appends
|
||||
As you can see, the file contents are automatically updated as the bash script appends
|
||||
to the file.
|
||||
This feature is implemented by using the inotify API%
|
||||
\footnote{\href{http://man7.org/linux/man-pages/man7/inotify.7.html}
|
||||
@ -217,7 +217,7 @@ terminal emulator to do so.
|
||||
|
||||
This component has a tiny server process which is managed by a TFW event handler.
|
||||
This small server is responsible for spawning bash sessions and
|
||||
unix pseudoterminals (or \code{pty}s) in the \code{solvable} Docker
|
||||
UNIX pseudoterminals (or \code{pty}s) in the \code{solvable} Docker
|
||||
container.
|
||||
It is also responsible for connecting the master end of the \code{pty} to the
|
||||
emulator running in the browser and the slave end to the bash session it has
|
||||
@ -225,7 +225,7 @@ spawned.
|
||||
This way users are able to work in the shell displayed on the frontend just like
|
||||
they would on their home machines, which allows for great tutorials
|
||||
explaining topics that involve the usage of a shell.
|
||||
Note that this allows us to cover an extremely wide variety of topics using TFW:
|
||||
Note that this allows us to cover an extremely wide variety of topics using TFW\@:
|
||||
from compiling shared libraries for development, to using cryptographic FUSE file systems
|
||||
for enhanced privacy%
|
||||
\footnote{\href{https://github.com/rfjakob/gocryptfs}{https://github.com/rfjakob/gocryptfs}},
|
||||
@ -248,7 +248,7 @@ container using an interactive bash session.
|
||||
This is not an easy thing to accomplish without relying on some sort of heavyweight
|
||||
monitoring solution such as Sysdig%
|
||||
\footnote{\href{https://sysdig.comq}{https://sysdig.com}}.
|
||||
I deemed most simiar systems a huge overkill to implement this functionality, and their
|
||||
I deemed most similar systems a huge overkill to implement this functionality, and their
|
||||
memory footprints are not something we could afford here%
|
||||
\footnote{These containers will be spawned on a per-user basis, so we must be as
|
||||
conservative with memory as possible.}.
|
||||
@ -308,10 +308,10 @@ as well as switching between them mid-tutorial using API messages.
|
||||
|
||||
The framework includes an event handler capable of managing processes running inside
|
||||
the \code{solvable} Docker container.
|
||||
The capabilities of this componenet include the starting, stopping and restarting of processes,
|
||||
The capabilities of this component include the starting, stopping and restarting of processes,
|
||||
as well as emitting the standard out or standard error logs belonging to them, even
|
||||
in real-time (by broadcasting TFW messages).
|
||||
This logging feature allows for interesting possiblities such as the handling
|
||||
This logging feature allows for interesting possibilities such as the handling
|
||||
of live process output, or just requesting the logs belonging to a certain application when
|
||||
some sort of event has occurred (such as on errors).
|
||||
This component also can be interacted with using TFW API messages.
|
||||
@ -353,7 +353,7 @@ location \code{/tmp/}.
|
||||
All logs coming from the container itself were also logged to this location.
|
||||
This had caused an infinite recursion: when a process would write to \code{/tmp/}
|
||||
inotify would invoke a process that would also log to that location causing the kernel to
|
||||
emit more inotify events, which in turn would cause more and more new proesses to spawn
|
||||
emit more inotify events, which in turn would cause more and more new processes to spawn
|
||||
and write to \code{/tmp/}, causing the whole procedure to repeat again and again.
|
||||
This continued until my machine would start to run out of memory and begin swapping
|
||||
pages to disk%
|
||||
@ -369,7 +369,7 @@ fascinating phenomenon, but those were \emph{very} fun hours at least.
|
||||
\section{FSM Management}
|
||||
|
||||
I have already mentioned the event handler called \code{FSMManagingEventHandler},
|
||||
which is responsible for managing the framework FSM.
|
||||
which is responsible for managing the framework FSM\@.
|
||||
For completeness I chose to include it in this chapter as well.
|
||||
The API it exposes through TFW messages allows client code to attempt stepping the
|
||||
state machine.
|
||||
@ -406,7 +406,7 @@ thing work with the Same Origin Policy%
|
||||
\footnote{\href{https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy}
|
||||
{https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin\_policy}}
|
||||
being in effect?
|
||||
The answer is that developers must use a \emph{relative url}, that is an URL relative
|
||||
The answer is that developers must use a \emph{relative URL}, that is an URL relative
|
||||
to the entry point of the TFW frontend itself.
|
||||
To allow serving several web applications from a single port the framework
|
||||
supports optional reverse-proxy configurations through the nginx%
|
||||
@ -416,8 +416,8 @@ More on this in a Chapter~\ref{usingtfw}.
|
||||
\section{Various Frontend Features}
|
||||
|
||||
The Angular frontend of the framework features several different layouts.
|
||||
These layouts are useful to accomodate different workflows for users,
|
||||
such as the previous exampe of editig code and being able to view the
|
||||
These layouts are useful to accommodate different workflows for users,
|
||||
such as the previous example of editing code and being able to view the
|
||||
result of said code in real time next to the editor.
|
||||
Another example would be editing Ansible playbooks in the file editor,
|
||||
and then trying to run them in the terminal.
|
||||
@ -426,11 +426,11 @@ to be used like that, for instance the code editor can be used to conveniently
|
||||
edit larger files this way.
|
||||
|
||||
The frontend was designed in a way to be fully responsive in window sizes
|
||||
that still keep the whole thing usable (i.e.\ it would not be practial to start
|
||||
that still keep the whole thing usable (i.e.\ it would not be practical to start
|
||||
solving TFW tutorials on a smart phone, simply because of size limits, so such small screens are
|
||||
not supported, but the frontend still behaves as expected on small laptops or bigger tablets).
|
||||
This is not an easy thing to impelent and maintain due to the lots of small
|
||||
incompatibilites between browsers given the complexity of the frontend.
|
||||
This is not an easy thing to implement and maintain due to the lots of small
|
||||
incompatibilities between browsers given the complexity of the frontend.
|
||||
|
||||
Just remember that a few years ago the clearfix%
|
||||
\footnote{\href{https://stackoverflow.com/questions/8554043/what-is-a-clearfix}
|
||||
@ -438,7 +438,7 @@ Just remember that a few years ago the clearfix%
|
||||
hack was the industry standard in creating CSS layouts.
|
||||
The situation has improved \emph{a lot} since then with flexboxes
|
||||
and grid layouts despite the sheer chaos that is generally involved in web
|
||||
standardization efforts, but CSS espacially%
|
||||
standardization efforts, but CSS especially%
|
||||
\footnote{\href{https://developer.mozilla.org/en-US/docs/Web/CSS/CSS3}
|
||||
{https://developer.mozilla.org/en-US/docs/Web/CSS/CSS3}}.
|
||||
|
||||
@ -460,10 +460,10 @@ keep your software up to date.
|
||||
There are several additional APIs exposed by the frontend,
|
||||
which include the changing of layouts, selecting the terminal or console
|
||||
component to be displayed, the possibility of dynamically modifying
|
||||
frontend configuration values (such as the frequency of autosaving the files in the editor)
|
||||
frontend configuration values (such as the frequency of auto-saving the files in the editor)
|
||||
and more.
|
||||
|
||||
To accomodate communication with the TFW server, the frontend of the framework
|
||||
To accommodate communication with the TFW server, the frontend of the framework
|
||||
comes with some library code which can be used to send and receive TFW messages.
|
||||
This code is mostly WebSockets combined with RxJS%
|
||||
\footnote{\href{https://rxjs-dev.firebaseapp.com}{https://rxjs-dev.firebaseapp.com}},
|
||||
|
@ -1,11 +1,11 @@
|
||||
\chapter*{Acknowledgements}
|
||||
\addcontentsline{toc}{chapter}{Acknowledgements}
|
||||
\chapter*{Acknowledgments}
|
||||
\addcontentsline{toc}{chapter}{Acknowledgments}
|
||||
|
||||
This creation of this framework would not have been possible alone.
|
||||
In this chapter I would like to express my gratitude towards great people who have
|
||||
helped me in some way or another along the way.
|
||||
|
||||
First of all I would like to thank Bálint Bokros, my good friend and colleauge for
|
||||
First of all I would like to thank Bálint Bokros, my good friend and colleague for
|
||||
his awesome work regarding TFW and for always
|
||||
being open to provide useful input.
|
||||
He has also earned my gratitude by always being there to lift my spirits, be that
|
||||
@ -22,7 +22,7 @@ I can't thank my consultant, Levente Buttyán enough for enduring my general
|
||||
inability to deal with deadlines and administration.
|
||||
|
||||
I also appreciate the great morale my colleagues and friends provided in the office,
|
||||
by always being there, be that for work or fun. This project couldn't have been realised
|
||||
by always being there, be that for work or fun. This project couldn't have been realized
|
||||
sitting in a depressing cube between 200 people hating their jobs. They also have my gratitude
|
||||
for direct contributions to the framework, be that with ideas, assistance,
|
||||
or actual code.
|
||||
|
@ -13,7 +13,7 @@ During most of this chapter I am going to be discussing the \code{solvable} Dock
|
||||
with the exception of Section~\ref{solutioncheck}, where I will dive into how the
|
||||
\code{controller} image is implemented.
|
||||
|
||||
The most important feature of the framework is it's messaging system.
|
||||
The most important feature of the framework is its messaging system.
|
||||
Basically what we need is a system where processes running inside a Docker container
|
||||
would be allowed to communicate with each other.
|
||||
This task is very easy to solve, with lots of possible solutions
|
||||
@ -27,7 +27,7 @@ that can communicate with JavaScript running in a browser connected to it.
|
||||
The solution the framework uses is a proxy server, which connects to frontend components
|
||||
on one side and handles interprocess communication on the other side.
|
||||
This way the server is capable of proxying messages between the two sides, enabling
|
||||
communitaion between them.
|
||||
communication between them.
|
||||
Notice that this way what we have is essentially an IPC%
|
||||
\footnote{Interprocess communication} system in which a web application
|
||||
can ``act like'' it was running on the backend in a sense: it is easily able to
|
||||
@ -43,7 +43,7 @@ which was later redesigned and fully rewritten by me to allow for greater flexib
|
||||
message authentication, restoration of frontend state, a complete overhaul of the
|
||||
state tracking system and the possibility for solution checking among other things).
|
||||
If you are explicitly interested in the differences between the original POC implementation
|
||||
(which is out of scope for this thesis due to lenght constraints) and the current
|
||||
(which is out of scope for this thesis due to length constraints) and the current
|
||||
framework please consult Bálint's excellent paper and Bachelor's thesis on it\cite{BokaThesis}.
|
||||
\end{note}
|
||||
|
||||
@ -62,16 +62,16 @@ HTTP request-response on each update).
|
||||
This has been superseded by WebSockets around 2011, which provide a full-duplex
|
||||
communication channel over TCP between your browser and the server.
|
||||
This is done by initiating a protocol handshake using the \code{Connection: Upgrade}
|
||||
HTTP header, which establishes a premanent socket connection between the browser
|
||||
HTTP header, which establishes a permanent socket connection between the browser
|
||||
and the server.
|
||||
This allows for communication with lower overhead and latency facilitating efficient
|
||||
real-time applications, which were not always possible to create before due to
|
||||
the overheads%
|
||||
\footnote{In some applications this overhead could be bigger than the actual data sent,
|
||||
such as singaling.} introduced by AJAX polling.
|
||||
such as signaling.} introduced by AJAX polling.
|
||||
|
||||
The Tutorial Framework uses WebSockets to connect to it's web frontend.
|
||||
The TFW proxy server is capable to connecting to an arbirary number of WebSockets,
|
||||
The Tutorial Framework uses WebSockets to connect to its web frontend.
|
||||
The TFW proxy server is capable to connecting to an arbitrary number of WebSockets,
|
||||
which allows the framework to simultaneously connect to components running in
|
||||
separate browser windows and tabs, or even
|
||||
in different browsers altogether (such as opening a terminal in Chrome and an IDE in Firefox).
|
||||
@ -88,11 +88,11 @@ RabbitMQ%
|
||||
ZMQ does not require a message broker daemon to be running in the background at all times
|
||||
and as such has a much lower memory footprint while still providing various messaging
|
||||
patterns and bindings for almost any widely used programming language.
|
||||
An other --- yet untilized --- capability of this solution is that since ZMQ is capable
|
||||
Another --- yet unutilized --- capability of this solution is that since ZMQ is capable
|
||||
of using simple TCP sockets, we could even communicate with processes running on remote
|
||||
hosts using the current architecture of the framework.
|
||||
|
||||
There are various lower level and higher level alternatives for IPC other than
|
||||
There are various lower level and higher-level alternatives for IPC other than
|
||||
ZMQ which were also considered during the design process of the framework at some point.
|
||||
A few examples of top contenders and reasons for not using them in the end:
|
||||
\begin{itemize}
|
||||
@ -101,13 +101,13 @@ A few examples of top contenders and reasons for not using them in the end:
|
||||
all bytes are sent or received both require constantly checking the return values of the
|
||||
libc \code{send()} and \code{recv()} system calls%
|
||||
\footnote{Developers forget this very often, resulting in almost untraceable bugs
|
||||
that seem to occour randomly.},
|
||||
that seem to occur randomly.},
|
||||
while ZMQ takes care of this
|
||||
extra logic involved and even provides higher level messaging patterns such as
|
||||
extra logic involved and even provides higher-level messaging patterns such as
|
||||
subscribe-publish, which would need to be implemented on top of raw sockets again.
|
||||
\item Using something like gRPC\footnote{\href{https://grpc.io}{https://grpc.io}}
|
||||
or plain HTTP (both of which
|
||||
are considered to be higher level than ZMQ sockets) would require
|
||||
are considered to be higher-level than ZMQ sockets) would require
|
||||
all processes partaking in the communication to be HTTP servers themselves,
|
||||
which would make the framework
|
||||
less lightweight and flexible: socket communication with or without ZMQ does not
|
||||
@ -125,7 +125,7 @@ simultaneously.} in nature, which extorts certain design choices on code
|
||||
Now being familiar with the technological basis of the framework we can now
|
||||
discuss it in more detail.
|
||||
|
||||
\pic{figures/tfw_architecture.png}{An overwiew of the Tutorial Framework}
|
||||
\pic{figures/tfw_architecture.png}{An overview of the Tutorial Framework}
|
||||
|
||||
Architecturally TFW consists of four main components:
|
||||
\begin{itemize}
|
||||
@ -186,14 +186,14 @@ at a later point in this paper.
|
||||
|
||||
\subsection{Networking Details}
|
||||
|
||||
The default behaviour of the TFW server is that it forwards all messages from coming from
|
||||
The default behavior of the TFW server is that it forwards all messages from coming from
|
||||
the frontend to the event handlers and vice versa.
|
||||
So messages coming from the WebSockets of the frontend are forwarded to event handlers
|
||||
via ZMQ and messages received on ZMQ from event handlers are forwarded to
|
||||
the frontend via WebSockets.
|
||||
|
||||
The TFW server is also capable of ``reflecting'' messages back to the side they were
|
||||
received from (to faciliate event handler to event handler communication for instance),
|
||||
received from (to facilitate event handler to event handler communication for instance),
|
||||
or broadcast messages to all components.
|
||||
This is possible by embedding a whole TFW message in the \code{data} field of
|
||||
an outer wrapper message with a special \code{key} that signals to the TFW server that
|
||||
@ -236,10 +236,10 @@ at any time when the running of the tests would be required.
|
||||
|
||||
An interesting thing to mention is that there \emph{could} be event handlers which
|
||||
broadcast messages with a \code{key} that they are also subscribed to.
|
||||
This can distrupt their behaviour in weird ways if they are not prepared to
|
||||
This can disrupt their behavior in weird ways if they are not prepared to
|
||||
deal with their own ``echoes''.
|
||||
The framework offers a solution for this by providing a special
|
||||
event handler type, which is capable of filtering out it's own broadcasts.
|
||||
event handler type, which is capable of filtering out its own broadcasts.
|
||||
The way they do this is by caching the checksum of every message they broadcast,
|
||||
and ignore the first message that comes back with the same checksum.
|
||||
|
||||
@ -286,7 +286,7 @@ This code editor essentially is a Monaco editor%
|
||||
{https://microsoft.github.io/monaco-editor/}}
|
||||
instance integrated into Angular and upgraded with the capability to
|
||||
exchanges messages with an event handler to save, read and edit files
|
||||
that reside in the writeable file system of the \code{solvable}
|
||||
that reside in the writable file system of the \code{solvable}
|
||||
Docker container.
|
||||
|
||||
All of the built-ins come with a full API documentation explaining what they do
|
||||
@ -319,7 +319,7 @@ search these messages for the given string.
|
||||
|
||||
The exact capabilities of these built-in components will be explained in greater
|
||||
detail in Chapter~\ref{atouroftfw}.
|
||||
Developers who are well-aware of these capabilites are able to use the framework in extremely
|
||||
Developers who are well-aware of these capabilities are able to use the framework in extremely
|
||||
creative ways allowing for very interesting functionality, such as the above example.
|
||||
The components of TFW can often be combined to work together in unexpected, yet useful
|
||||
ways, similarly how command-line utilities on UNIX-like systems do.
|
||||
@ -365,7 +365,7 @@ Depending on the results three cases are possible:
|
||||
|
||||
This example shows how content creators can create tutorials that could behave
|
||||
in many different ways based on what the user does.
|
||||
In high quality challenges developers can implement several ``paths'' to
|
||||
In high-quality challenges developers can implement several ``paths'' to
|
||||
a successful completion.
|
||||
This is a very engaging feature that offers an immersive learning experience for
|
||||
users, which many solutions for distance education lack so often.
|
||||
@ -373,7 +373,7 @@ users, which many solutions for distance education lack so often.
|
||||
Developers can use a YAML file or write Python code to implement finite
|
||||
state machines in TFW\@. This is going to be further detailed in
|
||||
Chapter~\ref{usingtfw}.
|
||||
In the implementation of state machines it is also possbile to subscribe callbacks to be
|
||||
In the implementation of state machines it is also possible to subscribe callbacks to be
|
||||
invoked on certain events regarding the machine, such as before and after
|
||||
state transitions, or on entering and exiting a state.
|
||||
It is \emph{very} important to be aware of these callbacks, as much of the
|
||||
@ -404,7 +404,7 @@ can be used to digitally sign messages (this is what the \code{signature} messag
|
||||
field is designed for) using HMAC%
|
||||
\footnote{Hash-based message authentication code}.
|
||||
In this case the TFW server will only forward the privileged messages that
|
||||
have a valid signature, and the evend handler managing the state machine
|
||||
have a valid signature, and the event handler managing the state machine
|
||||
will also validate the signature of messages it receives
|
||||
(and sign the updates it broadcasts as well, so that other components can verify that
|
||||
they come from a trusted source).
|
||||
@ -425,6 +425,6 @@ makes the Tutorial Framework suitable for implementing
|
||||
traditional hacking challenges, such as exercises developed for CTF%
|
||||
\footnote{A ``capture the flag'' game is a competition designed for professionals
|
||||
--- or just people interested in the field --- to sharpen their skills in IT security.
|
||||
Avatao often organises similar events.}
|
||||
Avatao often organizes similar events.}
|
||||
events, as the controller image is also capable of verifying the authenticity of
|
||||
FSM update messages via inspecting their signatures.
|
||||
|
@ -3,7 +3,7 @@
|
||||
\section{Project justification}
|
||||
|
||||
As the world is being completely engulfed by software, the need for accessible, but
|
||||
high quality learning materials covering software engineering and especially secure software
|
||||
high-quality learning materials covering software engineering and especially secure software
|
||||
engineering is on the rise.
|
||||
While we are enjoying the comfort that information technology provides us, we often forget
|
||||
about the risks involved in relying so much on software in our everyday lives.
|
||||
@ -25,7 +25,7 @@ sensitive data through our ill-protected smart phones\cite{Android} and IoT devi
|
||||
What a time to be alive.
|
||||
It is important to express that IT security is something that is \emph{really hard} to
|
||||
get right.
|
||||
Even if right often only means better then your neighbour, as perfect security is an utopia
|
||||
Even if right often only means better then your neighbor, as perfect security is an utopia
|
||||
that doesn't seem to exist\cite{NoPerfectSecurity}.
|
||||
Often when large and reputable companies in the industry such as
|
||||
CloudFlare\cite{CloudFlareLeak} or eBay\cite{EBayGit} can fail to get it right at times
|
||||
@ -44,9 +44,9 @@ The only thing we can hope and work for is to become better and better as time
|
||||
and generations pass by.
|
||||
We \emph{must} do better, and education is the way forward.
|
||||
|
||||
The short term goal of this project --- and the goal of this thesis --- is to provide
|
||||
The short-term goal of this project --- and the goal of this thesis --- is to provide
|
||||
a new angle in the education of software engineering, especially secure software
|
||||
engineering based on the aspirations above, with the long term goal of bringing
|
||||
engineering based on the aspirations above, with the long-term goal of bringing
|
||||
something new to the table in the matter of IT education as a whole
|
||||
(not just for developers, but for users as well).
|
||||
|
||||
@ -63,17 +63,17 @@ universities around the world and providing a solution for companies in building
|
||||
\emph{security consciousness} amongst their developer teams.
|
||||
|
||||
Since starting out we have amassed some experience in building fun challenges
|
||||
that showcase the exploitation and fixing of relevant security vulnerabilites in code or
|
||||
that showcase the exploitation and fixing of relevant security vulnerabilities in code or
|
||||
configuration.
|
||||
Traditionally these exercises revolved around offensive and defensive tasks, with challenges
|
||||
often being split into two or more parts.
|
||||
For example users would have to hack a website by exploiting a buffer overflow vulnerability,
|
||||
then in the second challenge they would fix the code they've just exploited in a web based
|
||||
then in the second challenge they would fix the code they've just exploited in a web-based
|
||||
code editor.
|
||||
These kind of exercises offer great flexibility to reflect real world security issues, as in
|
||||
more complex challenges users might be required to exploit multiple vulnerabilites for success,
|
||||
These kind of exercises offer great flexibility to reflect real-world security issues, as in
|
||||
more complex challenges users might be required to exploit multiple vulnerabilities for success,
|
||||
and understand the ways they augment each other.
|
||||
We often recreate real world scenarios based on incident reports released by companies for
|
||||
We often recreate real-world scenarios based on incident reports released by companies for
|
||||
added authenticity and relevance\cite{AkosFacebook}.
|
||||
Our challenges usually involve some sort of website acting as frontend for the vulnerable
|
||||
application, or require the user to connect using SSH\@.
|
||||
@ -83,24 +83,24 @@ application, or require the user to connect using SSH\@.
|
||||
The Avatao platform relies heavily on Docker containers to spawn challenges,
|
||||
which makes it extremely flexible in terms of what is possible to do when creating
|
||||
content.
|
||||
Essentially anything that you can do inside a Docker conainer can be done on
|
||||
Essentially anything that you can do inside a Docker container can be done on
|
||||
the Avatao platform as well.
|
||||
Currently each challenge is implemented as a set of Docker images residing inside a
|
||||
Git repository exclusive to the specific challenge in mind.
|
||||
Our content creation wokflow enables developers to create such repositories on GitHub,
|
||||
Our content creation workflow enables developers to create such repositories on GitHub,
|
||||
which are automatically set up with the proper webhooks, so that when their content gets
|
||||
reviewed (and their feature branches merged), their changes will go live on the
|
||||
platform as well.
|
||||
In the future we also plan on supporting the use of virtual machines to implement
|
||||
challenges, which could further increase this fexibility by addig the possiblity to do
|
||||
challenges, which could further increase this flexibility by adding the possibility to do
|
||||
things like exercises involving the use of Docker or Windows based challenges.
|
||||
|
||||
\section{Emergence}\label{intro:emergence}
|
||||
|
||||
While working as a content creator I have stumbled into the idea of automating the completion
|
||||
of challenges for QA\footnote{Quality Assurrance} and demo purposes.
|
||||
of challenges for QA\footnote{Quality Assurance} and demo purposes.
|
||||
I used to record short videos or GIFs to showcase my content to management.
|
||||
In a certain scenario I was required to integrate a web based terminal emulator into a
|
||||
In a certain scenario I was required to integrate a web-based terminal emulator into a
|
||||
frontend application to improve user experience by making it possible to use a shell
|
||||
right on the website rather than having to connect through SSH\@.
|
||||
|
||||
@ -110,7 +110,7 @@ as I have often found myself recording over and over again for a demo without an
|
||||
During the time I was playing around with this idea, researching possible solutions have led me
|
||||
to a hidden gem of a project on GitHub called \code{demo-magic}%
|
||||
\footnote{\href{https://github.com/paxtonhare/demo-magic}{https://github.com/paxtonhare/demo-magic}},
|
||||
which is esentially a bash script that simulates someone typing into a terminal and executing
|
||||
which is essentially a bash script that simulates someone typing into a terminal and executing
|
||||
commands.
|
||||
|
||||
I have created a fork%
|
||||
@ -123,7 +123,7 @@ the solution script with the challenge code itself, making it toggleable using b
|
||||
variables.
|
||||
Should the solution script be enabled, the challenge would automatically start%
|
||||
\footnote{I did this by injecting the solution script into the user's \code{.bashrc} file.}
|
||||
completing itself in the terminal integrated into it's frontend, often even explaining the
|
||||
completing itself in the terminal integrated into its frontend, often even explaining the
|
||||
commands executed during the solution process.
|
||||
|
||||
\lstinputlisting[
|
||||
@ -153,7 +153,7 @@ a related task.
|
||||
This teacher scenario would allow you to take the helm sometimes and try applying
|
||||
your newfound skills in action immediately.
|
||||
|
||||
For example a chatbot would show you how to encrypt a file using GnuGP%
|
||||
For example a chatbot would show you how to encrypt a file using GnuPG%
|
||||
\footnote{\href{https://www.gnupg.org}{https://www.gnupg.org}},
|
||||
then it would ask you to encrypt another file similarly.
|
||||
After this the bot could teach you how to a configure a database server and then
|
||||
@ -184,13 +184,13 @@ should report the exact exception raised while running the application.
|
||||
\pic{figures/webapp_and_editor.png}{The code editor and web application example in TFW}
|
||||
|
||||
Even if we did all this, we would still need a way to integrate this whole thing into
|
||||
a web based frontend with a file editor, terminal, chat window and stuff like that.
|
||||
a web-based frontend with a file editor, terminal, chat window and stuff like that.
|
||||
Turns out that today all this can be done by writing a few hundred lines of Python
|
||||
code which uses the Tutorial Framework.
|
||||
|
||||
\pic{figures/webapp_and_editor_err.png}{Invalid code and deployment failure with process output}
|
||||
|
||||
Note that it is possible to try out the current version of the Tutorial Framewok
|
||||
Note that it is possible to try out the current version of the Tutorial Framework
|
||||
using a guest account on the Avatao platform on this
|
||||
\href{https://platform.avatao.com/paths/d0ccef1f-0389-45bf-9d44-e85b86d66c49/challenges/a7e08c0a-199f-4f8d-aa7e-51b6e9bfcb15}{url}%
|
||||
\footnote{\href{https://platform.avatao.com/paths/d0ccef1f-0389-45bf-9d44-e85b86d66c49/challenges/a7e08c0a-199f-4f8d-aa7e-51b6e9bfcb15}
|
||||
@ -202,7 +202,7 @@ Based on this it is now more or less possible to define requirements for the pro
|
||||
The reason for the ``more or less'' part is that all of this is pretty much bleeding edge,
|
||||
where the requirements could shift dynamically with time.
|
||||
For this reason I am going to be as general as possible, to the point that some of
|
||||
this might even sound vauge.
|
||||
this might even sound vague.
|
||||
To achieve our goals we would need:
|
||||
|
||||
\begin{itemize}
|
||||
@ -211,7 +211,7 @@ To achieve our goals we would need:
|
||||
the user has edited a file, or has executed a command in the terminal)
|
||||
\item a highly flexible messaging system, in which processes running on the backend and
|
||||
frontend components running in a web browser could communicate with each other
|
||||
\item a web based frontend with lots of built-in options (terminal, file editor, chat
|
||||
\item a web-based frontend with lots of built-in options (terminal, file editor, chat
|
||||
window, etc.) that use said messaging system
|
||||
\item stable APIs that can be exposed to content creators to work with (so that
|
||||
framework updates won't break client code)
|
||||
@ -220,11 +220,11 @@ To achieve our goals we would need:
|
||||
|
||||
\section{Early Development}
|
||||
|
||||
Around a year ago a good friend and collage of mine Bálint Bokros, the CTO of our company
|
||||
Around a year ago a good friend and colleague of mine Bálint Bokros, the CTO of our company
|
||||
Gábor Pék and myself would start designing the TFW architecture.
|
||||
In this early phase we would research solutions for the issues described such as
|
||||
tracking user progress, process management, interprocess communication
|
||||
and making a web based frontend application capable of communicatig with processes running
|
||||
and making a web-based frontend application capable of communicating with processes running
|
||||
inside a Docker container.
|
||||
|
||||
After seeing some sort of light at the end of the tunnel regarding what technologies could
|
||||
@ -253,11 +253,11 @@ But since the project has followed Agile Methodology%
|
||||
\footnote{Manifesto for Agile Software Development:
|
||||
\href{https://agilemanifesto.org}{https://agilemanifesto.org}}
|
||||
from the start, we were able to adapt to these changes without losing
|
||||
the progess he made in said thesis. Quoting from the Agile Manifesto:
|
||||
the progress he made in said thesis. Quoting from the Agile Manifesto:
|
||||
``Responding to change over following a plan''.
|
||||
This is a really important takeaway.
|
||||
|
||||
After becoming a full time employee at Avatao I was tasked with developing the project
|
||||
After becoming a full-time employee at Avatao I was tasked with developing the project
|
||||
with Bálint, who was later reassigned to work on the GDPR compliance of the platform.
|
||||
Thus it became my job to turn the framework into a stable code base ready for
|
||||
usage by content creators and to implement most of the features that we've envisioned
|
||||
|
@ -18,13 +18,13 @@ be honest and admit that I have a sweet spot for this project.
|
||||
|
||||
Currently a total of 63 tutorials based on the framework are running in production,
|
||||
with new ones being released on a weekly basis.
|
||||
These exercises have been solved several hunders of times.
|
||||
These exercises have been solved several hundreds of times.
|
||||
User feedback is getting better and better as the project moves forward.
|
||||
As a maintainer, currently I know about a single unfixed bug in the framework, which
|
||||
is getting reported by users as well.
|
||||
There are more, of course, the world is never going to run out of bugs to fix,
|
||||
but at least I sleep well knowing that things aren't breaking on a constant basis.
|
||||
Considering that this is a one year old project including initial development,
|
||||
Considering that this is a one-year-old project including initial development,
|
||||
I'd consider this a solid success.
|
||||
|
||||
We were able to achieve most --- if not all --- of the goals we have envisioned on the
|
||||
@ -38,17 +38,17 @@ apart from implementing new features, as these will always keep coming in, and w
|
||||
have some great ones planned, that I can promise.
|
||||
|
||||
First of all I think that we need to put more focus on developing TFW, as currently
|
||||
other projects are often being priorized over it.
|
||||
other projects are often being prioritized over it.
|
||||
While some of these are understandable, the framework is a very promising project
|
||||
with great potential and deserves more attantion from us.
|
||||
with great potential and deserves more attention from us.
|
||||
The fact that it is stable does not validate neglecting it.
|
||||
|
||||
I'd also like to concentrate on stabilizing the API of the framework.
|
||||
Currently each major release lasts for a few months before I am forced to break something
|
||||
to accomodate new features.
|
||||
to accommodate new features.
|
||||
While the communication of these breakages is fine --- we use mailing lists for this purpose
|
||||
and our versioning scheme seems solid so far, this forces developers
|
||||
to constantly update older tutorials to comply new API.
|
||||
to constantly update older tutorials to comply new API\@.
|
||||
To make this better I'd need to consider planning ahead more, so that the newest API is flexible
|
||||
enough to support new features on the roadmap and not get distracted as much by
|
||||
other features emerging on the horizon.
|
||||
@ -58,7 +58,7 @@ that could be made a lot easier. As a maintainer sometimes I find it hard to
|
||||
tell what these things exactly are, as I know the framework inside out, having written most
|
||||
of the codebase myself.
|
||||
I'd like to set some time aside to create tutorials using the framework myself,
|
||||
so I can better narrow these potential difficulities down.
|
||||
so I can better narrow these potential difficulties down.
|
||||
This would require me to be able to take things slow for a few weeks, as this is not
|
||||
something that is possible to do effectively in a rush. In the summer months, maybe?
|
||||
|
||||
@ -86,7 +86,7 @@ working. Back in the day I used to be nervous because of deadlines and things no
|
||||
working how they were supposed to, but now I know that these things are a part
|
||||
of the job and one must be able to deal with them without getting agitated.
|
||||
Any time I feel like something is not OK, I just try take a step back, relax a bit to
|
||||
blow of steam and approach the issue without acting in haste.
|
||||
blow off steam and approach the issue without acting in haste.
|
||||
I think this is not too related to working as a software engineer, but something
|
||||
that can be applied to anything we do.
|
||||
|
||||
|
@ -12,7 +12,7 @@ The main points include:
|
||||
for this machine, such as ones that display messages to the user
|
||||
\item Implementing the required event handlers, which may trigger state transitions in the FSM,
|
||||
interact with non-TFW code and do various things that might be needed during an exercise,
|
||||
such as compiling code written by the user or runnign unit tests
|
||||
such as compiling code written by the user or running unit tests
|
||||
\item Defining what processes should run inside the container besides the things TFW
|
||||
starts automatically
|
||||
\item Setting up reverse proxying for any user-facing network application such as web servers
|
||||
@ -58,7 +58,7 @@ understanding of how the framework interacts with client code.
|
||||
The \code{config.yml} file is an Avatao challenge configuration file,
|
||||
which is used describe what kind of Docker containers implement a challenge,
|
||||
what ports do they expose talking what protocols, define the name of the
|
||||
excercise, it's difficulity, and so on.
|
||||
exercise, it's difficulty, and so on.
|
||||
Every Avatao challenge must provide such a file.
|
||||
Another thing that is not even indicated on the structure above is the \code{metadata}
|
||||
directory, which contains the short and long descriptions of challenges in
|
||||
@ -131,8 +131,8 @@ or kubernetes%
|
||||
to orchestrate their containers).
|
||||
This approach is not suitable for TFW, as it would require the framework to orchestrate
|
||||
Docker containers from inside a container managed by the same Docker daemon, which is
|
||||
feasible in theory but very hard and unservicable to do in practice.
|
||||
This would require doing something like mounting the unix domain socket used
|
||||
feasible in theory but very hard and unserviceable to do in practice.
|
||||
This would require doing something like mounting the UNIX domain socket used
|
||||
to manage the Docker daemon inside a running container managed by that daemon,
|
||||
which is a fun thing to
|
||||
play around with in my free time but not something suitable for running in production,
|
||||
@ -155,7 +155,7 @@ command=python3 server.py
|
||||
autostart=true
|
||||
\end{lstlisting}
|
||||
As mentioned earlier in~\ref{processmanagement}, any program that is started this way
|
||||
can be managed by the framewok using API messages.
|
||||
can be managed by the framework using API messages.
|
||||
All this is possible through using the xmlrpc%
|
||||
\footnote{\href{https://docs.python.org/3/library/xmlrpc.html}
|
||||
{https://docs.python.org/3/library/xmlrpc.html}}
|
||||
@ -170,7 +170,7 @@ invoke it's command line utility in a separate process when you need something d
|
||||
For simplicity, exercises based on the framework only expose a single port from the
|
||||
\code{solvable} container.
|
||||
This port is required to serve the frontend of the framework.
|
||||
If this is the case, how do we run additional web applications to showcase vulnerabilies
|
||||
If this is the case, how do we run additional web applications to showcase vulnerabilities
|
||||
on during a tutorial?
|
||||
Since one port can only be bound by one process at a time, we will need to
|
||||
run a reverse-proxy%
|
||||
@ -191,23 +191,23 @@ location /yoururl {
|
||||
proxy_pass http://127.0.0.1:3333;
|
||||
}
|
||||
\end{lstlisting}
|
||||
Now the content served by this websever on port 3333
|
||||
will be available on the url \code{<challenge-url>/yoururl} despite that port 3333
|
||||
Now the content served by this web server on port 3333
|
||||
will be available on the URL \code{<challenge-url>/yoururl} despite that port 3333
|
||||
does not accept connections from outside the container as it is not exposed.
|
||||
It is very important to understand, that developers
|
||||
have to make sure that their web application \emph{behaves well} behind a reverse proxy.
|
||||
What this means is that they are going to be served from a ``subdirectory'' of the top
|
||||
level URL\@:
|
||||
for example \code{/register} will be served under \code{/yoururl/register}.
|
||||
This means that all links in the final HTML must refer to the proxied urls, e.g.\
|
||||
\code{/yoururl/login}, and server side redirects must point to these correct hrefs as well.
|
||||
This means that all links in the final HTML must refer to the proxied URLs, e.g.\
|
||||
\code{/yoururl/login}, and server-side redirects must point to these correct hrefs as well.
|
||||
Idiomatically this is usually implemented by supplying a \code{BASEURL}
|
||||
to the application through an environment variable, so that it is able to set
|
||||
itself up correctly.
|
||||
|
||||
\subsection{Copying Configuration Files}
|
||||
Behind the curtains, the Tutorial Framework uses some Dockerfile trickery to
|
||||
faciliate the copying of supervisor and nginx configuration files to their correct
|
||||
facilitate the copying of supervisor and nginx configuration files to their correct
|
||||
locations.
|
||||
Normally when one uses the \code{COPY}%
|
||||
\footnote{\href{https://docs.docker.com/engine/reference/builder/\#copy}
|
||||
@ -251,7 +251,7 @@ framework-specific directories.
|
||||
The use of this directory is not mandatory, only a good practice, as developers
|
||||
are free to implement the non-TFW parts of their exercises as they see fit
|
||||
(the copying of these files into image layers using \code{solvable/Dockerfile}
|
||||
is their resposibility as well).
|
||||
is their responsibility as well).
|
||||
|
||||
\section{Configuring Built-in Components}
|
||||
|
||||
@ -268,11 +268,11 @@ initialized in, which exposes several communicative options through the
|
||||
\code{__init__()} methods of these event handlers:
|
||||
\lstinputlisting[
|
||||
language=python,
|
||||
caption={Example of inicializing some event handlers},
|
||||
caption={Example of initializing some event handlers},
|
||||
captionpos=b
|
||||
]{listings/event_handler_main.py}
|
||||
|
||||
\section{Impelenting a Finite State Machine}
|
||||
\section{Implementing a Finite State Machine}
|
||||
|
||||
The Tutorial Framework allows developers to define state machines in two ways,
|
||||
as discussed before.
|
||||
@ -281,7 +281,7 @@ to showcase the capabilities of the framework.
|
||||
|
||||
\subsection{YAML based FSM}
|
||||
YAML\footnote{YAML Ain't Markup Language: \href{http://yaml.org}{http://yaml.org}}
|
||||
is a human friendly data serialization standard and a superset of JSON.
|
||||
is a human friendly data serialization standard and a superset of JSON\@.
|
||||
It is possible to use this format to define a state machine like so:
|
||||
\lstinputlisting[
|
||||
caption={A Finite State Machine implemented in YAML},
|
||||
@ -291,7 +291,7 @@ This state machine has two states, state \code{0} and \code{1}.
|
||||
It defines a single transition between them, \code{step_1}.
|
||||
On entering state \code{1} the FSM will write a message to the frontend messaging component
|
||||
by invoking TFW library code using Python.
|
||||
The transition can only occour if the file \code{allow_step_1} exists.
|
||||
The transition can only occur if the file \code{allow_step_1} exists.
|
||||
|
||||
YAML based state machine implementations also allow the usage of the Jinja2%
|
||||
\footnote{\href{http://jinja.pocoo.org/docs/2.10/}{http://jinja.pocoo.org/docs/2.10/}}
|
||||
@ -338,7 +338,7 @@ I am going to use the Python programming language, but it isn't hard
|
||||
to create event handlers in other languages, as the only thing
|
||||
they have to be capable of is communicating with the TFW server using
|
||||
ZeroMQ sockets, as previously discussed.
|
||||
The library provided by the framework abstracts low level socket logic
|
||||
The library provided by the framework abstracts low-level socket logic
|
||||
away by implementing easy to use base classes.
|
||||
Creating such base classes in a given language shouldn't take longer
|
||||
than a few hours for an experienced developer.
|
||||
@ -346,7 +346,7 @@ Our challenge creators have already implemented similar libraries for
|
||||
Java, JavaScript and C++ as well.
|
||||
\lstinputlisting[
|
||||
language=python,
|
||||
caption={A very simple event hander implemented in Python},
|
||||
caption={A very simple event handler implemented in Python},
|
||||
captionpos=b
|
||||
]{listings/event_handler_example.py}
|
||||
This simple event handler subscribes to the \code{fsm_update} messages,
|
||||
@ -359,7 +359,7 @@ abstract method, which is used to, well, handle events.
|
||||
\section{Setting Up a Developer Environment}\label{devenv}
|
||||
|
||||
To make getting started as smooth as possible I have created
|
||||
a ``bootstrap'' script which is capable of creating a development envrionment from
|
||||
a ``bootstrap'' script which is capable of creating a development environment from
|
||||
scratch.
|
||||
This script is distributed as the following bash one-liner:
|
||||
\begin{lstlisting}[language=bash]
|
||||
@ -370,7 +370,7 @@ This command downloads the script using \code{curl}%
|
||||
In the open source community it is quite common to distribute installers this way%
|
||||
\footnote{A good example of this is oh-my-zsh:
|
||||
\href{https://github.com/robbyrussell/oh-my-zsh}{https://github.com/robbyrussell/oh-my-zsh}},
|
||||
which might seem a little scary at first, but is not less safe then
|
||||
which might seem a little scary at first, but is not less safe than
|
||||
downloading and executing a binary installer from a website with a valid TLS certificate, as
|
||||
\code{curl} will fail with an error message if the certificate is invalid.
|
||||
This is because both methods place their trust in the PKI~\footnote{Public Key Infrastructure}
|
||||
@ -400,13 +400,13 @@ mitigating MITM attacks.
|
||||
|
||||
The bootstrap script clones the three TFW repositories and does several steps
|
||||
to create a working environment into a single directory, that is based on
|
||||
test-tutorail-framework:
|
||||
test-tutorial-framework:
|
||||
\begin{itemize}
|
||||
\item It builds the newest version of the TFW baseimage locally
|
||||
\item It pins the version tag of this image in \code{solvable/Dockerfile},
|
||||
so that this newly-built version will be used by the tutorial
|
||||
\item It places the latest frontend in \code{solvable/frontend} with
|
||||
depencendies installed
|
||||
dependencies installed
|
||||
\end{itemize}
|
||||
It is important to note that this script \emph{does not} install anything system-wide,
|
||||
it only works in the directory it is being executed from.
|
||||
@ -456,7 +456,7 @@ to remove all the dependencies that won't be used when running the application%
|
||||
\footnote{Otherwise it won't be possible to serve these applications efficiently
|
||||
over the internet.}.
|
||||
The problem is, that these things can take a \emph{really} long time.
|
||||
This is why today frontend builds usually take a lot longer than building anything
|
||||
This is why today frontend builds usually take a lot longer then building anything
|
||||
not involving JavaScript (such as C++, C\# or any other compiled programming language).
|
||||
|
||||
This mess presents it's own challenges for the Tutorial Framework as well.
|
||||
@ -475,7 +475,7 @@ To circumvent this, it is possible to entirely exclude the Angular frontend from
|
||||
build, using build time arguments%
|
||||
\footnote{In practice this is done by supplying the option
|
||||
\code{--build-arg NOFRONTEND=1} to Docker.}.
|
||||
But when doing so, developers would have to run the frondent locally with
|
||||
But when doing so, developers would have to run the frontend locally with
|
||||
the whole \code{node_modules} directory present.
|
||||
The bootstrap script takes care of putting these dependencies there,
|
||||
while the \code{tfw.sh} script is capable of starting a development server
|
||||
@ -484,8 +484,8 @@ the Docker container without the frontend.
|
||||
If this whole thing wasn't complicated enough, since Docker binds the port
|
||||
the container is going to use, \code{tfw.sh} has to run the Angular dev server on
|
||||
another port, then use the proxying features of Angular-CLI to forward requests
|
||||
from this port to the runnign Docker container when requesting resources
|
||||
other then the entrypoint to the Angular application.
|
||||
from this port to the running Docker container when requesting resources
|
||||
other than the entrypoint to the Angular application.
|
||||
|
||||
This is the reason why the frontend is accessible through port \code{4200} (default
|
||||
port for \code{ng serve}) when using \code{tfw.sh} to start a tutorial, but when running
|
||||
|
Loading…
Reference in New Issue
Block a user