==================================== ssh and git setup and explorations ==================================== Motivation and plan =================== I consider ssh to be one of the most useful and most ingenious programs ever written. I use it to: * Log in to other hosts, with authentication that is *both* secure and convenient, and full encryption of the connection text. * Authenticate to version control servers like codeberg, gitlab, and others. * Run graphical programs on a remote host using ssh's clever creation of an ad-hoc X server on the remote host which tunnels back to the local host's display. * Forward ports through encrypted tunnels for a variety of services. ssh uses an approach to encryption called *public-key cryptography* at various points in its inner workings to encrypt credentials and traffic. This means that among other things you will be creating a private/public key pair. In addition to its functionality, ssh also has an approach to authentication that I use regularly, the *ssh agent*. This allows you to enter a *passphrase* just *once* on your main display, and that authentication will be passed forward down a chain of ssh connections so you do not have to type passwords anymore. Let us go on a romp through that part of the world of ssh that I use and get ready to use it in our workflows. Our steps will be to (a) create a private/public key pair for ssh; (b) test that logging in to our own laptop; (c) attaching our ssh public key to web services like codeberg and gitlab. Your console, and the ssh setup =============================== A bit of jargon before we begin. We will have the idea of the "first host you log in to". This can be called your console, or your workstation. It is the first computer you log in to, typically the one to which your keyboard and mouse are attached. I will call this host your *console* or your *workstation*. We will test by logging in to your current machine, so make sure you install the ssh server. Example: on debian-based linux distributions you can type something like: .. code-block:: console $ sudo apt install openssh-server Create the public/private keys ------------------------------ Log in to your console, and open up a terminal. .. warning:: do you already have an ssh setup? If so we do not want to mess things up. Try typing: .. code-block:: console $ ls -last ~/.ssh/ and if that directory exists and has in it files that start with ``id_rsa`` or ``id_ed25519`` or something similar, then **stop**. You seem to already have an ssh setup, and you need to decide out if you want to resurrect that old ssh setup or to wipe it out and start from scratch. If you have no ``.ssh`` directory at all, or a ``.ssh`` directory with no public or private keys (those ``id_`` files), then you can continue with what we are doing here. Before we run the command to create the ssh private and public key pair, please understand the idea of a *passphrase*. A good discussion of it is given by the `xkcd webcomic on password strength at https://xkcd.com/936/ `_ Now run: .. code-block:: console $ ssh-keygen and use the default location for the key files, and give it a passphrase that you choose. Using ssh to log in to this host -------------------------------- Now verify that you can log in to the current host - you will have to type your password: .. code-block:: console $ ssh localhost # or sometimes ssh 127.0.0.1 It should ask you for a password and then log you in. Note that you are on the same host you were on before, but you've gone through a layer of *remote login*. Make sure you now log out of that with: .. code-block:: console $ exit so you are back to your plain terminal. This is so important that you might even want to continue typing ``exit`` until the terminal is gone, and then open a new one. Now the crucial step of setting up your ``authorized_keys`` file to let yourself in without a password. You do this by putting your *public* key in the ``~/.ssh/authorized_keys`` file on the remote host. (Note: we are pretending that this computer is also the "remote" host.) .. code-block:: console $ cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys $ # (or this might be cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys) $ chmod 600 ~/.ssh/authorized_keys Now you should be able to this again: .. code-block:: console $ ssh localhost # or sometimes ssh 127.0.0.1 and now it should ask you for your *passphrase* with a graphical pop-up dialog. And of course make sure you exit from all that: .. code-block:: console $ exit $ # you might even want to fully exit the terminal Agent forwarding and X11 forwarding ----------------------------------- In software development you might log in to remote hosts often, and then need to access yet other hosts, in a whole chain of ssh connections. Because of this there are a couple of useful settings for your ``~/.ssh/config`` file. Edit that file and put these two lines at the top: :: ForwardX11 yes ForwardAgent yes and after saving type: .. code-block:: console $ chmod 600 ~/.ssh/config Using ssh to authenticate with remote git servers ================================================= Remote git servers like codeberg, gitlab, github, ... allow you to upload your ssh public key to them, using their web interface. I will use codeberg as an example, but the others all have analogous settings to enter your ssh public key. The details to add your key --------------------------- Log in to codeberg and find the top level user menu (upper right), then pick "settings", and in there pick "SSH/GPG keys". See the following figures: .. _fig-codeberg-personal-menu: .. figure:: codeberg-personal-menu.png :width: 20% The codeberg personal menu - pick the "settings" item. .. _fig-codeberg-: .. figure:: codeberg-user-settings.png :width: 20% Here you pick the "SSH/GPG keys" item. .. _fig-codeberg-ssh-key-management: .. figure:: codeberg-ssh-key-management.png :width: 40% Top level for key management - click the "Add key" button. .. _fig-codeberg-add-ssh-key-before: .. figure:: codeberg-add-ssh-key-before.png :width: 40% Ready for pasting in the ssh public key. .. code-block:: console $ cat .ssh/id_ed25519.pub ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINImmL9El2c8Mm747liAa0f5TCjXXtEXjqyfQCdU65lc markgalassi@dongiovanni $ .. _fig-codeberg-add-ssh-key-after: .. figure:: codeberg-add-ssh-key-after.png :width: 40% After pasting in the ssh public key. Testing if the key has been added well -------------------------------------- Start by cloning my repo. If you have an account on codeberg you can run: .. code-block:: $ git clone ssh://git@codeberg.org/markgalassi/serious-programming-courses.git Or if you have an account on github you can clone a github repo, for example with: .. _fig-github-example: .. figure:: github-example.png The ssh clone URL from github. .. code-block:: console $ git clone git@github.com:githubtraining/hellogitworld.git Creating your own git repo and having a git workflow ==================================================== You will want to create yourself a software repository on codeberg (or gitlab or github or ...) Then you will clone it as we I showed above. When teaching this I then create an on-the-fly codeberg repo, clone it, and demonstrate the workflow of: #. Once per project: create repo and git clone #. Once per creation of a new file: git add #. Frequently, every time you make a change: git commit -a #. Frequently, almost every time you make a change: git push Following the `gitlab docs on git `_ you might think of it this way: One time setup -------------- .. code-block:: console $ git config --global user.name "FirstName LastName" $ git config --global user.email "user@domain.tld" $ git config --global --list One time per project -------------------- If you are creating a new project: .. code-block:: console $ git init . If you are cloning an existing project from somewhere (for example a codeberg repo you have just created): .. code-block:: console $ git clone git@someplace.tld:/path/to/master/reponame $ cd reponame One time when you add new files ------------------------------- .. code-block:: console $ echo "int main() { return 0; }" > trivial.c $ git add trivial.c $ git commit -a # create a useful log message $ git push ## synchronize out to other people's code Of course you should create your programs with your programming editor - in the example above I just created a trivial ``.c`` file with a one-liner in the shell, but you will eventually create real significant files with your editor. Daily work flow --------------- .. code-block:: console $ git pull ## pulls in what other people have been doing # Edit code and save. $ git commit -a # create a useful log message $ git push ## synchronize out to other people's code Taking stock ------------ .. code-block:: console $ git log ## detailed information on what's been happening $ git tag release-1.5 ## reproducibly define a release The minimum you should have in a version control repo ===================================================== license You should have a license. If there is no extremely strong and specific reason to do otherwise, use GPLv3. Both codeberg and gitlab have menus to pick a license and it will put the license file there for you when you choose something like "GNU General Public License (GPL) v3". Naming the file ``LICENSE`` usually works well. README Or ``README.md`` or ``README.rst``. This file needs to show how someone can start from scratch, clone your repo, and use programs in your repo to generate some result. You should have "end-to-end" generation of a plot with simple copy+paste from your README file to the command line. And remember: the commands should be on lines by themselves, so that your users do not have to carefully select - they should simply triple-click to get your line of code. I discuss README files in more detail below. programs not notebooks (for python code) Some people use jupyter notebooks to explore datasets. I very strongly recommend avoiding the habit of jupyter notebooks because then people do not write good programs. But whether you use jupyter notebooks or not, the code you put in your repository should be a python program rather than the notebook, so that it can run reproducibly and produce a result unattended. If you want to use jupyter notebooks, make sure that you put all your code in functions (you should do this anyway), and call those functions from your notebook. More on what makes for a good README file ========================================= Remember the principle that your repo must allow you to *reproducibly* from source files you have created to all your final outputs. This must all be doable with *unattended* command line commands. (There are very few exceptions to this.) For example, let us say that you have a repository with the following programs: calculate_pressure.c A program that applies a physics equation of state (for example the ideal gas law) to calculate pressure of a gas given temperature and volume. It writes the output to a file. It needs to know temperature and volume, so we accept those as command-line parameters. plot_pressure.py A program that uses the matplotlib library to plot pressure as a function of temperature and volume. You have also written a paper (in a file called ``pressure_studies.tex``) describing the work and showing a plot, which will be saved (by ``plot_pressure.py``) in a file called ``p_vs_T_and_V.pdf`` In this case you will have the following files in your repository: ``README.md``, ``LICENSE``, ``calculate_pressure.c``, ``plot_pressure.py``, ``pressure_studies.tex`` Your typical work flow is: #. Edit your ``.c``, ``.py``, and ``.tex`` files. #. Build the C source with: .. code-block:: console gcc -o calculate_pressure calculate_pressure.c -lm #. Run the program with .. code-block:: console ./calculate_pressure 14.1 4.8 > pressure_14.1-3_4.8.out #. Sometimes run a sweep of temperatures and volumes with: .. code-block:: console for temp in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do for vol in 0.5 1 1.5 2 2.5 3 3.5 4 4.5 5; do out_fname=pressure_${temp}_${vol}.out echo "running temperature $temp and volume $vol - output in $out_fname" ./calculate_pressure $temp $vol > $out_fname done done #. Make plots: .. code-block:: console ./plot_pressure.py pressure_*_*.out #. Rebuild the paper: .. code-block:: console pdflatex pressure_studies.tex The README.md file must start with a brief conceptual note and then quickly get into the meat of how to produce results. Here is a sample README.md file which covers this situation: :download:`sample-README.md ` I also show the file here: .. literalinclude:: sample-README.md :language: markdown :caption: sample-README.md - a README.md example that demonstrates the full reproducible pipeline to plots and paper, starting from scratch. link: :download:`sample-README.md` .. note:: Notice how every command that the user should type in to reproduce the results is on a line by itself. This lets the user triple-click and paste that into a terminal directly. .. note:: Notice how the prerequisites were mentioned.