├── README.md
├── backend
└── Dockerfile
├── database
└── Dockerfile
├── frontend
├── .babelrc
├── Dockerfile
├── index.html
├── package.json
├── src
│ └── app
│ │ ├── Assets
│ │ └── backgrond.jpg
│ │ ├── components
│ │ ├── AddTodo.jsx
│ │ ├── App.scss
│ │ ├── Login.jsx
│ │ ├── Main.jsx
│ │ ├── Navigation.jsx
│ │ ├── Signup.jsx
│ │ ├── TodoContainer.jsx
│ │ ├── TodoForm.jsx
│ │ ├── TodoList.jsx
│ │ ├── UpdateTodo.jsx
│ │ └── UsernameDisplay.jsx
│ │ ├── index.jsx
│ │ └── store
│ │ ├── history.js
│ │ ├── index.js
│ │ ├── mutations.js
│ │ ├── reducer.js
│ │ ├── sagas.js
│ │ └── sagas.mock.js
└── webpack.config.js
└── images
├── architecture.jpeg
├── cloud9-environments.png
├── cloud9-step-01.png
├── cloud9-step-02.png
├── cloud9-step-03.png
├── image-1.png
├── image-2.png
└── image-3.png
/README.md:
--------------------------------------------------------------------------------
1 | # Docker CTF
2 |
3 | In this CTF (Capture The Flag), you will learn how to deploy a 3-tier TODO application to AWS using Docker! Below are all the tasks that were originally published at [devslop.ctfd.io](https://devslop.ctfd.io).
4 |
5 | # Table Of Contents
6 |
7 | - [Introduction (1 Point)](#introduction-1-point)
8 | * [Architecture](#architecture)
9 | * [Capture The Flag](#capture-the-flag)
10 | - [Getting Started (5 Points)](#getting-started-5-points)
11 | * [Accessing the AWS Console](#accessing-the-aws-console)
12 | * [Accessing your Cloud9 Environment](#accessing-your-cloud9-environment)
13 | * [Enable Auto-Save (optional)](#enable-auto-save-optional)
14 | * [Cloning the TODO App Repository](#cloning-the-todo-app-repository)
15 | - [Task 1: Building and Running the Client Application (1000 Points)](#task-1-building-and-running-the-client-application-1000-points)
16 | * [Dockerfile](#dockerfile)
17 | * [Capture The Flag](#capture-the-flag-1)
18 | - [Task 2: Pushing a Docker Image to DockerHub (1500 Points)](#task-2-pushing-a-docker-image-to-dockerhub-1500-points)
19 | * [Capture The Flag](#capture-the-flag-2)
20 | - [Task 3: Deploying to the Remote Docker Host (2700 Points)](#task-3-deploying-to-the-remote-docker-host-2700-points)
21 | * [Capture The Flag](#capture-the-flag-3)
22 | * [Available Hints](#available-hints)
23 | + [Hints](#hints)
24 | - [Obtaining the instance's public IP address (700 points)](#obtaining-the-instance's-public-ip-address-700-points)
25 | - [Exposing a container port to the host and making it accessible via the Internet (800 points)](#exposing-a-container-port-to-the-host-and-making-it-accessible-via-the-internet-800-points)
26 | - [Task 4: Building and Running The Backend (5000 Points)](#task-4-building-and-running-the-backend-5000-points)
27 | * [Bridge Network](#bridge-network)
28 | * [Dockerfile](#dockerfile-1)
29 | * [Capture The Flag](#capture-the-flag-4)
30 | * [Available Hint](#available-hint)
31 | + [Copying the backend repository folder from the first stage into the second stage (500 Points)](#copying-the-backend-repository-folder-from-the-first-stage-into-the-second-stage-500-points)
32 | - [Task 5: Running the Backend in the Correct Network (4500 Points)](#task-5-running-the-backend-in-the-correct-network-4500-points)
33 | * [Capture The Flag](#capture-the-flag-5)
34 | - [Task 6: Starting the Database (4700 Points)](#task-6-starting-the-database-4700-points)
35 | * [Bridge Network](#bridge-network-1)
36 | * [Dockerfile](#dockerfile-2)
37 | * [Capture The Flag](#capture-the-flag-6)
38 | - [Task 7: Connecting the Backend to the Database - Part 1 (2500 Points)](#task-7-connecting-the-backend-to-the-database---part-1-2500-points)
39 | * [Capture The Flag](#capture-the-flag-7)
40 | - [Task 8: Connecting the Backend to the Database - Part 2 (15000 Points)](#task-8-connecting-the-backend-to-the-database---part-2-15000-points)
41 | * [Capture The Flag](#capture-the-flag-8)
42 | - [Task 9: Connecting the Backend to the Database - Part 3 (3300 Points)](#task-9-connecting-the-backend-to-the-database---part-3-3300-points)
43 | * [Capture The Flag](#capture-the-flag-9)
44 | - [Task 10: Connecting the Frontend to the Backend (4500 Points)](#task-10-connecting-the-frontend-to-the-backend-4500-points)
45 | * [The Final Test](#the-final-test)
46 | * [Capture The Flag](#capture-the-flag-10)
47 | * [Available Hints](#available-hints-1)
48 | - [Replacing BACKEND_IP during build time (500 points)](#replacing-backend-ip-during-build-time-500-points)
49 | - [Flags](#flags)
50 |
51 | # Introduction (1 Point)
52 |
53 | In this first-ever DevSlop Game Day, you will containerize an existing three-tier TODO application and deploy it to a Docker host on AWS. The final solution, which can be seen in the diagram below, will be broken down into multiple tasks and each task will award you points if all the steps are executed successfully.
54 | As previously mentioned, you will need a [DockerHub](https://hub.docker.com/) account. If you haven't created one, please do so now. Otherwise, let's have a look at the solution's architecture.
55 |
56 | ## Architecture
57 |
58 | Here's the diagram of the solution you will be building in this competition:
59 |
60 | 
61 |
62 | In summary:
63 |
64 | * There will be 3 containers: Front-end (client application), Backend (server application) and the Database
65 | * Each container will be running on its own Docker network **inside the same Docker host (i.e. an EC2 instance)**
66 | * The Front-end should be accessible from the Internet and it should communicate with the Backend also through the Internet
67 | * The backend and the database should communicate with each other privately inside the same Docker host.
68 | * By the end of this competition, you will have deployed a fully functional TODO application
69 |
70 | ## Capture The Flag
71 |
72 | > *Read what [Capture The Flag](https://dev.to/atan/what-is-ctf-and-how-to-get-started-3f04) is if you're not familiar with the term.*
73 |
74 | During this competition, you will have to capture and submit multiple "flags", which are secrets that will be revealed or will need to be calculated after successfully executing all the steps of each one of the tasks. For each obtained flag, you will have to submit it using the following format:
75 |
76 | ```
77 | DevSlopCTF{FLAG}
78 | ```
79 |
80 | **PS: please note that all flags are case sensitive, meaning that uppercase and lowercase letters are NOT the same.**
81 |
82 | After submitting the correct flag, you will earn points (each task will award you different amount of points). Let's submit the first flag of the competition so you know how it works.
83 |
84 | The first flag is: `ok`. Submit the following in the text field below to earn **1 point**: `DevSlopCTF{ok}`
85 |
86 | # Getting Started (5 Points)
87 |
88 | In this task, we'll go through a few steps to make sure you have access to an AWS Console and the code which you will be working on.
89 |
90 | ## Accessing the AWS Console
91 |
92 | To access the AWS Console, take a look at your Discord team channel name and find out your team number (e.g. team1, team2, team3 etc). Then click on one of the links below:
93 |
94 | * team1 to team40 - [Click here to sign in to AWS](https://docker-ctf.signin.aws.amazon.com/console)
95 | * team41 to team50 - [Click here to sign in to AWS](https://docker-ctf-2.signin.aws.amazon.com/console)
96 |
97 | Your IAM User and its password will be provided to you via your team channel on Discord.
98 |
99 | ## Accessing your Cloud9 Environment
100 |
101 | **The steps below should be followed by EVERYONE in the team**
102 |
103 | We've set up a [Cloud9 environment](https://aws.amazon.com/cloud9/) for your team. If you haven't heard of Cloud9 yet, it's an AWS solution for teams to write and debug code together just with a web browser (it's basically an IDE which you can access through the AWS Console, everyone sees in real time all the code changes being made and you also have access to a terminal).
104 | After you've logged in to AWS, click on **Services** at the top and type in `Cloud9`. That will take you to the Cloud9 console. You should see your team's environment (team1 has been used as example only):
105 |
106 |
107 |
108 | Click on **Open IDE**. This will be your team's workspace for this Dojo (you don't need to write code in your local computer, but if you want to develop locally and copy and paste to Cloud9, that is totally fine).
109 |
110 | ## Enable Auto-Save (optional)
111 |
112 | To configure Cloud9 to save files automatically, do the following. Click on the Cloud9 icon on the top-left corner and then on Preferences:
113 |
114 | 
115 |
116 | At the bottom, click on Experimental:
117 | 
118 |
119 | Finally, click on drop down and then on `After Delay`, which will cause files to be saved after a second or so:
120 | 
121 |
122 | ## Cloning the TODO App Repository
123 |
124 | As a first step, let's clone this repository to your Cloud9 environment as it contains all the files you will need to start this challenge. This is the URL: [https://github.com/thedojoseries/docker-ctf](https://github.com/thedojoseries/docker-ctf)
125 |
126 | In your Cloud9 environment, go to the terminal (it should show up at the bottom of the page) and type in the following:
127 |
128 | ```
129 | git clone https://github.com/thedojoseries/docker-ctf
130 | ```
131 |
132 | After cloning the repository and cd'ing into it, you should see the following files and folders:
133 |
134 | ```
135 | $ ls -l
136 | total 76
137 | drwxrwxr-x 2 ec2-user ec2-user 4096 MMM D 19:01 backend
138 | drwxrwxr-x 2 ec2-user ec2-user 4096 MMM D 19:01 database
139 | drwxrwxr-x 3 ec2-user ec2-user 4096 MMM D 19:01 frontend
140 | drwxrwxr-x 8 ec2-user ec2-user 4096 MMM D 19:01 .git
141 | -rw-rw-r-- 1 ec2-user ec2-user 51343 MMM D 19:01 README.md
142 | ```
143 |
144 | Now let's begin! Submit the flag `I am ready` using the format `DevSlopCTF{FLAG}` (i.e. `DevSlopCTF{I am ready}`) to unlock the first task and earn 5 more points!
145 |
146 | # Task 1: Building and Running the Client Application (1000 Points)
147 |
148 | The **frontend** folder contains all the files needed in order to run the **client application** (which will also be referred to as "frontend" sometimes). Since the goal of this challenge is to create Docker containers, let's see how we can create a Docker image for this application.
149 |
150 | ## Dockerfile
151 |
152 | Docker images can be automatically created using a [Dockerfile](https://docs.docker.com/engine/reference/builder/). Let's have a look at the Dockerfile in the **frontend** directory:
153 |
154 | ```
155 | FROM
156 |
157 | RUN
158 |
159 | COPY
160 |
161 | CMD [ "executable" ]
162 | ```
163 |
164 | This Dockerfile is simply a template and cannot be used as-is. Use the following information to fill out the Dockerfile:
165 |
166 | * Use `node:carbon` as the base image (see [FROM](https://docs.docker.com/engine/reference/builder/#from))
167 | * Copy the current directory (.) to /code (see [COPY](https://docs.docker.com/engine/reference/builder/#copy))
168 | * Make /code the Working Directory (see [WORKDIR](https://docs.docker.com/engine/reference/builder/#workdir))
169 | * Install all the dependencies using the command **npm install** (see [RUN](https://docs.docker.com/engine/reference/builder/#run))
170 | * Run the following command when the container comes up: **npm run dev** (see [CMD](https://docs.docker.com/engine/reference/builder/#cmd))
171 |
172 | When you're done, use the `build` command to build the Docker image (please ignore the `npm WARN` messages during the build):
173 |
174 | ```
175 | docker build -t dd-fe .
176 | ```
177 |
178 | PS 1: The `-t` option is for *tagging*. In this case `dd-fe` is the name of your image.
179 | PS 2: The dot (`.`) at the end indicates to Docker that the Dockerfile is in the current directory.
180 |
181 | Once the images has been built, you can view it using the `images` command:
182 |
183 | ```
184 | $ docker images
185 |
186 | REPOSITORY TAG IMAGE ID CREATED SIZE
187 | dd-fe latest 4c047fc8432e 3 minutes ago 1.07GB
188 | node carbon 8eeadf3757f4 10 months ago 901MB
189 | ```
190 |
191 | To run the container, use the `run` command, passing the `--rm` option to remove the container once it stops running:
192 |
193 | ```
194 | docker run --rm dd-fe
195 |
196 | > docker-dojo-fe@1.0.0 dev /code
197 | > webpack-dev-server --open --host 0.0.0.0 --port 8080
198 |
199 | ℹ 「wds」: Project is running at http://0.0.0.0:8080/
200 | ℹ 「wds」: webpack output is served from /
201 | ℹ 「wds」: 404s will fallback to /index.html
202 | ⚠ 「wds」: Unable to open browser. If you are running in a headless environment, please do not use the --open flag
203 | [BABEL] Note: The code generator has deoptimised the styling of /code/node_modules/react-dom/cjs/react-dom.development.js as it exceeds the max of 500KB.
204 | ℹ 「wdm」: Hash: 104d1215cdcac375e62e
205 | Version: webpack 4.30.0
206 | Time: 4349ms
207 | Built at: 2019-05-08 20:44:58
208 | Asset Size Chunks Chunk Names
209 | 291a9d331c48885d1d8c9e13e0ae0a0b.jpg 457 KiB [emitted]
210 | bundle.js 1.64 MiB main [emitted] main
211 | Entrypoint main = bundle.js
212 | [0] multi (webpack)-dev-server/client?http://0.0.0.0:8080 ./src/app 40 bytes {main} [built]
213 | [./node_modules/ansi-html/index.js] 4.26 KiB {main} [built]
214 | [./node_modules/loglevel/lib/loglevel.js] 6.84 KiB {main} [built]
215 | [./node_modules/querystring-es3/index.js] 126 bytes {main} [built]
216 | [./node_modules/react-dom/index.js] 1.32 KiB {main} [built]
217 | [./node_modules/react/index.js] 189 bytes {main} [built]
218 | [./node_modules/strip-ansi/index.js] 162 bytes {main} [built]
219 | [./node_modules/url/url.js] 22.2 KiB {main} [built]
220 | [./node_modules/webpack-dev-server/client/index.js?http://0.0.0.0:8080] (webpack)-dev-server/client?http://0.0.0.0:8080 8.26 KiB {main} [built]
221 | [./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.59 KiB {main} [built]
222 | [./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.05 KiB {main} [built]
223 | [./node_modules/webpack/hot sync ^\.\/log$] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
224 | [./node_modules/webpack/hot/emitter.js] (webpack)/hot/emitter.js 75 bytes {main} [built]
225 | [./src/app/components/Main.jsx] 1.36 KiB {main} [built]
226 | [./src/app/index.jsx] 184 bytes {main} [built]
227 | + 159 hidden modules
228 | ℹ 「wdm」: [REDACTED].
229 | ```
230 |
231 | ## Capture The Flag
232 |
233 | If you did everything correctly, once the container is running, you will see a message at the very bottom on the right-hand side of `ℹ 「wdm」:`. The message has been hidden from the output above and replaced with `[REDACTED]`. First, kill the container by pressing `CTRL+C` twice. Then, grab the message **without the period at the end** and calculate its SHA-256 Hash. Feel free to use a command-line application or an online calculator.
234 | The flag will be the **first 6 characters of the Hash**. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn **1000 points**!
235 |
236 | # Task 2: Pushing a Docker Image to DockerHub (1500 Points)
237 |
238 | At the moment, the container is running on your Cloud9 environment, which cannot be reached from the Internet and you will not be able to access your client application. Therefore, you will need to deploy the container to another Docker host which can be reached from the Internet. This Docker host is an EC2 instance running in the same AWS account as your Cloud9 environment.
239 |
240 | Kill the running container (press `CTRL+C` twice) before proceeding.
241 |
242 | Because you built the image in Cloud9, you will need to push it to a Docker repository so that you can download it in the Docker host. The Docker repository we're talking about here is called [DockerHub](https://hub.docker.com/). If you haven't created an account, you will need to do so before proceeding. If you're working in a team, decide which account will be used (only one account should be used).
243 |
244 | To push the image, you will use the `push` command ([Read more about the push command](https://docs.docker.com/engine/reference/commandline/push/)). However, if you try to push your Docker image as-is, you will get the following message:
245 |
246 | ```
247 | $ docker push dd-fe
248 |
249 | (...)
250 | denied: requested access to the resource is denied
251 | ```
252 |
253 | Read the following documentation to understand how to push an image to DockerHub: [https://docs.docker.com/docker-hub/repos/](https://docs.docker.com/docker-hub/repos/). Here are the requirements to push your image to your DockerHub account:
254 |
255 | * The repository you will create on DockerHub should be **public**.
256 | * The tag of the image should be `latest`
257 |
258 | Don't hesitate to contact one of the organizers if you have any questions about how images should be named.
259 |
260 | ## Capture The Flag
261 |
262 | Once you're able to successfully push the image to your DockerHub account, you should see the following message at the bottom of the `docker push` command output:
263 |
264 | ```
265 | [W1]: [W2]: [W3]:[HASH] [W4]: [SIZE]
266 | ```
267 |
268 | What are the values of W1, W2, W3 and W4? The flag for this task will be a concatenation of W1, W2, W3 and W4 separated by colon (`:`) like so: `W1:W2:W3:W4`.
269 | For example, if you had the following output:
270 |
271 | ```
272 | foo: bar: baz:1234567890 qux: 3611
273 | ```
274 |
275 | Then the flag would be `foo:bar:baz:qux`. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn **1500 points**!
276 |
277 | # Task 3: Deploying to the Remote Docker Host (2700 Points)
278 |
279 | > During the competition, all teams were provided with a Docker host on AWS
280 |
281 | Before you deploy the client application, you will need to get access to the Docker host as already discussed. From the Cloud9 terminal, SSH into an EC2 instance using the following information:
282 |
283 | * The SSH username should be `ec2-user`
284 | * The IP Address of the instance should be `10.0.X.99`, where `X` is the number of your team. For example, team1's IP address will be `10.0.1.99`, team2's will be `10.0.2.99` and so on.
285 | * Use the same password you used to log in to your AWS account.
286 |
287 | Once you're in the host, run the command `docker run hello-world` to make sure Docker is working and you should get the following output:
288 |
289 | ```
290 | [ec2-user@ip-X-X-X-X ~]$ docker run hello-world
291 |
292 | (...)
293 | Hello from Docker!
294 | (...)
295 | ```
296 |
297 | If you got the `Hello from Docker` message back, you're good to go!
298 |
299 | Now pull the image you pushed to DockerHub using the `pull` command:
300 |
301 | ```
302 | docker pull
303 | ```
304 |
305 | Run `docker images` to confirm the image was pulled successfully. Next, run the container:
306 |
307 | ```
308 | docker run -d
309 | ```
310 |
311 | After you run this command, you will see a hash (i.e. the container ID). Copy that hash and run:
312 |
313 | ```
314 | docker logs -f
315 | ```
316 |
317 | The command above will show the output of the client application. When you see:
318 |
319 | ```
320 | (...)
321 | [0] multi (webpack)-dev-server/client?http://0.0.0.0:8080 ./src/app 40 bytes {main} [built]
322 | [./node_modules/ansi-html/index.js] 4.26 KiB {main} [built]
323 | [./node_modules/loglevel/lib/loglevel.js] 6.84 KiB {main} [built]
324 | [./node_modules/querystring-es3/index.js] 126 bytes {main} [built]
325 | [./node_modules/react-dom/index.js] 1.32 KiB {main} [built]
326 | [./node_modules/react/index.js] 189 bytes {main} [built]
327 | [./node_modules/strip-ansi/index.js] 162 bytes {main} [built]
328 | [./node_modules/url/url.js] 22.2 KiB {main} [built]
329 | [./node_modules/webpack-dev-server/client/index.js?http://0.0.0.0:8080] (webpack)-dev-server/client?http://0.0.0.0:8080 8.26 KiB {main} [built]
330 | [./node_modules/webpack-dev-server/client/overlay.js] (webpack)-dev-server/client/overlay.js 3.59 KiB {main} [built]
331 | [./node_modules/webpack-dev-server/client/socket.js] (webpack)-dev-server/client/socket.js 1.05 KiB {main} [built]
332 | [./node_modules/webpack/hot sync ^\.\/log$] (webpack)/hot sync nonrecursive ^\.\/log$ 170 bytes {main} [built]
333 | [./node_modules/webpack/hot/emitter.js] (webpack)/hot/emitter.js 75 bytes {main} [built]
334 | [./src/app/components/Main.jsx] 1.36 KiB {main} [built]
335 | [./src/app/index.jsx] 184 bytes {main} [built]
336 | + 159 hidden modules
337 | ℹ 「wdm」: Compiled successfully.
338 | ```
339 |
340 | That means the client application is ready. Kill the `logs -f` process by pressing `CTRL+C` (don't worry, you won't kill the container).
341 |
342 | Before you access the application in your web browser, you need to obtain the **public IP** of the docker host. Use whatever tool you deem necessary to achieve that.
343 |
344 | Once you obtain the public IP address, go to your browser and type in the IP of your Docker host and **specify port 8080** (e.g., X.X.X.X:8080). Can you see an interface like this?
345 |
346 |
347 |
348 | You probably can't (yet)! To fix that, you need to expose port 8080 in the container to the host and port 8080 in the host to the external world so the website is accessible via the Internet. If you're not familiar with what publishing ports means in Docker, [read the Container networking](https://docs.docker.com/engine/reference/commandline/run/) page.
349 |
350 | To publish ports and make the website accessible via the Internet, you will need to stop the container and run it again using a certain Docker CLI option to publish the aforementioned ports.
351 |
352 | ## Capture The Flag
353 |
354 | Once you're able to successfully load the website, click on the blue **Login** button. At the moment, you will not be able to log in because the client application is not connected to the backend application. Let's find out which URL the website is calling whenever you click on the Login button.
355 |
356 | To find that out, [open your Browser's developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools). Look for a tab called **Network** and click on it. Now that you can see all API calls being made by the website, click on the Login button again and you should see an API call to the `authenticate` endpoint being made and failing. What's the **domain AND port** the website is sending the POST request to? That's the flag! For example, if the website was sending a request to `http://example.com:5656/authenticate`, the flag would be `example.com:5656`. Submit the correct flag (domain and port separated by colon) using the format `DevSlopCTF{FLAG}` to earn **2700 points**! And remember that the flag is case sensitive.
357 |
358 | ## Available Hints
359 |
360 | Here are the available hints for this task and how much points you will lose by unlocking them:
361 |
362 | * How to obtain the instance's public IP address - **700 points**
363 | * How to expose a container port to the host and making it accessible via the Internet - **800 points**
364 |
365 | ### Hints
366 |
367 | #### Obtaining the instance's public IP address (700 points)
368 |
369 | To obtain the public IP of the ec2 instance, run the following command in your terminal:
370 |
371 | ```
372 | curl ifconfig.co
373 | ```
374 |
375 | Use the IP above to access the front-end.
376 |
377 | #### Exposing a container port to the host and making it accessible via the Internet (800 points)
378 |
379 | **Hint 1**
380 |
381 | To expose container ports to the host and making them available via the Internet, use the `-p` option. For example to expose port 80 in a container and map it to port 9999 in the Docker host, you'd specify `-p 9999:80` in the `docker run` command. However, note that this is only an example. Read the task to find out which port to expose.
382 |
383 | **Hint 2**
384 |
385 | After running the container, you can have a look at the ports being exposed. If you see the following:
386 |
387 | ```
388 | 127.0.0.1:8080->8080/tcp
389 | ```
390 |
391 | That means the container can only be accessed through port 8080 from within the host. If you want the container to be accessible from outside the host, the output should look like:
392 |
393 | ```
394 | 0.0.0.0:8080->8080/tcp
395 | ```
396 |
397 | **Hint 3**
398 |
399 | The only ports allowed to receive traffic from outside our EC2 instances are 8080 and 7777. If you run your container on any other port and it needs to be able to receive traffic from outside, it won't work.
400 |
401 | # Task 4: Building and Running The Backend (5000 Points)
402 |
403 | Now things will start to get interesting! Let's talk about Docker networks. **Read the first 3 (three) paragraphs** of the following documentation page: https://docs.docker.com/network/bridge/
404 |
405 | ## Bridge Network
406 |
407 | Before we start developing a Dockerfile for the Backend application, let's create a bridge network for the Backend container. **In the Docker host**, use the command `docker network` to `create` a network called `backend` (do not run this command in the Cloud9 environment, you need to run it in the docker host).
408 |
409 | After successfully creating the network, list the networks (you should see the following):
410 |
411 | ```
412 | $ docker network list
413 | NETWORK ID NAME DRIVER SCOPE
414 | e52edcff0c1e backend bridge local
415 | 57c19bd7421b bridge bridge local
416 | 4a3cd7ddc34d host host local
417 | 3895c7b02506 none null local
418 | ```
419 |
420 | **Go back to the Cloud9 environment to develop a Dockerfile for the Backend.**
421 |
422 | ## Dockerfile
423 |
424 | Here's the difference between the frontend and the backend directories in the repository: the frontend contains all the files necessary to run the application. But, what about the backend? It only contains an empty Dockerfile (how exciting!).
425 |
426 | The backend Dockerfile will look a bit different than the frontend's Dockerfile as you will perform a [Multi-stage build](https://docs.docker.com/develop/develop-images/multistage-build/). Here's all the information you will need in order to develop the backend's Dockerfile:
427 |
428 | * Your Dockerfile should have 2 (two) FROM statements. Both statements should use this image: **alpine:3.12**
429 | * The first stage in your Dockerfile should install git (use the command: `apk add --no-cache git`) and clone the backend repository: [https://github.com/thedojoseries/react-todo-backend](https://github.com/thedojoseries/react-todo-backend)
430 | * Once the repository is cloned, create a second stage using the same image: **alpine:3.12**.
431 | * The second stage should install Node.js and NPM (use the command: `apk add --no-cache nodejs npm`) and copy the backend repository folder from the first stage into the second stage
432 | * Once the repository is copied over, run `npm install` to install all the dependencies
433 | * The container command to run the backend application should be: `npm run server-dev`
434 |
435 | After you build the image and run the container, you should see the following:
436 |
437 | ```
438 | $ docker run
439 |
440 | > docker-dojo-be@1.0.0 server-dev /code
441 | > nodemon src/server --exec babel-node src/server
442 |
443 | [nodemon] 1.19.4
444 | [nodemon] to restart at any time, enter `rs`
445 | [nodemon] watching dir(s): *.*
446 | [nodemon] watching extensions: js,mjs,json
447 | [nodemon] starting `babel-node src/server src/server`
448 | Server running, listening on port 7777
449 |
450 | (...)
451 | (node:58) UnhandledPromiseRejectionWarning: [REDACTED]: failed to connect to server [undefined:27017] on first connect [Error: getaddrinfo ENOTFOUND undefined
452 | at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:66:26) {
453 | name: '[REDACTED]'
454 | }]
455 | (...)
456 | ```
457 |
458 | The error you are seeing is expected because the backend is trying to connect to the database, which is not running yet.
459 |
460 | ## Capture The Flag
461 |
462 | The flag for this task will be the name of the error, which has been replaced in the output above with `[REDACTED]`. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn **5000 points**!
463 |
464 | ## Available Hint
465 |
466 | * How to copy the backend repository folder from the first stage into the second stage - 500 points
467 |
468 | ### Copying the backend repository folder from the first stage into the second stage (500 Points)
469 |
470 | When you clone the backend repository from GitHub in the first stage, take a look at the full path where the repository was cloned to. In the second stage, specify that full path in the `COPY --from` instruction
471 |
472 | # Task 5: Running the Backend in the Correct Network (4500 Points)
473 |
474 | Before you can run the backend application in the Docker host, you will need to push it to your DockerHub account (same way as you pushed the client application).
475 |
476 | Once the backend image has been pushed, **go to the Docker host**. Earlier, we created a network for the backend application called `backend`. This means that whenever you run the backend container, you'll have to specify which network the container will run on. That can be achieved with the `--net` option. Run the backend container in the `backend` network and in background mode using the `docker run -d` command (you don't need to worry about the port for now, just run the container specifying the correct network).
477 |
478 | ## Capture The Flag
479 |
480 | To make sure you are running the backend container in the correct network, let's [inspect](https://docs.docker.com/engine/reference/commandline/inspect/) the container. To inspect a container, you can use the `docker inspect` command. When inspecting a container, the command outputs a JSON array with an object that contains tons of information about the container. Run `docker inspect` on the backend container and find the name of the network on which the container is running.
481 |
482 | To find the flag for this task, you will have to use [jq](https://stedolan.github.io/jq/), which has already been installed in your Docker host. With jq, you're able to use filters to query JSON objects. For example, take a look at the JSON object below:
483 |
484 | ```json
485 | {
486 | "firstName": "John",
487 | "lastName" : "doe",
488 | "age" : 26,
489 | "address" : {
490 | "streetAddress": "naist street",
491 | "city" : "Nara",
492 | "postalCode" : "630-0192"
493 | },
494 | "phoneNumbers": [
495 | {
496 | "type" : "iPhone",
497 | "number": "0123-4567-8888"
498 | },
499 | {
500 | "type" : "home",
501 | "number": "0123-4567-8910"
502 | }
503 | ]
504 | }
505 | ```
506 |
507 | If you put that object into a file called `obj.json` and ran:
508 |
509 | ```
510 | cat obj.json | jq .firstName
511 | ```
512 |
513 | The command above would return:
514 |
515 | ```json
516 | "John"
517 | ```
518 |
519 | In this case, `.firstName` is the filter and returns the value of the key `firstName`.
520 |
521 | **The flag of this task** will be the jq filter that returns the **IP address of the backend container running in the backend network**. You can run the following command to input the output of `docker inspect` into jq:
522 |
523 | ```
524 | docker inspect | jq ''
525 | ```
526 |
527 | The expected output should be the IP address in quotes:
528 |
529 | ```
530 | "X.X.X.X"
531 | ```
532 |
533 | ATTENTION: because the JSON object from `docker inspect` is an array, you will have to filter for the first item of the array. **Make sure you include an INDEX (i.e. a number) in your filter**. If you do not include an index in the filter, it could still work, but it will not be the correct flag.
534 |
535 | Find out what `` should be and submit it using the format `DevSlopCTF{}` (no single quotes around ``) to earn **4500 points**!
536 |
537 | # Task 6: Starting the Database (4700 Points)
538 |
539 | Remember when you created a Bridge network for the Backend in the Docker host? Let's do the same for the database.
540 |
541 | ## Bridge Network
542 |
543 | Create a user-defined bridge network and call it `db`.
544 |
545 | You should have the following networks:
546 |
547 | ```
548 | $ docker network list
549 | NETWORK ID NAME DRIVER SCOPE
550 | 8a6c11vd618a db bridge local
551 | e52edcff0c1e backend bridge local
552 | 57c19bd7421b bridge bridge local
553 | 4a3cd7ddc34d host host local
554 | 3895c7b02506 none null local
555 | ```
556 |
557 | The Dockerfile for the database will be the easiest one you will be building today.
558 |
559 | ## Dockerfile
560 |
561 | Use the following information to develop the Dockerfile for the database (MongoDB) **in the Cloud9 environment**:
562 |
563 | * Use the alpine:3.9 image
564 | * Run the following command: `apk add --no-cache mongodb` (installs MongoDB)
565 | * Use the command `mongod --bind_ip_all` as entrypoint. The option `--bind_ip_all` will make MongoDB bind to 0.0.0.0, meaning that it will accept incoming connections from anywhere. If you don't specify this option, MongoDB will only accept traffic coming from 127.0.0.1 (i.e. processes running on the same container), so no other container will be able to communicate with it.
566 |
567 | Push the image to your Dockerhub account and run the container **in the Docker host** (remember to run it in the `db` network you just created):
568 |
569 | ```
570 | $ docker run --net db
571 |
572 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=a241d165b821
573 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] db version v3.4.10
574 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] git version: 078f28920cb24de0dd479b5ea6c66c644f6326e9
575 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] OpenSSL version: LibreSSL 2.6.5
576 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] allocator: system
577 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] modules: none
578 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] build environment:
579 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] distarch: x86_64
580 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] target_arch: x86_64
581 | 2019-05-09T11:12:33.428+0000 I CONTROL [initandlisten] options: {}
582 | 2019-05-09T11:12:33.465+0000 I STORAGE [initandlisten] exception in initAndListen: 29 Data directory /data/db not found., terminating
583 | 2019-05-09T11:12:33.465+0000 I NETWORK [initandlisten] shutdown: going to close listening sockets...
584 | 2019-05-09T11:12:33.465+0000 I NETWORK [initandlisten] shutdown: going to flush diaglog...
585 | 2019-05-09T11:12:33.465+0000 I CONTROL [initandlisten] now exiting
586 | 2019-05-09T11:12:33.466+0000 I CONTROL [initandlisten] shutting down with code:100
587 | ```
588 |
589 | Note how the database just shut itself down at the end. That is because MongoDB, by default, requires the path `/data/db` to exist inside the container before it runs. However, we do not want the data to be stored only in the container, because if the container dies, you will lose all the data. What you need to do is to create a directory in the host (EC2 instance) where data will be persisted. Then, when running the container, you'd just tell Docker to *mount* the **host filesystem into the container**. This way, even if the container dies, the data is still being safely stored in the host. Let's learn about [Volumes](https://docs.docker.com/storage/volumes/) next.
590 |
591 | In Linux, Docker offers three types of mounts: [Volumes](https://docs.docker.com/storage/volumes/), [Bind mounts](https://docs.docker.com/storage/bind-mounts/) and [tmpfs mounts](https://docs.docker.com/storage/tmpfs/). You should not use tmpfs mounts for databases because data is not stored on disk, it's stored temporarily in memory. So that leaves us Volumes and Bind mounts.
592 |
593 | From the official documentation, **Volumes** are stored in a part of the host filesystem which is **managed by Docker** (`/var/lib/docker/volumes/` on Linux). Non-Docker processes should not modify this part of the filesystem. However, **Bind mounts** may be stored **anywhere** on the host system. They may even be important system files or directories. Non-Docker processes on the Docker host or a Docker container can modify them at any time.
594 |
595 | Docker recommends **Volumes** over **Bind mounts**, so let's go with Volumes.
596 |
597 | [Refer to the documentation](https://docs.docker.com/storage/volumes/#create-and-manage-volumes) and create a volume called `db-vol` **in the Docker host**. Then, [run the database container one more time and specify the new volume](https://docs.docker.com/storage/volumes/#start-a-container-with-a-volume).
598 |
599 | Now, you should get different logs:
600 |
601 | ```
602 | (...)
603 | 2020-11-12T12:44:12.647+0000 I CONTROL [initandlisten] MongoDB starting : pid=1 port=27017 dbpath=/data/db 64-bit host=a947a2d0b9e0
604 | 2020-11-12T12:44:12.647+0000 I CONTROL [initandlisten] db version v4.0.5
605 | (...)
606 | 2020-11-12T12:44:13.208+0000 I STORAGE [initandlisten] createCollection: local.startup_log with generated UUID: 1a178090-b449-4beb-b5d8-bf88c4af167b
607 | 2020-11-12T12:44:13.221+0000 I FTDC [initandlisten] Initializing full-time diagnostic data capture with directory '/data/db/diagnostic.data'
608 | 2020-11-12T12:44:13.222+0000 I NETWORK [initandlisten] waiting for connections on port 27017
609 | (...)
610 | ```
611 |
612 | If it says `waiting for connections on port 27017`, then it worked!
613 |
614 | The container is running in foreground. Kill the container (`CTRL+C`) and run it again in background mode.
615 |
616 | ## Capture The Flag
617 |
618 | Remember when you used the command `docker inspect` to find out the IP Address of the container? Now, use the same command to find out which path in the Docker host is being used for the volume that you created. Once you have the path, list all the files in that directory. There will be one file with extension `.bson` (BSON is the binary encoding of JSON-like documents that MongoDB uses when storing documents in collections). The flag will be the name of the file **without the extension**. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn **4700 points**!
619 |
620 | # Task 7: Connecting the Backend to the Database - Part 1 (2500 Points)
621 |
622 | The first step will be to pass in an environment variable to the backend application. Run `docker ps` in the docker host to list all the running containers, take the ID of the backend container and run `docker logs `. Pay attention to the following message:
623 |
624 | ```
625 | (node:58) UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [undefined:27017] on first connect
626 | ```
627 |
628 | This `undefined` means Node.js was expecting to find an environment variable in the container, but the variable was not found. Here's the portion of the Node.js code where an environment variable is expected:
629 |
630 | ```
631 | const mongoContainerName = process.env.MONGODB_HOST
632 | const url = `mongodb://${mongoContainerName}:27017/myorganizer`;
633 | ```
634 |
635 | As you can see in the first line, Node.js expects an environment variable called `MONGODB_HOST` (environment variables can be accessed in Node.js using `process.env`). The value of this variable should be either the **IP address** or the **DNS name** of the MongoDB container. [If the backend and the database containers were deployed to the same user-defined bridge network, you could use a DNS name](https://stackoverflow.com/a/35691865). However, because the backend and the database are in **two separate user-defined bridge networks**, the backend container **cannot** use the database container's name to reach out to it. By default, DNS resolution only works locally in each user-defined bridge network. Therefore, the value of `MONGODB_HOST` should be an **IP address**, not an DNS name.
636 |
637 | First, let's grab the IP address of the database container. In the docker host, run `docker inspect` on the database container and grab its IP address.
638 |
639 | Now, shut down the backend container using the command `docker stop`. Spin up the container again in background mode, and pass in the environment variable `MONGODB_HOST`, setting it to the IP address of the database container using the `-e` option. [Read the documentation to understand how to use this option](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file).
640 |
641 | Use `docker logs` to obtain the container logs and you should see the following:
642 |
643 | ```
644 | > docker-dojo-be@1.0.0 server-dev /code
645 | > nodemon src/server --exec babel-node src/server
646 |
647 | [nodemon] 1.19.0
648 | [nodemon] to restart at any time, enter `rs`
649 | [nodemon] watching: *.*
650 | [nodemon] starting `babel-node src/server src/server`
651 | Server running, listening on port 7777
652 | ```
653 |
654 | However! If you wait 30-60 seconds, you will see a new message:
655 |
656 | ```
657 | (node:58) UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [X.X.X.X:27017] on first connect [REDACTED: connection timed out
658 | (...)
659 | ```
660 |
661 | By default, Docker does not allow containers in two separate bridge networks to communicate with each other. That's why you see the error above. What you need to do is to figure out a way for these two networks to be able to communicate with each other.
662 |
663 | **PS: If you've got the message `Connected to MongoDB!`, that probably means the backend and database container are running on the same network. Make sure you run these containers using `--net` and place them in their own respective networks before proceeding to the next tasks.**
664 |
665 | ## Capture The Flag
666 |
667 | In the error above, you saw another `REDACTED`. That's the flag. Submit it using the format `DevSlopCTF{FLAG}` to earn **2500 points**!
668 |
669 | # Task 8: Connecting the Backend to the Database - Part 2 (15000 Points)
670 |
671 | The next step will be to connect the Backend bridge network to the Database bridge network. This step will require a bit of networking knowledge, but don't worry, we'll guide you through what needs to be done.
672 |
673 | Run the following command in the Docker host:
674 |
675 | ```
676 | sudo iptables -t filter -vL
677 | ```
678 |
679 | This will list a few chains, such as **INPUT**, **OUTPUT**, **DOCKER-ISOLATION-STAGE-1**, **DOCKER-ISOLATION-STAGE-2** etc. There are a lot of rules, but let's focus on the **DOCKER-ISOLATION-STAGE-2** chain.
680 |
681 | You should see something similar to the following:
682 |
683 | ```
684 | Chain DOCKER-ISOLATION-STAGE-2 (3 references)
685 | pkts bytes target prot opt in out source destination
686 | 10 600 DROP all -- any br-111111111111 anywhere anywhere
687 | 0 0 DROP all -- any br-222222222222 anywhere anywhere
688 | 0 0 DROP all -- any docker0 anywhere anywhere
689 | 3128 165K RETURN all -- any any anywhere anywhere
690 | ```
691 |
692 | The IDs starting with `br-` are IDs for network interfaces. If you run `ifconfig`, you should see:
693 |
694 | ```
695 | br-111111111111: flags=4163 mtu 1500
696 | inet 172.19.0.1 netmask 255.255.0.0 broadcast 172.19.255.255
697 | inet6 fe80::42:b0ff:fe16:67 prefixlen 64 scopeid 0x20
698 | ether 02:42:b0:16:00:67 txqueuelen 0 (Ethernet)
699 | RX packets 0 bytes 0 (0.0 B)
700 | RX errors 0 dropped 0 overruns 0 frame 0
701 | TX packets 0 bytes 0 (0.0 B)
702 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
703 |
704 | br-222222222222: flags=4099 mtu 1500
705 | inet 172.18.0.1 netmask 255.255.0.0 broadcast 172.18.255.255
706 | inet6 fe80::42:d4ff:fee2:e3c prefixlen 64 scopeid 0x20
707 | ether 02:42:d4:e2:0e:3c txqueuelen 0 (Ethernet)
708 | RX packets 8 bytes 648 (648.0 B)
709 | RX errors 0 dropped 0 overruns 0 frame 0
710 | TX packets 8 bytes 648 (648.0 B)
711 | TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
712 | ```
713 |
714 | Your interface IDs (and maybe IPs) will probably be different, so don't worry if some information here doesn't match what you're seeing in your terminal.
715 |
716 | Now back to the IP table, take a look at the following two rules in the `DOCKER-ISOLATION-STAGE-2` chain:
717 |
718 | ```
719 | 10 600 DROP all -- any br-9ebf3477d04d anywhere anywhere
720 | 0 0 DROP all -- any br-cedb7f13e197 anywhere anywhere
721 | ```
722 |
723 | These two rules are responsible for dropping packets that are going out from these two Docker networks. This means that whenever the backend application in the `backend` network tries to reach out to the database in the `db` network, the traffic is dropped.
724 |
725 | What you will have to do to allow this connection is to create two additional iptables rules:
726 |
727 | * One allowing traffic from `backend` to the `db` network
728 | * and another allowing traffic from `db` to the `backend` network
729 |
730 | When creating the rules, you will need to specify which chain the rule will belong to. There should be four Docker-related chains in your IP Table: `DOCKER`, `DOCKER-ISOLATION-STAGE-1`, `DOCKER-ISOLATION-STAGE-2`, `DOCKER-USER`. Choose the most appropriate chain.
731 |
732 | *HINT: use the `iptables` command to create the rules*
733 |
734 | Once the rules have been created, let's see if the backend is able to connect to the database. Here's how you can test it:
735 |
736 | * Grab the ID of the backend container and run: `docker exec -it sh`. This command will take you into the container
737 | * Install telnet: `apk add --no-cache busybox-extras`
738 | * From the **backend container** try to connect to MongoDB: `telnet X.X.X.X 27017` (where `X.X.X.X` is the private IP address of the database container)
739 |
740 | If you're able to connect, you should get the following message from `telnet`:
741 |
742 | ```
743 | Connected to X.X.X.X
744 | ```
745 |
746 | To exit, press `CTRL+C`, then press `e`.
747 |
748 | ## Capture The Flag
749 |
750 | The flag for this task is hidden in the network traffic being exchanged between the backend and the database. What you will need to do is to capture the network traffic going through the database container's Network Interface and analyze it.
751 |
752 | To capture the network traffic going through the database container's network interface, you should use the `tcpdump` command. Run the following command, replace `br-xxxxxxxxxx` with the ID of the database container's network interface (which you should've obtained already when you ran `ifconfig`):
753 |
754 | ```
755 | sudo tcpdump -i br-xxxxxxxxxx -nnSX
756 | ```
757 |
758 | The command above will output the traffic going through the network interface specified. The traffic is broken down into multiple chunks where you will see at the top of each chunk information about the packet (like timestamp, source IP, destination IP, etc) and below that an Hexadecimal representation of the message on the left and an ASCII representation of the message on the right. For example:
759 |
760 | ```
761 | 15:08:23.749857 IP 172.19.0.1.35240 > 172.19.0.2.27017: Flags [P.], seq 514019149:514019180, ack 1526378127, win 1414, options [nop,nop,TS val 591804980 ecr 318629181], length 31
762 |
763 | 0x0000: 4500 0053 ae13 4000 fe06 7667 ac13 0001 E..S..@...vg....
764 | 0x0010: ac13 0002 89a8 6989 1ea3 4f4d 5afa ae8f ......i...OMZ...
765 | 0x0020: 8018 0586 586f 0000 0101 080a 2346 3a34 ....Xo......#F:4
766 | 0x0030: 12fd e53d 1f00 0000 0869 736d 6173 7465 ...=.....ismaste
767 | 0x0040: 7200 0102 2464 6200 0600 0000 6164 6d69 r...$db.....admi
768 | 0x0050: 6e00 00 n..
769 | ```
770 |
771 | If you wait a few seconds and the backend is able to communicate with the database, you should see the following message in ASCII format:
772 |
773 | ```
774 | E.....@...&.....
775 | ....i...Z.....Ol
776 | ....Y0..........
777 | #F:4........t...
778 | ..............is
779 | master...maxBson
780 | ObjectSize......
781 | maxMessageSizeBy
782 | tes..l...maxWrit
783 | eBatchSize......
784 | localTime.E.3.u.
785 | ...logicalSessio
786 | nTimeoutMinutes.
787 | .....minWireVers
788 | ion......maxWire
789 | Version......rea
790 | dOnly...ok......
791 | ..?.
792 | ```
793 |
794 | Take a look at this specific line:
795 |
796 | ```
797 | Version......rea
798 | ```
799 |
800 | On the left-hand side, there will be an Hexadecimal representation of the string `Version......rea`. The flag is the first two (2) octets (no space) **of the Hexadecimal representation reading from right to left**. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn a whopping **15000 points**!
801 |
802 | # Task 9: Connecting the Backend to the Database - Part 3 (3300 Points)
803 |
804 | Now that the backend application is able to communicate with the database, let's restart it. Kill the container and run it again, but this time, expose port `7777` so the client application can later communicate with the backend. If you see the message `Connected to MongoDB!`, that means it's all working!
805 |
806 | ```
807 | > docker-dojo-be@1.0.0 server-dev /code
808 | > nodemon src/server --exec babel-node src/server
809 |
810 | [nodemon] 1.19.0
811 | [nodemon] to restart at any time, enter `rs`
812 | [nodemon] watching: *.*
813 | [nodemon] starting `babel-node src/server src/server`
814 | Server running, listening on port 7777
815 | Connected to MongoDB!
816 | ```
817 |
818 | ## Capture The Flag
819 |
820 | In the Docker host, run `docker ps` and copy the ID of the database container. Then, run `docker logs `, where `` is the ID of the database container. In the logs, you will see a message saying `connection accepted from X.X.X.X:YYYY #Z (1 connection now open)` (you might see it more than once, but look at the last occurrence). Then right after, you should see a message starting with `received client metadata from X.X.X.X:YYYYY connX:`. The `received client metadata from (...)` message includes a payload (in JSON format). In this payload, you will see 3 versions in the format `..`. Get the `` number of each version, multiply them and **you will have the flag for this task**. For example, if you had to multiply the major number of the three versions below:
821 |
822 | - 6.1.8
823 | - 8.12.6
824 | - 10.5.7
825 |
826 | The result would be `6 * 8 * 10 = 480`. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn **5005 points**!
827 |
828 | # Task 10: Connecting the Frontend to the Backend (4500 Points)
829 |
830 | The last step will be to connect the frontend to the backend. Take a look at the code below (client application):
831 |
832 | `frontend/src/app/components/TodoContainer.jsx`
833 | ```
834 | import React from 'react';
835 | import { TodoList} from './TodoList';
836 | import axios from 'axios';
837 | import './App.scss';
838 | import { Link } from 'react-router-dom';
839 |
840 | // Contaner Component
841 | // Todo Id
842 | window.id = 1;
843 | export class TodoApp extends React.Component{
844 | constructor(props){
845 | // Pass props to parent class
846 | super(props);
847 | // Set initial state
848 | this.state = {
849 | tasks: []
850 | }
851 | this.apiUrl = `http://__BACKEND_IP__:7777`;
852 | }
853 |
854 | (...)
855 | ```
856 |
857 | `frontend/src/app/store/sagas.js`
858 | ```
859 | import { take, put, select } from 'redux-saga/effects';
860 | import uuid from 'uuid';
861 | import axios from 'axios';
862 |
863 | import { history } from './history'
864 | import * as mutations from './mutations';
865 | const url = process.env.NODE_ENV === 'production' ? `` : `http://__BACKEND_IP__:7777`;
866 |
867 | (...)
868 | ```
869 |
870 | To connect the client to the backend, you will have to replace all occurrences of `__BACKEND_IP__` with a public IP address. You **should not** use the backend container's private IP address because the client application will be running on your web browser, in your laptop. Therefore, if you specified a private IP address which can only be accessed inside the EC2 instance, the client would never be able to reach the backend.
871 |
872 | Now, pay attention to the following. You cannot hardcode the IP address into the client code. You will have to replace `__BACKEND_IP__` during build time (i.e. when you're building the container image with the `docker build` command). That can be achieved by running a Unix program in a Docker `RUN` instruction.
873 |
874 | ## The Final Test
875 |
876 | Here's the final test to see if you've completed the challenge.
877 |
878 | First, you should see the same interface you saw when you deployed the frontend application:
879 |
880 | 
881 |
882 | Click on the Login button. If you're still getting the message `Login incorrect`, that means the client application is not able to communicate with the server application. Otherwise, you should see the following interface:
883 |
884 | 
885 |
886 | ## Capture The Flag
887 |
888 | Once you are able to log in, you will need to find out the size of the payload being returned by the backend that contains all the tasks. To find that out, [open your Browser's developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools). Look for a tab called **Network** and click on it. Now that you can see all API calls being made by the client application, refresh the page where you see the tasks (if you need to log in again, just click on the blue Login button) and observe the API calls being made. One of the last API calls should be to `/tasks`. Analyze that API call and the flag will be the number of Bytes of the payload that contains all the tasks. Submit the correct flag using the format `DevSlopCTF{FLAG}` to earn the last **4500 points** of the competition!
889 |
890 | ## Available Hints
891 |
892 | Here are the available hints for this task and how much points you will lose by unlocking them:
893 |
894 | * How to replace __BACKEND_IP__ during build time - **500 points**
895 |
896 | #### Replacing BACKEND_IP during build time (500 points)
897 |
898 | To replace __BACKEND_IP__ with the Backend's IP, you can use the Unix command [sed](https://www.geeksforgeeks.org/sed-command-in-linux-unix-with-examples/). For example, to replace all occurrences of `__A__` with `__B__` on the `test.txt` file, you'd run the following:
899 |
900 | ```
901 | sed -i "s/__A__/__B__/g" test.txt
902 | ```
903 |
904 | # Flags
905 |
906 | These are the flags for each one of the tasks:
907 |
908 | * Task 1: `DevSlopCTF{c2e3ff}`
909 | * Task 2: `DevSlopCTF{latest:digest:sha256:size}`
910 | * Task 3: `DevSlopCTF{__backend_ip__:7777}`
911 | * Task 4: `DevSlopCTF{MongoNetworkError}`
912 | * Task 5: `DevSlopCTF{.[0].NetworkSettings.Networks.backend.IPAddress}`
913 | * Task 6: `DevSlopCTF{storage}`
914 | * Task 7: `DevSlopCTF{MongoNetworkTimeoutError}`
915 | * Task 8: `DevSlopCTF{6561}`
916 | * Task 9: `DevSlopCTF{144}`
917 | * Task 10: `DevSlopCTF{754}`
918 |
--------------------------------------------------------------------------------
/backend/Dockerfile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thedojoseries/docker-ctf/9cecff38ded6b4f58f7b0a3384ed4378abfcaf66/backend/Dockerfile
--------------------------------------------------------------------------------
/database/Dockerfile:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thedojoseries/docker-ctf/9cecff38ded6b4f58f7b0a3384ed4378abfcaf66/database/Dockerfile
--------------------------------------------------------------------------------
/frontend/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env",{
4 | "targets":{
5 | "node":"current"
6 | }
7 | }],
8 | "@babel/preset-react"
9 | ],
10 | "plugins": [
11 | "@babel/plugin-proposal-class-properties"
12 | ]
13 | }
--------------------------------------------------------------------------------
/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM
2 |
3 | RUN
4 |
5 | COPY
6 |
7 | CMD [ "executable" ]
8 |
--------------------------------------------------------------------------------
/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | An Application
5 |
6 |
7 |
8 |
9 |