|
| 1 | +--- |
| 2 | +layout: page |
| 3 | +title: Setting up SSH for GitHub in JupyterHub |
| 4 | +permalink: /instructions/link/ |
| 5 | +--- |
| 6 | + |
| 7 | +# Setting up SSH for GitHub in JupyterHub |
| 8 | + |
| 9 | +This page walks you through generating an SSH key inside JupyterHub and adding it to your GitHub account. This setup allows you to push and pull without typing a Personal Access Token each time. |
| 10 | + |
| 11 | +> **Prereq:** Open a **Jupyter Notebook**, paste the code below into a cell, and click the **Run/Play ▶️** button to execute it. |
| 12 | +
|
| 13 | +--- |
| 14 | + |
| 15 | +## 1) Run this one‑cell setup script in a Notebook |
| 16 | + |
| 17 | +Paste the entire block into a new cell and run it. It will: |
| 18 | + |
| 19 | +* prompt for your **GitHub username/email** (used for commit identity), |
| 20 | +* create an **Ed25519 SSH keypair** at `~/.ssh/github` and a matching SSH config entry for `github.com`, |
| 21 | +* start `ssh-agent`, add your key, and |
| 22 | +* add `github.com` to `~/.ssh/known_hosts` to avoid first‑connect prompts. |
| 23 | + |
| 24 | +```python |
| 25 | +import os, subprocess, textwrap |
| 26 | + |
| 27 | +def add_github_to_known_hosts(): |
| 28 | + ssh_dir = os.path.expanduser("~/.ssh") |
| 29 | + known_hosts = os.path.join(ssh_dir, "known_hosts") |
| 30 | + os.makedirs(ssh_dir, exist_ok=True) |
| 31 | + out = subprocess.run( |
| 32 | + ["ssh-keyscan", "-t", "rsa,ed25519", "github.com"], |
| 33 | + capture_output=True, text=True, check=True |
| 34 | + ).stdout.strip() |
| 35 | + with open(known_hosts, "a") as fh: |
| 36 | + if out: |
| 37 | + fh.write(out + "\n") |
| 38 | + print("github.com added to known_hosts") |
| 39 | + |
| 40 | + |
| 41 | +def configure(): |
| 42 | + username = input("GitHub username: ") |
| 43 | + email = input("GitHub email: ") |
| 44 | + |
| 45 | + subprocess.run(["git", "config", "--global", "user.name", username], check=True) |
| 46 | + subprocess.run(["git", "config", "--global", "user.email", email], check=True) |
| 47 | + |
| 48 | + ssh_dir = os.path.expanduser("~/.ssh") |
| 49 | + os.makedirs(ssh_dir, exist_ok=True) |
| 50 | + key_path = os.path.join(ssh_dir, "github") # ~/.ssh/github and github.pub |
| 51 | + |
| 52 | + # Create Ed25519 keypair (no passphrase for this ephemeral VM) |
| 53 | + subprocess.run(["ssh-keygen", "-t", "ed25519", "-f", key_path, "-N", ""], check=True) |
| 54 | + |
| 55 | + # Minimal SSH config entry |
| 56 | + cfg_path = os.path.join(ssh_dir, "config") |
| 57 | + block = textwrap.dedent(f"""\ |
| 58 | + Host github.com |
| 59 | + HostName github.com |
| 60 | + User git |
| 61 | + IdentityFile {key_path} |
| 62 | + """) |
| 63 | + with open(cfg_path, "a") as fh: |
| 64 | + fh.write(block) |
| 65 | + |
| 66 | + # Start agent and add key |
| 67 | + subprocess.run(f'eval "$(ssh-agent -s)" && ssh-add {key_path}', shell=True, check=True) |
| 68 | + |
| 69 | + add_github_to_known_hosts() |
| 70 | + |
| 71 | + with open(key_path + ".pub") as fh: |
| 72 | + pub = fh.read().strip() |
| 73 | + print("\nPublic key — copy to GitHub → Settings → SSH keys:\n") |
| 74 | + print(pub, "\n") |
| 75 | + |
| 76 | +configure() |
| 77 | +``` |
| 78 | + |
| 79 | +When the cell finishes, the **last lines of output** show your **public key** (starts with `ssh-ed25519`). **Copy** that entire line. |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +## 2) Add the key to GitHub |
| 84 | + |
| 85 | +1. In a new browser tab, go to **GitHub**. |
| 86 | +2. Click your **profile picture (top‑right)** → **Settings**. |
| 87 | +3. In the left menu, click **SSH and GPG keys** → **New SSH key**. |
| 88 | +4. Title: *JupyterHub Key* (or similar). |
| 89 | +5. Paste the **public key** you copied from the Notebook into the **Key** box. |
| 90 | +6. Click **Add SSH key**. |
| 91 | + |
| 92 | +--- |
| 93 | + |
| 94 | +## 3) Test the connection |
| 95 | + |
| 96 | +Back in JupyterHub, open a **Terminal** and run: |
| 97 | + |
| 98 | +```bash |
| 99 | +ssh -T git@github.com |
| 100 | +``` |
| 101 | + |
| 102 | +You should see: |
| 103 | + |
| 104 | +``` |
| 105 | +Hi <your-username>! You've successfully authenticated, but GitHub does not provide shell access. |
| 106 | +``` |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## 4) Ensure your repo uses SSH (not HTTPS) |
| 111 | + |
| 112 | +When you clone or set your remote, **always copy the SSH link** from GitHub, not the HTTPS link. |
| 113 | + |
| 114 | +### How to get the SSH link |
| 115 | + |
| 116 | +1. Go to your repository page on GitHub. |
| 117 | +2. Click the green **Code** button. |
| 118 | +3. In the pop‑up, choose the **SSH** tab. |
| 119 | +4. Copy the URL (looks like `git@github.com:ORG/REPO.git`). |
| 120 | + |
| 121 | + * ⚠️ Do **not** copy the HTTPS link (`https://github.com/...`), or GitHub will keep asking for a Personal Access Token. |
| 122 | + |
| 123 | +Example: |
| 124 | + |
| 125 | +``` |
| 126 | +git@github.com:CU-ESIIL/home.git |
| 127 | +``` |
| 128 | + |
| 129 | +### Check your remote inside JupyterHub |
| 130 | + |
| 131 | +Open a Terminal in JupyterHub and run: |
| 132 | + |
| 133 | +```bash |
| 134 | +git remote -v |
| 135 | +``` |
| 136 | + |
| 137 | +If you see `https://...`, change it to SSH: |
| 138 | + |
| 139 | +```bash |
| 140 | +git remote set-url origin git@github.com:<org-or-user>/<repo>.git |
| 141 | +``` |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## 5) Notes & troubleshooting |
| 146 | + |
| 147 | +* The script adds an entry to `~/.ssh/config` so `github.com` will automatically use the new key at `~/.ssh/github`. |
| 148 | +* Some Hubs reset the agent between sessions. If you later see `Permission denied (publickey)`, re‑run: |
| 149 | + |
| 150 | +```bash |
| 151 | +eval "$(ssh-agent -s)" && ssh-add ~/.ssh/github |
| 152 | +``` |
| 153 | + |
| 154 | +* If `ssh -T git@github.com` hangs on first use, ensure `github.com` is in `known_hosts` (the script already does this) or run: |
| 155 | + |
| 156 | +```bash |
| 157 | +ssh-keyscan -t rsa,ed25519 github.com >> ~/.ssh/known_hosts |
| 158 | +``` |
| 159 | + |
| 160 | +You’re now ready to use the **Git widget** page to **Pull → Stage → Commit → Push** without PAT prompts. |
0 commit comments