Container environment setup
Overview
This guide will demonstrate how to set up the tools and development environment we will use for the remaining projects in this course. We will provide our environment using a container, a technology that provides an abstraction of a separate OS without the full overhead of a Virtual Machine (VM). This container runs a Linux-based operating system, Ubuntu 20.04. In addition, we will set up some additional tools to allow you to run Wireshark, and industry-standard packet capture tool, from inside your container. Wireshark is useful as a debugging tool, and a great way to learn about networks.
When we grade your work, we will use the same container environment–so if your program works in your container, it will work in our grading environment.
Sound familiar? If you’ve taken CS 300, you may have worked with a similar container setup before. We are building on CS 300’s environment for this course (thanks, CS 300 course staff!). Our container uses a few extra components compared to the CS300 container, so if you already have one from another course, you still need to set up this one.
Why are we using these tools?
- You’ll be able to develop locally. With the container environment, we can specify a standard development environment you can run on your own machine, so your code can work on any system. Thus, you don’t need to log into the department machines to write/test your code!
- Wireshark is awesome. Wireshark is a powerful debugging tool for building network programs: as you send packets over the network, you can view their contents and determine if they are formatted properly. You can also perform a number of analyses over different types of network connections (like TCP), which we will see later in the course.
What if I don’t have (or don’t want to use) my own computer?
-
For students who do not have access to their own laptop, note that the Brown IT service provides laptops you can borrow for free
-
If you do not have a personal system to use, we will make sure you have a way to develop code for this course. If you already indicated that you don’t have a personal computer for development on HW0, look for an email from us within 24 hours of the IP project release date with instructions. Otherwise, if access to a personal computer is an issue, please email the TAs list (
cs1680tas@lists.brown.edu
) and we will work something out. -
You can still develop for assignments on the department machines, but you will not have access to Wireshark.
Environment setup
To run our environment, there are two components you need to configure on your host computer: Docker, the program that builds and runs the container, and an X11 server, which is responsible for displaying Wireshark’s window on your system.
Some of the configuration steps here differ based on your host platform, i.e. the system you are using to run the container, which is probably Windows, Mac OS, or Linux. Please make sure you follow the correct set of instructions for your platform.
Configure docker
Docker is one of the most popular container solutions and widely used in industry.
-
Download and install Docker Desktop, located here. On Linux machines, follow the instructions here.
-
On Windows or macOS, open the Docker Desktop application after it has been installed. You may see a message similar to “Your Docker is starting…”. Once this message goes away, your Docker has started successfully!
Click for extra instructions for Windows-based systems
To run the following steps in this lab, you will need to set up Windows Subsystem for Linux (WSL). WSL should already be enabled after you install Docker, but you may still need to install a Linux distribution. This will run in an actual Linux VM, and you will run your Docker container within that VM (turtles all the way down for you!).
- Do I have a Linux distribution (Linux distro) installed?
- Run
wsl -l -v
in the Command Prompt or Powershell. If there is only “Docker Desktop” and “Docker Desktop Data”, you do not have a Linux distribution installed. Proceed to step 2. - Otherwise, you have a Linux distro installed. Proceed to step 3.
- Run
- Install a Linux Distribution.
- Run
wsl --set-default-version 2
to ensure Ubuntu will be installed under WSL 2. - Install “Ubuntu 20.04” from Microsoft Store.
- Click “Open” after Ubuntu is downloaded. A terminal will open and guide you through the installation process.
- Run
- Ensure your Linux Distribution runs on WSL 2.
- From the output of
wsl -l -v
, find out if your Linux distro is using WSL 1 or WSL 2. If it’s WSL1:- Run
wsl --set-version <distro name> 2
to update your distro to use WSL 2.
- Run
- From the output of
- Set your default Linux distro
- Run
wsl --setdefault <distro-name>
to configure your default Linux distro.<distro-name>
should be “Ubuntu-20.04” if you installed using step 2.
- Run
Enter wsl
in your Command Prompt or Powershell, and you’ll enter into your WSL! For the rest of the Lab, run commands within your WSL Linux environment, unless otherwise specified.
You will also need to connect Docker with WSL. To do so, open your Docker Desktop’s settings (on its top right corner), click “Resources”, “WSL integration”, then enable integration with your Linux distro. Then, click “Apply and Restart”.
- Verify Docker is installed by executing the following:
$ docker --version
- After installing Docker, a Docker process (the Docker daemon) will run
in the background. Run the following command to verify:
$ docker info
If you see the following error:
ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
it means Docker hasn’t started running yet. On Windows or macOS, ensure your Docker Desktop is running. On Linux, try the command
sudo systemctl docker restart
in a terminal.
X Server
The next component we need is an X11 server. X11, or X Window System is the framework that powers most GUI applications in Linux. Wireshark is a GUI application, so when we run it in the container, it needs to connect to a program on the host (the X server) that can draw its window.
The X server we will install differs based on your host operating system. Select the set of instructions for your platform:
Instructions for Windows
-
Download and install VcXsrv, which is an X11 server built for Windows. Run the installer (default options should be fine), and allow the app to make changes to your computer when prompted.
-
Once VcXsrv is installed, run it from the start menu. If it asks you some questions, you can just accept the defaults and click “Next” until it goes away.
-
Once VcXsrv has finished starting, you should see it running in your system tray (bottom right corner of your screen), indicating you are ready to continue.
Note: Each time you restart your computer, you will need to start VcXsrv whenever you need to run the container.
Instructions for Mac OS
-
Download and install XQuartz, which is an X11 server built for Mac OS. Run the installer, and allow the app to make changes to your computer when prompted. During installation, you will be asked log out and log back into your computer, in order to update your system environment.
-
Once XQuartz is installed, open it from the applications menu. Once it opens, go to the menu bar at the top of your screen and select XQuartz > Preferences. Go to the Security tab and check the box labeled “Allow connections from network clients”. This will allow the container to connect to your x server.
-
Quit XQuartz by selecting XQuartz > Quit from the menu bar. XQuartz should re-open automatically when you start the container later.
Why am I allowing network connections to XQuartz? Isn’t this a security risk?
The X server itself provides its own authentication mechanism to prevent outside hosts from connecting. When we run the container, we tell the X server to allow connections from the virtual network that Docker creates on your computer–this network is only accessible within your own system, so outside users cannot connect to it.
If you are still concerned, however, you can turn on the Mac OS firewall, which will prevent outside connections to the X11 port at the network level.
Instructions for Linux
If you use Linux, you most likely already have an X server installed, so you have little work to do here! There is one other small utility we need, xhost
, a tool to control who can access the X server.
Check if you have xhost
by entering the following command:
$ which xhost
If the result is xhost not found
, you will need to install this on your system. On Ubuntu or Debian, you can do this using the command sudo apt-get install x11-xserver-utils
. On other distributions, you will need to install a similar package (though the name may be different).
Once you have installed xhost
, you should get a result like the following:
$ which xhost
/usr/bin/xhost
Set up the container environment
In Docker, an environment is defined as a Docker image. An image specifies the Os environment that a container provides. as well as other software dependencies and configurations. The instructions defining an image are specified in a file called the Dockerfile.
Next, you will download the course’s setup code and create the CS 1680 Docker image!
Instructions for Mac OS users
If you’re running on macOS, you will need to install a set of Apple-recommended command-line tools via the following command:
xcode-select --install
This ensures that your computer has installed git
, a program we’ll use below. Alternatively, you may also download and install git
directly, following instructions here. After that, continue with the rest of these instructions.
Do the following to set up your development environment:
-
Enter the directory on your computer where you want to do your coursework. For Windows users, choosing somewhere in the C drive will make the following steps easier.
- Enter the following command to download our development environment to the new subdirectory
<DEV-ENVIRONMENT>
(you can choose your own name, without the brackets,<DEV-ENVIRONMENT>
is a placeholder):$ git clone https://github.com/brown-csci1680/container-env.git DEV-ENVIRONMENT
-
Run
cd <DEV-ENVIRONMENT>
to enter the directory you just created - Inside this directory, do the following:
$ cd docker # Enter the directory containing Dockerfiles $ ./cs1680-build-docker # Builds our course docker image
./cs1680-build-docker
builds your Docker image, which may take a while. It’s normal for this script to take up to 20 minutes, so feel free to take a break!
Why are there two Dockerfiles?
You may notice that there are two Dockerfiles in the docker folder: Dockerfile and Dockerfile.arm64. Why are there two Dockerfiles, when CS 1680 only uses one container?
Machines run on different architectures, and we will explore that in our course during the semester. Computers that use an Intel chip runs on Intel’s x86 architecture. Recent Macs released by Apple begin to use Apple silicon, which run on the ARM architecture.
Different architectures use different sets of CPU instructions. Therefore, programs running on different architectures need to be compiled differently.
Students running on ARM machines use Dockerfile.arm64, which installs additional packages to compile x86 binaries on an ARM machine. Other students, running on chips that use the x86 architecture, use Dockerfile.
Entering the container
Once you created your Docker image, we need to create a container running the image. In Docker terms, a /container/ is an /instance/ of an image, which is where you will actually do your work. Docker (and other container frameworks) are designed to easily start up and tear down individual containers based on a single image.
You can enter your container as follows:
-
Make sure you’re inside he directory
<DEV-ENVIRONMENT>
, which is the top-level directory of the repository earlier. (If you’re following these instructions exactly,cd ..
should get you there.) -
Run the script
cs1680-run-docker
to start the container, and poke around to get a sense of the environment: (Windows users: If you get errors, see the instructions below.)
$ ./cs1680-run-docker # enters your Docker container
cs1680-user@9899143429a2:~$ # you're inside the container!
cs1680-user@9899143429a2:~$ uname
Linux
cs1680-user@9899143429a2:~$ echo "Hello world!"
Hello world!
cs1680-user@9899143429a2:~$ ls -lah
total 24K
drwxr-xr-x 6 cs1680-user cs1680-user 192 Jan 25 22:23 .
drwxr-xr-x 1 root root 4.0K Jan 25 22:25 ..
-rw-r--r-- 1 cs1680-user cs1680-user 132 Jan 25 22:23 .bash_profile
-rw-r--r-- 1 cs1680-user cs1680-user 4.0K Jan 25 22:23 .bashrc
-rw-r--r-- 1 cs1680-user cs1680-user 25 Jan 25 22:23 .gdbinit
-rw-r--r-- 1 cs1680-user cs1680-user 813 Jan 25 22:23 .profile
cs1680-user@9899143429a2:~$ exit # or Ctrl-D
Extra instructions for Windows users
If you see errors along the lines of bash:
'\r': command not found
or bash: /home/cs1680-user/.bash_profile: line
5: syntax error: unexpected end of file
when you enter the container,
you need to convert your files’ line endings (characters that
delineate the end of a line) from Windows to UNIX (Linux) format.
To do so, do the following:
-
Enter into WSL. You may do so by opening Ubuntu in the start menu or through the
wsl
command. -
Use
cd
to navigate to the folder where you just cloned the setup repository. If you cloned your setup repository to your C: drive, usecd /mnt/c
to enter the C: drive from your WSL. -
Run
sudo apt-get update
, then installdos2unix
withsudo apt-get -y install dos2unix
. -
Run
dos2unix ./cs1680-run-docker
(you should only need to run this once). -
Next, run
./cs1680-run-docker
. -
If you see the next line begins with
cs1680-user@@9899143429a2:~$ ...
, you’re inside the container, hopefully without errors!
Don’t worry if the number after cs1680-user is different. This is an identifier that uniquely identifies this container.
You may run any Linux commands inside this container, such as running your code for this course. To exit, enter exit or use Ctrl-D.
Resuming your work / Multiple shells
Once you have exited the container, you can open it again by running ./cs1680-run-docker
again from the <DEV-ENVIRONMENT>
directory. This will restart your current container if it exited in the same state it was in before.
If you want to open another shell in the same container, simply open a
new terminal window on your computer, navigate to the
<DEV-ENVIRONMENT>
directory and run cs1680-run-docker
–the script
will automatically detect that the container is already running and
“attach” itself to the current container.
Working in the container environment
Shared folders
“If my docker container is a separate (virtual) computer than my laptop, how will I move files between the two?”, you may ask. Great question!
Inside of the container, your home directory (/home/cs1680-user
, or
~
) is actually a mount of the home directory inside your
<DEV-ENVIRONMENT>
directory. Any changes you make in one will be
visible in the other.
At this stage, you should test this out to make sure it works (and to make sure you understand what’s happening).
-
Outside the container, go to the directory
<DEV-ENVIRONMENT>/home
and create a file and a directory. -
Inside the container, you should see the file and folder you created inside your home directory. Delete the file, and add a file to folder (again, this is just a test to see what’s happening).
-
Look at the
<DEV-ENVIRONMENT>/home
directory outside the container–you should see the changes you made in this directory.
Help
- Outside of the container, in your
<DEV-ENVIRONMENT>/home
folder:$ touch cool_file $ mkdir awesome_folder $ cd .. $ ./cs1680-run-docker
- Inside the container:
cs1680-user@9899143429a2:~$ ls # Show the file and dir we just created awesome_folder cool_file cs1680-user@9899143429a2:~$ rm cool_file cs1680-user@9899143429a2:~$ cd awesome_folder cs1680-user@9899143429a2:~$ touch even_cooler_file cs1680-user@9899143429a2:~$ exit # or just CTRL-D
- Back outside the container:
$ cd home # this enters the mounted directory $ ls # should just show awesome_folder awesome_folder $ cd awesome_folder $ ls # should show even_cooler_file even_cooler_file
Using shared folders
As you work on your code, you can take advantage of shared folders, for example:
- You could clone your repository and use git outside the container, rather than setting up SSH keys inside the container
- You can edit your code outside the container using your favorite graphical editor (VSCode, CLion, …) and the compile/run your work inside the container
Warning: if you move or rename your <DEV-ENVIRONMENT>
directory…
Your Docker container will still try to mount to the original
dev-environment path, even after you rename, remove, or move the
folder DEV-ENVIRONMENT
.
After moving your dev-environment folder, you’ll need to delete the old container and start a new container. You can do so with:
./cs1680-run-docker --clean
You should be able to enter a container, and see all of your work now!
Modifying the container
Once you have a shell inside the container, you can run any Linux command as you would on any other Linux system. Feel free to play around, install packages, etc. The user cs1680-user
has passwordless sudo
access inside the container, so to install a package you can simply run (eg. for vim
):
sudo apt-get install vim
We have pre-installed compilers for the languages you are likely to use in this class. As youwork on your projects, please use the versions of the compilers installed here so ensure that we can replicate your work when grading. If you have questions about the environment and our grading procedures, please feel free to check with us.
Rebuilding the container environment
If you want to start a fresh container, close any container shells you have open (eg. with exit
), go to the <DEV-ENVIRONMENT>
directory and run ./cs1680-run-docker --clean
.
Running ./cs1680-run-docker --clean
will remove any custom packages, that you have installed and make a fresh container from the base image. This is because --clean
removes any existing cs1680 containers on your system.
If you have custom configurations for your packages, (e.g., a .vimrc
file for vim), the configurations are persisted even if you have used --clean
. This is because user-specific configurations are stored in ~
(or its sub-directories), which are located in the <DEV-ENVIRONMENT>/home
directory on your machine.
Running Wireshark
One of the most important features of our container environment is that you can run Wireshark to view and debug your network traffic. Wireshark is a graphical application. In general, running graphical applications inside a container is tricky and requires a certain amount of host support.
Since this container environment issue is new this year, our container supports two methods for using Wireshark. As we work with the container environment, we will see which one works better for our needs.
The primary method requires an X11 server on your host system (which you should have just installed), and provides the most seamless way to run graphical apps from the container. If you encounter issues with the primary method, use the backup method. The backup method creates a virtual desktop that displays Wireshark inside a browser–this does not require any special applications on your host system, but is slower.
Primary method
To run Wireshark, open a terminal to your container and run the following:
$ wireshark
If everything worked, Wireshark should open in a new window. Yay! Proceed to Using Wireshark.
If this doesn’t work, try the backup method. Also feel free to post about your experience on EdStem and we will do our best to help, or come see Nick during office hours.
When you run Wireshark this way, it will stay running in your terminal until you close the application or press Ctrl+C. In the meantime, you can get another shell inside your container by simply running cs1680-run-docker
from another terminal window.
Running in the background: As an alternative, you can run Wireshark as a background process as follows, which redirects all of Wireshark’s output to /dev/null
so it doesn’t clutter your terminal:
$ wireshark 2>&1 > /dev/null & # Redirect stdout/stderr to /dev/null
If Wireshark is crashing for some reason, go back to the previous method and run Wireshark as a foreground process so that you can see the output. If you have issues with Wireshark and ask us for help, we may ask you for this information.
Running Wireshark (Backup method)
Our backup method to run Wireshark leverages Xpra, which is a clever application that provides various means of accessing X11 applications. We will use Xpra to create a virtual desktop that runs inside your browser that displays Wireshark. The interface is a bit cumbersome, and it may have some performance issues, but it /should/ always work.
- To run Wireshark using Xpra, open a terminal inside your container and
cd
to your home directory. From there, run the following script:cs1680-user@9899143429a2:~$ cs1680-scripts/run-wireshark-xpra
You will see a lot of output as Xpra starts up its various components. It’s okay if some messages are errors–Xpra has many features we are not using, and some of these are expected to fail on startup. When it’s ready, you should see a message like the following:
started command 'wireshark --fullscreen' with pid 54
This indicates that Xpra is running and is ready for you to connect to it.
-
Outside the container, open your browser and visit http://localhost:14500. If everything worked, this should open a web application that launches the Wireshark window. Yay!
- On top of Wireshark’s window, you should see a couple of buttons in a grey box at the top left corner of the window. This is a toolbar to control Xpra, which unfortunately blocks some of Wireshark’s controls. You can click and drag the left side of this toolbar (the part with the vertical lines) to move it out of the way.
When you run Wireshark this way, it will stay running in your terminal
until you close the application or press Ctrl+C. In the meantime,
you can get another shell inside your container by simply running
cs1680-run-docker
from another terminal window.
This interface should let you do everything we need with Wireshark. To close wireshark, you can simply interrupt the script in your container terminal by pressing Ctrl+C.
If you are not able to connect to Wireshark this way, please contact us by coming to office hours or posting on EdStem. We are happy to help!
Using wireshark
Now that you have Wireshark running, it’s time to use it to capture traffic!
Packet captures are performed by attaching to one of the system’s network interfaces. In this case, we will be capturing on one of the virtual network interfaces inside the container, so we will only see network traffic from container processes.
Starting a packet capture
To start a capture, select Capture > Options from the menus at the top. This will present a list of the container’s network interfaces. In general, you have two choices:
-
lo
, the loopback interface: This contains traffic to and fromlocalhost
–it lives exclusively inside the container. Your own system has its own loopback network, which is local to your own machine. You should capture on this interface if you are debugging a program that connects tolocalhost
, like your assignments. -
eth0
: This is the container’s link to the outside world (ie, your host system, and the Internet). You should capture on this interface to see traffic that leaves the container.
For now, click on eth0
and click Start.
Filtering out the garbage
Once the capture starts, you will likely see a lot of X11 protocol packets (or websocket packets, if you’re using the backup method). These packets are actually caused by Wireshark itself–they represent the X11 protocol messages to update Wireshark’s GUI. This is because we access the container by communicating with it over the local network that lives on your host system!
Since capturing the X11 traffic is not useful to us (and will use a lot of memory), we can tell Wireshark to ignore these packets using a /capture filter/. To do this:
-
If your capture is still running, stop it by selecting Capture > Stop from the menu bar
- Open a new capture with Capture > Options, and click on
eth0
. At the bottom of the window, enter the following text in the box labeled “Enter a capture filter…”- If you are using Wireshark via the primary method, enter
not tcp port 6000
- If you are using Wireshark via the backup method, enter
not tcp port 14500
- If you are using Wireshark via the primary method, enter
- Click Start to start the capture. You should see little (if any) traffic, which means the X11 traffic is being ignored!
Capturing something useful
We can see Wireshark in action by fetching something from the
Internet. With a capture running on eth0
, open a new terminal in
your container and run:
$ wget -O /dev/null http://cs.brown.edu
This will use the command wget
to fetch the CS department web page
(and dump the results to /dev/null
because we don’t need them).
Look at Wireshark after running this command. You should see a number of TCP packets, and two labeled as HTTP: these are the request to load the webpage, and the response, respectively.
Click on any packet and explore the packet’s header values and contents in the lower pane. This is how you can view the details of any packet, which is useful for exploration and debugging!
If you have reached this step, congratulations! Your development environment is configured and you can now run Wireshark! As we learn more about networks, we will discuss more features of Wireshark and how you can use them.
Looking for something to do next? Once you get started with the IP assignment, run the reference IP node and observe its traffic by capturing on the loopback interface.
Getting help
If you have questions on any stage of configuring the container environment, please do not hesitate to contact the course staff by posting on EdStem, or via email. In addition, if you notice any components that were particularly unclear–or you solve any issues on your own–please let us know we can update the documentation. This is a new component of the course, so we really appreciate your feedback!
Attribution
This setup is a modified version of the setup used by
CSCI0300 and reused with
permission, which is based on Harvard’s CS61.
For wireshark, we have used the setup described in Wireshark Web Container Image.