Preface
These guides are not done! Not remotely!
Welcome to CS at Reed! You'll be doing a lot of computer touching during your time here, and we wanted to put together some resources to make this as pleasant an experience as it can possibly be.
There are very many bits and pieces of software that you'll want to pick up and get used to along the way, but the vast majority of these tools can be unintuitive and frustrating. Here, we've collated a lot of words to hopefully make that experience easier.
This site covers a few core topics with copy-pastable snippets and explanations for reference, but if something isn't here, or if you just want some more information on anything, there are some really good resources on the broader internet. If you've got questions about how anything techy works, you might want to consult:
- Julia Evans! She's really good at writing zines.
- Anna Ritz (biology) directs her students to this command line bootcamp for a thorough, interactive guide to day-to-day terminal tasks.
- The Pro Git book provides a good, methodical explanation of Git.
- Zero to Nix describes the Nix package manager, a popular package manager for students here to use that's also installed on all of the Reednet machines.
- man pages :(
As a final introductory note, don't be afraid to ask for help. A lot of computer-touching knowledge is really just experience. The more times you see something, the more likely it'll be to click.
Also, if you have questions, yell at us! Issues pertaining to this guide can be sent to GitHub issues, and, along with more general questions, can be emailed to us.
If you want to help us write guides, let us know! If you're familiar with git, we also accept contributions through the guides repository.
Shell
The shell is how most people use the command line or terminal on their computes. It's a really bare-bones interface to your computer, and it can feel obtuse or archaic at times. Unfortunately, it's also a very powerful tool that's certainly worth taking the time to learn.
The first step to doing so, then, is accessing your shell! Go on to installation.
Installation
You might already have a shell installed.
-
If your computer is running Linux, you almost certainly have a shell called Bash installed on your computer already. See the Linux section for instructions on how to access it.
-
If you're running MacOS, you've already got a shell called
zsh
. See here to learn how to access it. -
If your computer runs Windows, you'll want to install the Windows Subsystem for Linux (WSL). See here.
Once you've got a shell installed, you'll be able to open your terminal and do something like this:
$ echo "Hello, world!"
Hello, world!
Linux
You've probably got an app called "Terminal" installed. Look for it in your launcher. You'll get an interface like this:
You can type and run shell commands at this prompt:
That's all there is here! Go to learn the basics.
MacOS
You've already got a terminal: Press ⌘+SPACE to open Spotlight, and
type terminal
to open the terminal. You'll see something that looks something
like this:
Where other operating systems use a shell called Bash, MacOS uses zsh by default. For our purposes here, these shells work mostly the same way, but parts of this guide (especially the scripting section) refer to Bash instead of zsh. This is unlikely to ever be important, but it's something to keep in mind.
Basics
The shell operates under some fairly foreign principles when compared to the rest of the software you might use on a daily basis. It doesn't work how you expect — Ctrl+C doesn't copy and Ctrl+V doesn't paste, for instance — but it's got plenty of conventions that are carried through into other tools you might end up using, so it's worth taking the time to play around a bit. Try entering this into your shell:
$ echo "Hello!"
Hello!
note
Only type the text after the $
. Then, press enter, and you should see the
same output on your screen as you see in the text above.
The $
here represents your prompt. It might look a little different in
your terminal, but many default prompts end in a $
, so that's the character most
commonly used to represent it in documentation.
echo
simply repeats whatever you give it. Despite what it looks like, there's
actually a lot going on here. Try the next two commands:
$ echo "Hello," "world!"
Hello, world!
$ echo "Hello, world!"
Hello, world!
These two commands are different, but they produce the same result. This comes
down to two very important concepts in the shell that you'll come into contact with
again and again: commands and arguments. In the above, we can say that
you ran the command echo "Hello!"
, but we can also say that the command
you ran was echo
, and the argument was Hello!
. This is the difference
between echo "Hello," "world!"
and echo "Hello, world!"
. The first has just
two arguments, and the second has just one. The command echo
just happens to
display all of its arguments together, putting a space in between them.
By the way, the quotes are strictly optional. All of the commands below have the same output. Try them!
$ echo "Hello," "world!"
Hello, world!
$ echo Hello, "world!"
Hello, world!
$ echo "Hello, world!"
Hello, world!
$ echo Hello, world!
Hello, world!
Even though these are all the same, it's generally considered good practice to quote arguments. To see why this is, try this command, and guess what the output might be:
$ echo Sorry. All the microchips are gone. I got #hungry.
You'll notice that the final #hungry
doesn't display! What's going on here? It
turns out that the shell treats everything in a line after the #
character to be a
comment: It's entirely ignored. You can use this to write out prose descriptions
of what your commands do:
$ echo "Hello" # Print a greeting to the display.
This command has three parts: There's the command itself (echo
), the argument
(Hello
), and the comment (everything after the #
). What if you really want
to echo a #
, though? Quoting will help you here:
$ echo "Sorry. All the microchips are gone. I got #hungry."
Sorry. All the microchips are gone. I got #hungry.
These ideas of arguments, comments, and commands make up very much of the
command line that you'll be interacting with, but you'll be using some more
commands than just echo
. Go on to navigation for some
more useful techniques.
Navigation
You're probably used to files and folders as they show up on your desktop. You can drag them around, put them in other folders, and so forth. As it turns out, the command line gives you access to the exact same thing.
The simplest command that interacts with your files is pwd
. This stands for
"print working directory," and if you run it, you'll see your current working
directory:
$ pwd
/home/user
note
Your output may be different if you're on MacOS, but it should have a similar form.
This is a very important concept: Every shell always has a working
directory and you can always check what it is with pwd
. A working directory
is itself not too strange a concept: If you open up your files app, you'll see
you're looking at some files. You can click on a directory to see the files and
folders in that folder, and keep going however long you'd like. It's the same
thing here. To see everything that's in your current directory, run ls
:
$ ls
Desktop Documents Downloads go Mail Music Pictures Public Templates Videos
note
This is just some possible output. You might have some or none of these folders on your own computer.
Just like in your GUI files app,
you can change your working directory. To do so, use the cd
command:
$ cd Documents
You'll notice that nothing happened. However, run pwd
again and you'll see
you're somewhere else:
$ pwd
/home/<your-username>/Documents
As before, you can run ls
to see all your documents:
$ ls
essay.txt cstar-guide.html shoelaces.pdf
note
Again, this is just example output. Yours is (presumably!) not quite the same.
The so-called parent directory of your working directory is referred to with
..
. This means that after you cd
into Documents
, you can get out just as
easily:
$ pwd
/home/<your-username>/Documents
$ cd ..
$ pwd
/home/<your-username>
$ cd Documents
$ pwd
/home/<your-username>/Documents
ls
is a command you'll be using a lot, so it's worth spending a little bit of
time on it. Just like with echo
, ls
can take arguments. Try using the -l
argument for a long listing:
$ ls -l
-rw-r--r--. 1 user group 0 Jan 24 12:13 essay.txt
-rw-r--r--. 1 user group 45 Jan 28 19:00 cstar-guide.html
-rw-r--r--. 1 user group 100M Jul 22 2024 shoelaces.pdf
note
An argument that begins with a -
is usually called a flag.
Reading the output right-to-left, you'll see that we have the filenames just as
before. Then, we have the time the files were last modified, preceded by their
size. In this example, essay.txt
is entirely empty. cstar-guide.html
takes
up 45 bytes of space, and the M in the 100M
taken by shoelaces.pdf
stands for
megabytes. We'll come back to the rest of the output later, in the section on
file permissions.
The echo
command can actually be used to create files. Try, for instance, the
following:
warning
Make sure you don't already have a file named a.txt
in your current
working directory. This command will overwrite that file!
$ echo "Hello!" > a.txt
This >
is called a redirect, and takes the output of the command (which would
normally just be "Hello!") and writes it to a file instead.
Now, if you run ls
again, you'll see the same output as before, with one
addition:
$ ls
a.txt essay.txt cstar-guide.html shoelaces.pdf
Now that you have that file saved to your disk, the cat
command will let you
see the contents:
$ cat a.txt
Hello!
The greatest sin of the command line environment is that cat
has nothing to do
with actual cats, and instead is an abbreviation of concatenate. You can pass
two or more filenames as arguments to cat
, and you'll be able to see them
both, one after the other:
$ echo "Goodbye!" > b.txt
$ cat a.txt b.txt
Hello!
Goodbye!
Redirections work on every command, cat
included. Try this:
$ cat a.txt b.txt > c.txt
Now, c.txt
has the same content as a.txt
and b.txt
combined, and if you
cat
it, you'll see what you expect:
$ cat c.txt
Hello!
Goodbye!
By the way, you can't just cat
any arbitrary file. Consider, for example,
shoelaces.pdf
. This thing is 100M, and, being a PDF, it probably has all sorts
of information about formatting, page size, and so forth that just isn't
expressed as plain text. It's usually inadvisable to cat
these to your
terminal, unless of course you're redirecting the output to a file.
note
If you want, you can look for a binary file like an image or a PDF with ls
and cd
. Then, cat
it. You'll see a stream of nonsense pour over your
terminal window. Press Ctrl+C to interrupt the command. You
probably won't see your usual prompt anymore. Nevertheless, type reset
and
press Enter. It'll take a couple seconds, but your shell will come
back just as before.
You probably want to get rid of a.txt
, b.txt
, and c.txt
now. You can do
that from the command line as well:
$ rm a.txt b.txt c.txt
Maybe you want to look for any other .txt
files just in case you missed some.
This can be achieved with a glob:
$ ls *.txt
essay.txt
note
Your output might be empty if you have no files that match this string. Try
playing around with other globs. For instance, *a*
matches any file with an
a
in the name.
Now that you know how to create and remove files, it's good to do the same for
directories. The mkdir
command does the first:
$ mkdir test-directory
And rm does the second:
$ rm -r test-directory
Notice the -r
flag. This causes a recursive removal. That is, you're not
just removing the directory you specify (here, that's test-directory
), but
also all the files and folders inside. Be very careful with this command. Unlike
the normal files app, there's no trash can to restore from!
Now that you can make and remove files and folders, you've already mastered about half of the command line. Next up are some niceties in line editing.
Line Editing
As you might have noticed, using the command line can involve repeating yourself
a lot. The shell itself actually helps you avoid most of the manual repetition by
keeping track of your command history. You can see this for yourself:
There's a file in your home directory called .bash_history
on Linux and WSL or
.zsh_history
on MacOS which stores all the commands you've run in a given
terminal session.
Close your terminal window and open a new one. Then, look for this file:
$ ls -h
Because this filename starts with a .
, your history file is actually a
hidden file, and you need the -h flag in ls
to see it. Now, once you
know if you have .bash_history
or .zsh_history
, check what's in it:
$ cat .bash_history # or .zsh_history!
By the way, you almost certainly have some other hidden files in this directory.
Recall that the command for seeing those is ls -h
, but you've already run it.
Press the up arrow to cycle through your previous commands. First you'll see
cat .bash_history
, then ls -h
. The shell will let you get through your entire
history this way! Play around with the command line a little bit. Scroll up,
modify a command, and run it. To modify a command, your normal keyboard
shortcuts won't all work. You can likely use the arrow keys to move your cursor
over the line, but there are some other, more shell-specific shortcuts that you
may want to use.
Shortcut | Action |
---|---|
Ctrl+H | Delete the last character. |
Ctrl+W | Delete the last word. |
Ctrl+A | Go to the start of the line. |
Ctrl+E | Go to the end of the line. |
Ctrl+F | Go to the next character. |
Ctrl+B | Go to the last character. |
Alt+F | Go to the next word. |
Alt+B | Go to the last word. |
↑ | Go to the previous (less recent) item in the history. |
↓ | Go to the next (more recent) item in the history. |
note
Most of these shortcuts are taken from a very old, decrepit piece of software called Emacs. You can see key bindings that look like this pretty much everywhere if you know where to look.
One last convenience: You can use Ctrl+R to search through your command history. You'll see something like this:
(reverse i-search)`':
Typing will match through your history in real time. You can use ↑ and ↓ to look through the matches, and edit the result just like normal.
File Permissions
We'll take another look at the output of ls -l
:
$ ls -l
drwxr-xr-x. 1 user group 451M Jul 22 2024 Documents
-rw-r--r--. 1 user group 0 Jan 24 12:13 essay.txt
-rw-r--r--. 1 user group 45 Jan 28 19:00 cstar-guide.html
-rw-r--r--. 1 user group 100M Jul 22 2024 shoelaces.pdf
When you run this, you likely won't see user
or group
. Instead, you'll
likely see your computer username in that first column and something like it in
the next. These are the names of a user and of a group, respectively. In
all likelihood, these are the names of your user and your group.
The systems we're using — Linux, MacOS, or WSL on Windows — all descend from an operating system called UNIX, which achieved success in part due to its multi-user support. On our personal computers, we don't generally use multi-user features all that frequently or explicitly, but they're baked in to the computer so much that, in fact, every single file is owned by one of many users, and, simultaneously, one of many groups.
If you want to know what your user is called, you can always run whoami
:
$ whoami
atalii
note
This is just my personal username. Yours will be different!
Similarly, groups
can tell you what groups you're a member of:
$ groups
atalii wheel input
note
Again, this is just the output on my machine. You'll probably be a member of some different groups. You don't need to worry about what each one is for right now.
This is the basis for the UNIX permission model: Every file has three sets of permissions. First, the owning user can read the file, write the file, or execute the file. Then, a different set of permissions (one, none, or all of reading, writing, and executing) apply to a user who is not the owner but is in the owning group. Then, there's a third set of permissions for everyone else.
This is actually what you see in the leftmost column of the long listing. The
first letter (above, this is either -
or d
) indicates the kind of file this
is. -
indicates that it's just a normal file, and d
indicates it's a
directory. Then, there are three characters describing what the owning user can
do. The first of these three indicates whether or not the
owner can read the file (r
if they can, -
if they can't), the second (r
or
-
) indicates whether they can write to the file, and the third (x
or -
)
indicates whether or not they can execute this file. We have another group of
the same three that describe permissions for members of the owning group, and a
final group of three that describe permissions for everyone else.
If this feels a bit abstract, we can actually see these permissions working with
a few commands. You can create a file with the touch
command:
$ touch perm-example.txt
note
This command has no output. One of the recurring themes in CLIs is that if
they don't complain, you're probably safe to assume that nothing's gone wrong.
You can run echo $?
to see the exit code of the process, which will
always be 0 if it's successful.
Now, look at how perm-example.txt
has been created:
$ ls -l perm-example.txt
-rw-r--r--. 1 atalii users 0 Feb 6 08:29 perm-example.txt
We can see that in this case, the owning user is atalii
, and the owning group
is users
. atalii
has the rw-
perms associated: They can read from and
write to the file. Anyone in the users group can read the file but do nothing
else, as indicated by r--
. And, with the same r--
repeated, anyone outside
the owning group can only read the file.
Let's take a closer look at what exactly this all means. To be able to read from a file means you're allowed to look at the contents, which, in this case, are empty:
$ cat perm-example.txt
To be able to write to a file means you're able to modify its contents. Try this:
$ echo "I can write to this file!" >> perm-example.txt
note
Notice that we use >>
here instead of >
as we used previously. The
difference is that >
will always overwrite a file, replacing the
contents with whatever it wants. By contrast, >>
appends to the file,
leaving alone whatever was there previously.
Now, if you read it again, you'll see that line of text:
$ cat perm-example.txt
I can write to this file!
Execution may, unfortunately, be a bit more mistifying. We'll explore what it
means for a file to be executable in more detail when we talk about compiling
code or, in this chapter, writing scripts. For now, it's
enough to say that lots of the commands we've been using (cat
, ls
, touch
,
and so forth) are actually files on disk just like everything else. Because we
can run them as commands, then, we know they're executable.
There's one last meaning to "executability" that should concern you, and that's
executability for directories. If you'll recall the example at the beginning, my
documents folder can be read, written, and executed by me, while it can only be
written and executed by anyone else. Here, execution does not actually mean
execution. Instead, it means that a user with executable permissions on the
directory can cd
into it.
This permission system would only be so useful if you couldn't change
permissions. Fortunately, you can use the chmod
command to change permissions.
While there's some more advanced usages we won't cover here, the basics are
good to know. Before we discuss exactly what this command does, try it out:
$ chmod ugo-r perm-example.txt
Now, if you try to read perm-example.txt, you should see an error:
$ cat perm-example.txt
cat: perm-example.txt: Permission denied
If you check perms with ls -l
, you'll get a good idea of what that chmod
command did:
$ ls -l perm-example.txt
--w-------. 1 atalii atalii 0 Feb 6 08:29 perm-example.txt
You'll see that you only have write permissions to the file. You can actually test that if you'd like:
$ echo "test" > perm-example.txt
Remember, you don't have read permissions on this test file, so if you try to
cat
it, you'll get that same error. Instead, you have to give yourself read
permissions:
$ chmod u+r perm-example.txt
$ cat perm-example.txt
test
Hopefully you can start guessing at what the arguments to chmod
do. You can
give or take permissions with +
or -
. These permissions can be r
, w
, or
x
. So, for instance, to give everyone executable permissions, you can run
chmod +x
on a file. If you want only the owning user to get those
executable permissions, however, run chmod u+x
. You can put any combination of
r
, w
, or x
on the right hand side, and any combination of u
, g
, or o
on the left hand side, for the owning user, the owning group, or
other users.
chmod
can be run on a file by the owning user, but it can also be run on
any file by the root user: This is a special system account meant for use in
modifying and administering the system, so it (or somebody logged in as this
root user) can change permissions, read, write, and modify nearly any file.
Let's say that we've decided that perm-example.txt is an incredibly important
file for the functioning of your computer, and we therefore want to make it
accessible only to the specially-privileged root user. This requires two new
commands: chown
, and sudo
.
$ sudo chown root:root perm-example.txt
$ ls -l perm-example.txt
-rw-------. 1 root root 5 Feb 6 09:18 perm-example.txt
sudo
will probably give you a very scary prompt and ask for for your password.
Once you confirm that you are indeed the user you're logged in as, sudo
will
pretend you're root to run the real command, which is chown root:root perm-example.txt
. As the name suggests, this changes ownership of the
file perm-example.txt
to be owned by the root user and the root group, and, as
we can see from the output of ls -l
, only root can read and write.
Once you decide you no longer need this file, you can remove it, but only as the
root user. So, you'll have to use sudo
again:
$ sudo rm perm-example.txt
caution
The note before about being careful with rm goes double for sudo rm. If you make a mistake, you can use this command to really break some things.
With this and some practice, you're well on your way to being comfortable on the command line. Next up are some topics in configuration before covering a small amount about scripting.
Useful Commands
The command line can make it fairly difficult to discover new tools. If you're going to know that anything exists, you have to read about it somewhere. What follows, then, is a list for your reference of a whole bunch of things you may want to refer to.
Unless otherwise noted, these commands work in roughly the same way. They take
some input from a file specified as a command line argument, perform some
operation, and output the result to the console. If you're ever unsure how a
specific command works, try running it with -h
to get a help message. If that
doesn't work, try --help
. If that doesn't work, see the section on getting
help.
Once you're done familiarizing yourself with the essentials, go on to learn some scripting techniques.
Essentials
Command | Description |
---|---|
cd | Change the current working directory. |
ls | List the files in the current working directory. You can also give it a target directory to list from, e.g., ls ./example . |
ls -l | List the files in the current working directory, but with a little more information. |
cat | Concatenate files, or, with one argument, print a file to the console. |
head | Get the first couple lines of a file. |
tail | Get the last couple lines of a file. |
grep <pattern> <file> | Output all lines in file that contain <pattern> |
Some Stuff More
You'll run these less often, but it can still be good to know they exist.
Command | Description |
---|---|
tail -f | Watch a file and output new lines as they're written to it. This can be useful for things like logs that are written to by another process over time. |
wc -l | Count the number of lines in stdin or from a file. |
wc -c | Count the number of characters in stdin or from a file. |
wc -w | Count the number of words in stdin or from a file. |
Some Options
A lot of the above commands are very old (cat
, e.g., descends from 1971), and
therefore has some characteristics you may or may not like. These are really
beyond the scope of the basics here, but if you find yourself curious, there are
some potentially preferable replacements for the old tools if you're looking to
play around a bit:
Command | Replacement |
---|---|
cat | bat |
cd | zoxide |
find | fd |
grep | ripgrep |
ls | eza |
nano | micro |
Scripting
The first and most important rule for writing Bash scripts is that you shouldn't. However, some concepts of Bash scripts matter a lot in most any interaction with the shell. Let's start simple by describing variables:
$ echo $HOME
/home/atalii
$ echo $PATH
"/bin:/sbin:/usr/bin:/usr/local/bin:...
Here, you've asked Bash to give you the values of two variables, $HOME
, and
$PATH
. You might recognize $HOME
as the path of your home directory (it's
what'll show up if you run pwd
in a new terminal), but $PATH
is new: Instead
of being one path, it's actually a :
separated list of paths. You probably see
the string bin
in here quite a bit: This is because PATH
lists the locations
of certain kinds of binary files, or, as you may already know them, executables.
For instance, you can run:
$ which cat
/usr/bin/cat
and you'll see that the cat
command is actually a file located in /usr/bin
.
Check the output of the echo
command above, and you'll likely see that
/usr/bin
is in your $PATH
.
note
Some systems will have cat
at /bin/cat
, or maybe /sbin/cat
. If your
output is slightly different, that's okay. The same principle applies.
Were $PATH
just a static list of paths, it wouldn't be so useful. However, you
can put whatever directory you want in there.
We'll demonstrate this by creating a command of our own. Using a text editor of
your choice, make a file in your home directory named foo.sh
with the
following contents:
#!/usr/bin/env bash
echo foo
The line that begins with a #!
is called a shebang, and tells the
operating system that it can run this script with bash. Now, make it executable:
$ chmod +x home.sh
If this command succeeds (and it should!), you'll be able to run it:
$ ./foo.sh
foo
Note that we're referring to the file by qualifying its path with that of the
current directory. We can't just use foo.sh
without the ./
, otherwise Bash
won't know that this is a file in the current directory. Instead, it'll look
through $PATH
, find nothing named foo.sh
, and complain:
$ foo.sh
bash: foo.sh: command not found
However, we can set $PATH
to include the directory in which foo.sh
lives, and
thereby let Bash run it just like any other command.
$ export PATH="$PATH:$HOME"
$ foo.sh
foo
The first command here is the one that does the work. It sets the $PATH
variable equal to the value of $PATH
followed by a colon, in turn followed by
the value of $HOME
, thus adding $HOME
to list. Now, you can run every
executable in your home directory just like anything else.
warning
Adding your $HOME
directory to $PATH
would make a lot of people angry and
be widely regarded as a bad move. Part of the concern is security: While it
takes elevated permissions to put an executable in /bin
, /usr/bin
, or the
like, you only need your permission to put something in $HOME
: If your user
is compromised, or you download a suspicious file and put it in $HOME
for
later, you might end up running something malicious on accident.
More practically, though, it's just regarded as kind of a messy practice.
Binaries are best off being put in places well-marked for them: e.g., /bin
.
If you don't like this: Don't worry, your changes above only matter in your current shell session: Close your terminal or open a new one and your path will be back to normal.
Regardless, there are some nuances in the command above to examine. Start by creating a dummy variable to experiment with:
$ export FOO="bar"
If you're really enthusiastic about foo, you might even want two of them:
$ echo "$FOO $FOO"
bar bar
This is basically the same kind of concatenation operation we did with $PATH
,
but notice the following doesn't work:
$ echo '$FOO $FOO'
$FOO $FOO
Like with comments, you can use single quotes to avoid interpolating
variables. In double quotes, Bash will read $
as letting it know that the
following characters are the name of a variable: In single quotes, however, $
is just another character.
note
By convention, variables in Bash are written in UPPER_CASE
.
For some reason, let's say you really like $FOO
and want to save it:
$ export MY_FAV_VAR="My favorite variable is: $FOO."
$ echo "$MY_FAV_VAR"
My favorite variable is: bar
Note that the kind of interpolation bash does is very flexible: Wherever double quotes can appear, you can use and manipulate variables.
Scripting in Bash is a very finnicky topic: Variables are really just the tip of
the iceberg. Much like many other programming languages, you can use loops,
functions, and conditional statements. For an example of some more involved
scripting, check out a hidden file in your home directory called ~/.bashrc
:
This is run every time you open your shell, and is usually populated with some
interesting knobs you can turn by editing the file and opening a new shell (or
by sourcing ~/.bashrc
with the .
command).
Ideally, you're now comfortable in the shell and have a good grasp of the kinds of things it can do: If not, don't worry! As always throughout this guide, experience is the best way to learn. Everything will become easier with repetition, practice, and conversations with other computer-touchers. Enjoy!
SSH
SSH, being an initialism for Secure SHell, is a set of programs and protocols that allow a user to access a shell on a remote device.
note
Throughout this guide, we'll use an example machine named Empanada, which is available by email request to CSTAR for Reed students.
As a protocol, SSH is a standardized method for two computers to communicate with each other. One computer, an SSH server, allows clients to authenticate themselves and, once authenticated, run commands on the server. Unless you're setting up a machine, you'll usually interact with this protocol from the perspective of the client, so that's what we'll cover here. The usual SSH flow will look something like below, where I log in to one of the Reed computers from my own laptop:
atali@my-laptop:~$ hostname
my-laptop
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada:~$ hostname
empanada
atali@empanada:~$ exit # or press Ctrl+D
atali@my-laptop:~$ hostname
my-laptop
note
Some things here, like my hostname, are specific to my computer. Others,
like, empanada.reed.edu
, are only accessible on the Reed network. If you'd
like to follow along, send an email to CSTAR to get access to some
machines.
Notice a new command: hostname
tells you the name of the device your shell is
running in. Every machine is given one of these names, and it's helpful to have
a human-recognizable identifier for each computer. Then, the second new
command, ssh
, logs in to empanada.reed.edu
as the user atali
and opens up
a shell. Once we're in the shell, we can see that this machine's hostname is
empanada
, and we run the exit
command to close this shell and get back into
the client. We then confirm that we're there with one last invocation of the
hostname
command.
Setting This up for Yourself
note
We recommend following along to get some practical experience. Contact CSTAR for access to some machines with an SSH server enabled.
Once an SSH server is aware of your existence, there are actually many ways to authenticate to it. You'll probably only come across the two most common ones, however:
- Password-based authentication.
- Key-based authentication.
These both work about as you'd expect. If the server is configured to accept password-based auth (and some other requirements are met), SSH will prompt you for your password:
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada.reed.edu's password:
atali@empanada:~$ # We're logged in!
When you type in your password, nothing will appear on screen. Once you've typed it correctly, pressing enter sends it off to the server to be checked, and, if correct, you'll find yourself logged in.
Despite being simple, password-based authentication is generally not preferred to key-based authentication. This authentication method revolves around a key pair that you generate on your own device. This consists of a public key and a private key — you keep the private key secret, but the server gets your public key. Via some witchcraft, if another party has your public key, they can confirm that you know your private key without ever knowing what that private key precisely is. This is unfortunately a slightly more involved process to set up, but leads to a more convenient and secure SSH login process later.
We'll start by creating a keypair. On your SSH client machine, run ssh-keygen
$ ssh-keygen
Generating public/private ed25519 key pair.
Enter passphrase for "id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_ed25519
Your public key has been saved in id_ed25519.pub
The key fingerprint is:
SHA256:QtXVxq6HOFptCucsULsI2bH3Tp0t4Xj6MO0BeThtfQI tali@thing-in-itself
The key's randomart image is:
+--[ED25519 256]--+
| .. ..o |
| . . + |
| . Eo |
| .. . + o. |
| o.+S.=o=oo .|
| o +.+ =X=+.o |
| . + X=+O.. |
| . +.=* o |
| oo.o |
+----[SHA256]-----+
Fortunately, you only have to make one decision, and that's whether to protect
your private key with a password. In some circumstances, you may prefer this,
but it's also acceptable to leave it blank. Then, it gives you some
information about your key. Pay particularly close attention to the fact that
two new files have been created: id_ed25519
and id_ed25519.pub
.
id_ed25519
is your private key, and id_ed25519
is your public key. This
information is enough to set up key-based SSH authentication:
atali@my-laptop:~$ cat ~/.ssh/id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMPdyjTGPE4WBxR75SJrOHs60flkRqJIyiNr7GNC8OcV atali@my-laptop
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada.reed.edu's password:
atali@empanada:~$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMPdyjTGPE4WBxR75SJrOHs60flkRqJIyiNr7GNC8OcV tali@thing-in-itself' >> ~/.ssh/authorized_keys
atali@empanada:~$ exit
atali@my-laptop:~$ ssh atali@empanada.reed.edu
atali@empanada:~$ # Notice how you haven't been asked for a password: SSH is automatically using your key!
All you're doing is adding your SSH public key to a file of authorized keys, which the SSH server can scan to let you in.
If this is confusing, don't worry. The best way to understand this is to do it. If you want practice, email CSTAR for access to our machines.
Nix Cheatsheet
You can use Nix to install additional software anywhere Nix itself is installed.
-
To search for a package:
$ nix search nixpkgs hello * legacyPackages.x86_64-linux.hello (2.12.1) A program that produces a familiar, friendly greeting
-
To install a package:
$ nix profile install nixpkgs#hello
-
To start a temporary shell without installing a package:
$ nix shell nixpkgs#hello $ hello Hello, world!
-
To list installed packages:
$ nix profile list Index: 0 Flake attribute: legacyPackages.x86_64-linux.hello Original flake URL: flake:nixpkgs Locked flake URL: github:NixOS/nixpkgs/2646b294a146df2781b1ca49092450e8a32814e1 Store paths: /nix/store/z6hzxgy21zimj58xhy06f2f49qgy06gg-hello-2.12.1
-
To uninstall a package by index:
nix profile remove 0 removing 'flake:nixpkgs#legacyPackages.x86_64-linux.hello'
For more information about Nix, you can visit https://zero-to-nix.com.
note
This section is TODO.
Writing Code
This section covers some more mechanical issues with writing code. How is code stored? How is it executed? How do you edit it? These questions are the frustrating, tooling-immersed bretheren of the more interesting questions you'll study in your classes, but, nevertheless, they demand answering. If you've written a good amount of code before, nothing here should be surprising. If you haven't, this can serve as a reference and also a brief explanation on the more mundane steps involved in that thing we call computer programming.
Certain concepts are universal to most programming languages — these concepts are specified further in this chapter — but certain languages demand specific toolchains to run code written in those languages. The language you're using largely depends on the class you're taking. Feel free to follow the links as appropriate or simply explore:
- 121 is taught with Python.
- 221 is taught with C, C++, and Assembly.
- You will encounter other toolchains; e.g., Jupyter notebooks for Deep Learning, JS (or possibly TypeScript soon!) for graphics, and OCaml for Programming Languages. If you have toolchain questions, let us know.
Git
Git is a version control system. This allows you to access multiple versions of the same directory on one disk. Git is immensely powerful, but this page will only cover the basics. If you're looking for a more in-depth reference, Julia Evans wrote a very good introduction, which you can also find in Erica's office.
Getting Started With Git
If you want to use version control on your own machine (e.g. a coding project
or homework), you can create a repository, or a collection of files that
git is able to keep track of and version. There are two relevant ways to
create a repository: Either you're workin with something that somebody's
already made, or you're starting an empty repository. In this first case,
you'll use git clone
, and in the second, you'll use git init.
As an example of the first, you can get the sources for this book with this:
$ git clone https://github.com/reed-cstar/guides
Cloning into 'guides'...
remote: Enumerating objects: 211, done.
remote: Counting objects: 100% (118/118), done.
remote: Compressing objects: 100% (88/88), done.
remote: Total 211 (delta 54), reused 77 (delta 30), pack-reused 93 (from 1)
Receiving objects: 100% (211/211), 632.06 KiB | 11.92 MiB/s, done.
Resolving deltas: 100% (82/82), done.
If you made changes to a repository, and you would like to track and version them, you can make a commit. A commit is the unit by which Git records changes made to some files. Alongside the content of those changes (including creations, deletions, and modifications), it also records the author, the date, and also some optional further metadata regarding the changes.
Before you start committing code, you'll need to tell git who you are.
Git provides a clear interface for this with the config subcommand. Try
the following, replacing the quoted vale Your Name
with your name, and
your.email@example.com
with your email:
$ git config --global user.name 'Your Name'
$ git config --global user.email 'your.email@example.com'
Note the use of the --global
flag: This instructs git to store a default
value for these settings across all repositores. If you leave it out, git
will associate your name and email only with the repository in your current
working directory, and future invocations of git will use this value to
override any global value that may or may not be set.
Now that git is sufficiently configured and you're in a repository, you can make some changes to the files and add them to the staging area with the add subcommand:
$ git add ./path/to/modified/files
note
You'll often see git add .
: If run from the root of your repository,
this'll add all changes to the staging area.
With your changes added, the commit subcommand will allow you to add them to version control:
$ git commit
note
If you aren't currently in in a repository, git commit
will tell you with
the only moderately cryptic error message:
fatal: not a git repository (or any of the parent directories): .git
You'll see that git brings up a text editor wherein you can write a
commit message. Most likely, this'll be a text editor called nano
— git
will run whatever command you specify in the $VISUAL
environment variable.
Regardless, writing some text, saving it, and exiting (via
Ctrl+x in Nano) will be enough to create a commit, and you'll be dropped back in your shell.
Reed CS Resources
You have access to many tools, like your own Reed website, compute resources, and access to Polytopia (located in LIB387). If you have any questions about any of these resources, feel free to email CSTAR: it's harder to look up any Reed resources than anything else in this guide.
Machines
Reed provides some machines for you to use. Some of these can be accessed in person, but others are best accessed over ssh.
note
To get access to all of these machines, email CSTAR.
These machines all run Ubuntu with Nix installed for user package management.
note
Don't know what this means? Don't worry. We'll deal with it later.
Peggy, Patty, and Polly
Peggy is in the corner of Polytopia and runs Ubuntu. You can
also access it from the Reed college Wi-fi over ssh at peggy.reed.edu
.
Polly and Patty are in Jim's office, and can be accessed solely by ssh over
the Reed college Wi-fi at polly.reed.edu
and patty.reed.edu
,
respectively. Patty in particular is useful when you're in need of some extra
compute power.
The Dumplings
Banku, Empanada, Gyoza, and Pierogi are iMacs running Ubuntu available for
use in Polytopia. These are available at banku.reed.edu
,
empanada.reed.edu
, gyoza.reed.edu
, and pierogi.reed.edu
over the Reed
Wi-fi.
Accounts
At Reed's instruction, your username is fixed at account creation to be your Kerberos username. Then, your password is set on Polly and copied out to everywhere else. Don't try to change your password on other machines; always SSH into Polly.1
Graduation
After you graduate, we'll delete your account, including any data that you may have.
Spring class of 2025: We've already deleted your accounts, but your home directories are still around. Send an email if you want me an archive of them.
Fall class of 2025 and onwards: We'll send you your home directories as a tarball via email. Then, we'll delete everything. If you've got any additional last wishes, just send an email.
-
At some point, I (Tali) might make a web app to change your password without SSH'ing. If this happens, I'll link it. ↩
Services
We self-host some services that you can use.
Coder
Coder is a web app that provides access to development environments running in Docker containers. This can be a convenient way to set up an environment for some specialized tasks. It runs physically on Patty, so it's got access to a good amount of compute. In particular, there's a pre-existing template you can use to make a container for CUDA development if you're having issues on your own machine or if you don't have an Nvidia GPU.
More detailed instructions on general use and configuring CUDA will be available here soon. If you have a question or what some guidance right now, send an email.
Authentication
These services are all accessed via an SSO login distinct from your normal Reed credentials. You can test your login (from a device on the Reed network) by logging into Polly over the web. Your username and password for this SSO system is the same as that which you'd use to log in to Polly over SSH.1
-
This isn't strictly true right now. If you change your password, you'll need to ping a CSTAR member (for now, Tali) to get everything to work. I (Tali) will make this automatic soon! ↩
Procedures
note
This page is meant primarily for CSTAR members administering these machines.
Authentication Mechanisms
We are not able to tie into Kerberos for a 'proper' SSO. Instead, we run a combination of two mechanisms, both on Polly, to make sure that everything stays in sync and that users don't need to change their password on 7+ machines.
Authelia
Authelia is our OIDC provider. With minimal extra configuration, it can also run as part of a reverse proxy to provide authentication to web services which don't have OAuth/OIDC support. Authelia has a few other mechanisms available for serving auth, and should be able to integrate with most services.
It runs on Polly and keeps a list of users from a users.yml
file which is
autogenerated from /etc/shadow/
.
warning
Authelia runs as a service called authelia-custom
. Do not use
authelia.service
.
shadow-syncd
shadow-syncd
is the name of a small Python script that runs on Polly and
propagates all changes in /etc/shadow
to the other machines. This keeps SSH
and physical logins consistent without the complication of PAM for LDAP, OIDC,
or anything of the sort.
Adding a New Machine
If the machine is owned by Reed (it should have an asset number and barcode; ask CUS in unclear cases), it needs to have an admin (sudo-capabale) account named for cus and for reedadmin. Additionally, it'll need to have Crowdstrike installed, so it should be running Ubuntu.
To set it up, make sure the MAC address isn't being blocked (i.e., that it has
internet access over Ethernet). If there are problems here, the issue should be
brought to CUS. Install Ubuntu and set up the polytopia
admin account. Email
CUS to ask them to set up their admin accounts and Crowdstrike. Then, add it to
the inventory.yaml
file and run any applicable Ansible playbooks.
If, on the other hand, the machine is not Reed-owned (e.g., Quail), CUS asks
us to have it set up individually on Netreg. That is, you're meant to set it up
in netreg as your own individual device. Then, you should install either NixOS
or Ubuntu,1 set up the polytopia
admin account, and get it set up with Ansible
as per usual.
Updating the Machines
Major version updates will happen during school breaks.
-
The account mechanisms as of now rely on a writable
/etc
. In particular, since Polly tries to copy its shadow into other machines, a NixOS machine meant to be accessed by all CSTAR-registered users will require some special treatment. My (Tali's) suggestion is to reserve personal machines for servers that don't require SSH access by individuals, where we can get away just with a Polytopia admin account. ↩