<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Jupyter Blog - Min RK</title><link href="https://jasongrout.github.io/medium-archive/pelican/" rel="alternate"/><link href="https://jasongrout.github.io/medium-archive/pelican/feeds/author-min-rk.atom.xml" rel="self"/><id>https://jasongrout.github.io/medium-archive/pelican/</id><updated>2026-03-02T23:33:00+00:00</updated><subtitle>The Project Jupyter blog: news, releases, and community stories, archived from blog.jupyter.org.</subtitle><entry><title>mybinder.org adding request Origin to events archive</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2026/mybinder-org-adding-request-origin-to-events-archive/" rel="alternate"/><published>2026-02-02T16:27:00+00:00</published><updated>2026-03-02T23:33:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2026-02-02:/medium-archive/pelican/posts/2026/mybinder-org-adding-request-origin-to-events-archive/</id><summary type="html">&lt;p&gt;In February, the mybinder.org service will start tracking the “origin” of requests that launch a Binder session. The website that created a…&lt;/p&gt;
</summary><content type="html">&lt;figure&gt;
&lt;img alt="The binder logo" src="https://jasongrout.github.io/medium-archive/pelican/posts/2026/mybinder-org-adding-request-origin-to-events-archive/images/001-1_gICbzIC3Qbq6ireueH1FYA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Binder logo&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In February, the &lt;a href="http://mybinder.org"&gt;mybinder.org&lt;/a&gt; service will start tracking the “origin” of requests that launch a Binder session. The website that created a request to &lt;a href="http://mybinder.org"&gt;mybinder.org&lt;/a&gt; will be visible in the Binder analytics archive. This will help us monitor which websites are embedding Binder sessions for impact or abuse reports. It shouldn’t have any implications for private information but we are sharing here for transparency. The only cases where new information will be recorded is websites which embed mybinder.org kernels via tools like &lt;a href="https://thebe.readthedocs.io"&gt;Thebe&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When folks use &lt;a href="https://mybinder.org"&gt;mybinder.org&lt;/a&gt; to run sessions, each launch is added to a public &lt;a href="https://archive.analytics.mybinder.org"&gt;events archive&lt;/a&gt;. That means that every time anyone launches a session on mybinder.org, the repo and version used and the time of the launch is &lt;strong&gt;public information&lt;/strong&gt;. There is no such thing as a private launch on mybinder.org (this is part of why we don’t support private repos). No information about the user &lt;em&gt;requesting the build&lt;/em&gt; is recorded (ip address, user agent, browser info, etc.), though some of that is sent to Plausible and aggregated and anonymized for &lt;a href="https://plausible.io/mybinder.org"&gt;analytics&lt;/a&gt;. Notably, Plausible only covers regular browser visits, not launches via the API.&lt;/p&gt;
&lt;p&gt;There are 3 main ways that people launch sessions on mybinder.org:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;(the vast majority) — visiting mybinder.org in a browser&lt;/li&gt;
&lt;li&gt;embedding &lt;em&gt;kernels&lt;/em&gt; from mybinder.org on another website to make e.g. interactive documentation with a tool like &lt;a href="https://thebe.readthedocs.io"&gt;Thebe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;‘headless’ launches via scripts&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Up to now, these events are indistinguishable in the events archive. We’d like to start distinguishing these cases in the events archive to better classify how mybinder.org is used, but that means recording new information we didn’t track before, so we want to communicate clearly about it ahead of time.&lt;/p&gt;
&lt;p&gt;We are planning to add the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Origin"&gt;&lt;strong&gt;Origin&lt;/strong&gt;&lt;/a&gt; of the launch request to the public events archive at the end of February. This still doesn’t track anything about the &lt;em&gt;&lt;strong&gt;user&lt;/strong&gt;&lt;/em&gt; (no ip address, user agent, etc.), but rather the host of the page where the launch request originated. For the vast majority of cases, this is not new information, as the origin is e.g. &lt;code&gt;gesis.mybinder.org&lt;/code&gt; (which we already track confusingly as ‘origin’, though this is not the request Origin, it is the &lt;em&gt;federation member&lt;/em&gt; where the launch event takes place). Notably, case 1 still includes any regular browser links to &lt;code&gt;mybinder.org/v1/gh/repo/name/HEAD&lt;/code&gt;. We don’t record where those come from (that would be Referer).&lt;/p&gt;
&lt;p&gt;Where this &lt;em&gt;changes&lt;/em&gt; is cross-origin requests (case 2), that is, pages which &lt;em&gt;embed&lt;/em&gt; kernels from mybinder.org, such as documentation websites that use mybinder.org to embed kernels from mybinder.org via tools like Thebe to make executable documentation (which is rad!). These requests will now include the Origin of the request, which would be the hostname (e.g. &lt;code&gt;https://course.spacy.io&lt;/code&gt; ), not the full page.&lt;/p&gt;
&lt;p&gt;Script launches (case 3) typically lack this information, so will usually have &lt;code&gt;null&lt;/code&gt; data in the new field. But of course, scripts can do whatever they want, so this isn’t guaranteed to be accurate for malicious or deliberately spoofing scripts.&lt;/p&gt;
&lt;p&gt;If you want to have a private, not-recorded use of Binder, you must run your own BinderHub instance. If you want to suggest that we &lt;em&gt;not&lt;/em&gt; do this, or do it differently, please let us know in the &lt;a href="https://github.com/jupyterhub/binderhub/pull/2053"&gt;proposal discussion&lt;/a&gt;. You can also check the &lt;a href="https://mybinder.readthedocs.io/en/latest/about/user-guidelines.html#how-we-ensure-user-privacy"&gt;privacy policy&lt;/a&gt; for more information. We plan to apply the change at the end of February, 2026.&lt;/p&gt;
</content><category term="Binder"/></entry><entry><title>Mybinder.org reducing capacity</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/mybinder-org-reducing-capacity/" rel="alternate"/><published>2023-04-25T07:28:00+00:00</published><updated>2023-04-27T20:16:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2023-04-25:/medium-archive/pelican/posts/2023/mybinder-org-reducing-capacity/</id><summary type="html">&lt;p&gt;Funding is running out for the largest member of mybinder.org, which will be shutdown this week, reducing overall capacity&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Funding is running out for the largest member of mybinder.org, which has been sponsored by the Google Open Source Program Office. gke.mybinder.org will shut down and there will be a reduction in capacity at the end of April, 2023 (end of this week).&lt;/p&gt;
&lt;p&gt;Key points:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our funds on &lt;code&gt;gke.mybinder.org&lt;/code&gt;, the biggest part of the federation, will run out in April. Google has decided not to provide more credits.&lt;/li&gt;
&lt;li&gt;Traffic previously sent to GKE will mostly be delivered to OVHcloud, which has less capacity. We are also working to deploy new federation members to spread the load.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Service reductions to expect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;This may come with an increase in errors and failures, as GKE has been the largest and most stable federation member&lt;/li&gt;
&lt;li&gt;We are currently looking at reducing the total capacity of Binder by 60%, so you may get rate limit or capacity errors&lt;/li&gt;
&lt;li&gt;We will start more aggressively deleting older cached builds, as storage costs have increased.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We are still working out the details of the shutdown and deploying new federation members, but mybinder.org users should expect disruption to the mybinder.org service and to see an increase in messages that the service is at capacity in the short term.&lt;/p&gt;
&lt;p&gt;The plan:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Thursday: gke.mybinder.org will be removed from the federation (user sessions not disrupted, but no new sessions will start there)&lt;/li&gt;
&lt;li&gt;Friday: gke.mybinder.org will be shutdown, and mybinder.org migrated to a smaller cluster&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can help us keep mybinder.org running by &lt;a href="https://numfocus.salsalabs.org/donate-to-binder"&gt;donating to Binder&lt;/a&gt; or encouraging your organization to &lt;a href="https://mybinder.readthedocs.io/en/latest/about/federation.html"&gt;join the BinderHub Federation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are grateful to the Google Open Source Program for their funding thus far, and appreciate the continued support of continuing federation members, OVHCloud and GESIS, which enable us to keep mybinder.org going without shutting down completely when one funding source goes away.&lt;/p&gt;
</content><category term="Binder"/></entry><entry><title>Updating defaults on mybinder.org</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2023/updating-defaults-on-mybinder-org/" rel="alternate"/><published>2023-03-09T08:31:00+00:00</published><updated>2023-03-10T15:53:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2023-03-09:/medium-archive/pelican/posts/2023/updating-defaults-on-mybinder-org/</id><summary type="html">&lt;p&gt;This week, we are deploying some updates to repo2docker, the image-building tool that underpins mybinder.org. This may affect your repos…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2023/updating-defaults-on-mybinder-org/images/001-1_0dd02RZZwHXDo-Fr3tdB5w.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;This week, we are deploying some updates to &lt;a href="http://repo2docker.readthedocs.io"&gt;repo2docker&lt;/a&gt;, the image-building tool that underpins &lt;a href="https://mybinder.org/"&gt;mybinder.org&lt;/a&gt;. This may affect your repos. Please &lt;a href="https://discourse.jupyter.org/c/binder/12"&gt;get in touch&lt;/a&gt; if you have problems! Hopefully this will make things better, more stable, and &lt;em&gt;less&lt;/em&gt; surprising overall, but change always breaks &lt;em&gt;something&lt;/em&gt;, however hard we work to keep things working.&lt;/p&gt;
&lt;p&gt;To highlight the changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.10 is now the default Python version&lt;/li&gt;
&lt;li&gt;Python 3.11 is now supported, and new versions of Python should be supported more quickly&lt;/li&gt;
&lt;li&gt;If a repo requests an old version of Python (≤ 3.6), it will be installed in a separate environment, as we’ve been doing with Python 2.7 for a long time.&lt;/li&gt;
&lt;li&gt;The server implementation is switched from &lt;code&gt;jupyter-notebook&lt;/code&gt; to &lt;code&gt;jupyter-server&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More detail on each change, the reasons behind them, and their consequences below.&lt;/p&gt;
&lt;h2 id="python-311-and-separate-envs-for-legacy-python-support"&gt;Python 3.11 and separate envs for legacy Python support&lt;/h2&gt;
&lt;p&gt;When building an image, repo2docker installs packages in one or two environments — the &lt;strong&gt;kernel&lt;/strong&gt; environment where your code runs, and the &lt;strong&gt;server&lt;/strong&gt; environment, which runs the Jupyter server. If you’re using a reasonably supported version of Python, these are actually the same environment. This is the vast majority of repositories on mybinder.org. In order to function properly, we have certain requirements of what goes in the ‘server’ environment. Eventually, those requirements start making it difficult to setup the server environment with an outdated version of Python, as packages march ahead with their minimum required Python (The widely adopted &lt;a href="https://numpy.org/neps/nep-0029-deprecation_policy.html#support-table"&gt;NEP 29&lt;/a&gt; suggested dropping Python 3.7 support in December, 2021). Our solution for this has been to separate the kernel environment from the server environment, so they don’t create a conflict. Until recently, only Python 2 was handled this way (most packages dropped Python 2 support years ago). As part of adding support for Python 3.11, we have expanded this behavior to include Python ≤3.6, so if you are using a ‘legacy Python’, currently defined to be Python ≤3.6, your requested packages will be installed in the kernel environment instead of the server environment. This &lt;em&gt;should&lt;/em&gt; only have a noticeable effect if you are installing something with server extension components , which includes anything modifying the JupyterLab or notebook interfaces— those will not be available in the server environment. To address this, you will have to upgrade Python &lt;em&gt;or&lt;/em&gt; handle the server extension installation in &lt;code&gt;postBuild&lt;/code&gt; . &lt;a href="https://github.com/jupyterhub/repo2docker/pull/1239"&gt;PR adding Python 3.11 and adding Python ≤3.6 to legacy Python versions&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="default-python-is-now-310"&gt;Default Python is now 3.10&lt;/h2&gt;
&lt;p&gt;The default Python for repo2docker and thereby mybinder.org is now Python 3.10, upgraded from 3.7. 3.10 will be used when a repository does not specify which version of Python to use. Following Python’s own annual release cadence, we plan to update this more regularly in the future, lagging behind Python itself by a version or two. This has no effect on your repos if they specify a Python version, which is why we &lt;a href="https://repo2docker.readthedocs.io/en/latest/howto/languages.html#specifying-a-version-of-python"&gt;&lt;strong&gt;encourage you to specify your Python version&lt;/strong&gt;&lt;/a&gt;! If you don’t, then your Python version &lt;em&gt;will&lt;/em&gt; change over time, probably about once a year. &lt;a href="https://github.com/jupyterhub/repo2docker/pull/1219"&gt;PR switching the default Python&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="now-launching-jupyter-server"&gt;Now launching Jupyter Server&lt;/h2&gt;
&lt;p&gt;The default UI for mybinder.org has been JupyterLab for some time, with the classic notebook still available if you specify a url of &lt;code&gt;/tree/&lt;/code&gt;or &lt;code&gt;/notebooks/mynb.ipynb&lt;/code&gt;. When we rolled out that change, we kept the default &lt;em&gt;server&lt;/em&gt; as the &lt;code&gt;jupyter-notebook&lt;/code&gt; server, for improved compatibility with existing server extensions. As the newer Jupyter Server has taken over, the situation is becoming reversed — using the older &lt;code&gt;notebook&lt;/code&gt; server is now the &lt;em&gt;cause&lt;/em&gt; of compatibility issues with server extensions, so we are switching the server to launch &lt;code&gt;jupyter-server&lt;/code&gt; instead of &lt;code&gt;jupyter-notebook&lt;/code&gt;. The classic UI is still available at the same URLs, thanks to &lt;a href="https://nbclassic.readthedocs.io"&gt;nbclassic&lt;/a&gt;. This &lt;em&gt;should&lt;/em&gt; have no visible effects, as &lt;code&gt;jupyter-server&lt;/code&gt; and &lt;code&gt;nbclassic&lt;/code&gt; make a great deal of effort to keep classic notebook extensions working, but some extensions may need an update for jupyter-server compatibility if they haven’t been updated already. Because of where the server is specified, there is no supported way for images to express a preference for the deprecated &lt;code&gt;notebook&lt;/code&gt; server (&lt;a href="https://github.com/jupyterhub/binderhub/pull/1635"&gt;PR switching the default server command&lt;/a&gt;.&lt;/p&gt;
</content><category term="Binder"/><category term="Docker"/><category term="JupyterHub"/><category term="reproducibility"/></entry><entry><title>Please don’t disable authentication in Jupyter servers</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2022/please-dont-disable-authentication-in-jupyter-servers/" rel="alternate"/><published>2022-04-05T09:50:00+00:00</published><updated>2022-04-05T09:50:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2022-04-05:/medium-archive/pelican/posts/2022/please-dont-disable-authentication-in-jupyter-servers/</id><summary type="html">&lt;p&gt;We are aware of ransomware attacks specifically targeting Jupyter servers.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are aware of ransomware attacks &lt;a href="https://blog.aquasec.com/python-ransomware-jupyter-notebook"&gt;specifically targeting Jupyter servers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First off: we have no evidence of a vulnerability in Jupyter associated with the attack. The attacks appear to focus on Jupyter servers &lt;em&gt;with authentication disabled.&lt;/em&gt; That’s what this post is about: &lt;strong&gt;please don’t do that&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Jupyter servers are authenticated with a random token &lt;em&gt;by default,&lt;/em&gt; so you should be fine with the defaults. That default random token is regenerated on each server launch. If that random token is &lt;em&gt;inconvenient&lt;/em&gt; for you (there are plenty of situations where this is the case), &lt;strong&gt;the answer is not to disable auth&lt;/strong&gt;! More on that below.&lt;/p&gt;
&lt;p&gt;We wrote about this &lt;a href="/posts/2017/public-notebooks-and-security/"&gt;before&lt;/a&gt;, but it bears repeating:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Jupyter exposes arbitrary code execution and a shell. If folks gain access, they can do anything you can do. You aren’t just protecting your notebooks, you are protecting your &lt;em&gt;whole computer&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;People can find your notebook server&lt;/strong&gt; if it’s accessible on the Internet. If they locate your server and it has auth disabled, that’s game over, and appears to be what happened in the above ransomware event. Jupyter’s popular enough (yay) that folks are always looking (boo).&lt;/li&gt;
&lt;li&gt;If the generated token pattern is inconvenient for how you work, you can set a persistent token or password in configuration instead of retrieving the generated token on each server launch. Save this token or password in your password manager for safe keeping, and accessing your server will be just as convenient as auth being disabled.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you have an interactive prompt, you can run &lt;code&gt;jupyter server password&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jupyter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;
&lt;span class="n"&gt;Enter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;🔐&lt;/span&gt;
&lt;span class="n"&gt;Verify&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;🔐&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="n"&gt;JupyterPasswordApp&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Wrote&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hashed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;~/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jupyter&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;jupyter_server_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which generates and stores config that looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
  &amp;quot;ServerApp&amp;quot;: {
    &amp;quot;password&amp;quot;: &amp;quot;argon2:$argon2id$v=19$m=10240,t=10,p=8$aFE/DuLj//6oGF2PHWy2DQ$eXHn6AbJe8Lryl4z9oGvMtZhX7iEdt41m+mhvyDvw88&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a &lt;em&gt;hashed, salted&lt;/em&gt; form of your password. We can’t get the password out of this, but we can use it to check if you typed it correctly on the login page. If an interactive prompt isn’t available to you (e.g. a cloud vm that starts the notebook server automatically), you can run this command on any machine, and copy the resulting config file to the destination machine as part of setup.&lt;/p&gt;
&lt;p&gt;If it works better for you, you can also generate &lt;em&gt;just&lt;/em&gt; the hashed password:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;jupyter_server.auth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;
&lt;span class="n"&gt;hashed_password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;passwd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;my-great-passphrase&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# just the &amp;quot;argon2:...&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and get that into your config files, as best suits your needs. For example, in &lt;code&gt;~/.jupyter/jupyter_server_config.py&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c.ServerApp.password = &amp;quot;argon2:...&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or the same config in &lt;code&gt;~/.jupyter/jupyter_server_config.json&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;{
  &amp;quot;ServerApp&amp;quot;: {
    &amp;quot;password&amp;quot;: &amp;quot;argon2:...&amp;quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Read more details in &lt;a href="/posts/2017/public-notebooks-and-security/"&gt;our earlier post&lt;/a&gt;, and &lt;a href="https://jupyter-server.readthedocs.io/en/latest/operators/security.html"&gt;security documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please contact security@ipython.org if you have a vulnerability to report.&lt;/p&gt;
</content><category term="security"/></entry><entry><title>JupyterHub 2.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/jupyterhub-2-0/" rel="alternate"/><published>2021-12-01T14:15:00+00:00</published><updated>2021-12-01T14:15:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2021-12-01:/medium-archive/pelican/posts/2021/jupyterhub-2-0/</id><summary type="html">&lt;p&gt;Hubs and roles and scopes, oh my!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Hubs and roles and scopes, oh my!&lt;/p&gt;
&lt;p&gt;We’ve just published JupyterHub 2.0, our biggest release in a while.&lt;/p&gt;
&lt;h2 id="roles-and-scopes-rbac"&gt;Roles and scopes (RBAC)&lt;/h2&gt;
&lt;p&gt;Thanks to the work of Omar Richardson and Ivana Huskova and more contributors along the way, JupyterHub now has fine-grained control of permissions, via &lt;strong&gt;scopes&lt;/strong&gt; (a &lt;strong&gt;role&lt;/strong&gt; is a named collection of scopes).&lt;/p&gt;
&lt;p&gt;This makes JupyterHub more secure because you no longer need to give every user or service full admin permissions if they need to take one elevated action (such as a cull-idle service). Now, you can give users and services exactly the permissions they actually need. &lt;code&gt;admin&lt;/code&gt; still exists, but nothing should need to be an admin anymore. Fine-grained scopes also allow new configurations that weren’t previously available, such as granting instructors permission to access their students’ servers, which is useful with JupyterLab’s new real-time collaboration features, without needing to grant them full admin permissions, or even access to servers of users not in their specific class.&lt;/p&gt;
&lt;p&gt;The significant change in how permissions work means that upgrading to 2.0 requires some downtime. You will need to shut down all services and single-user servers across the upgrade, and make sure you upgrade JupyterHub to 2.0 in both the user environment and the Hub environment at the same time.&lt;/p&gt;
&lt;p&gt;All tokens issued prior to the 2.0 upgrade will be revoked.&lt;/p&gt;
&lt;p&gt;As always, &lt;a href="https://jupyterhub.readthedocs.io/en/2.0.0/admin/upgrading.html"&gt;&lt;strong&gt;back up your database&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;before upgrading&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Read more about roles and scopes &lt;a href="https://jupyterhub.readthedocs.io/en/2.0.0/rbac/"&gt;in the docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="jupyterlab-by-default"&gt;JupyterLab by default&lt;/h2&gt;
&lt;p&gt;2.0 also marks JupyterHub’s transition to using JupyterLab as the default UI. You can stick with the classic notebook server with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c.Spawner.environment = {
    &amp;quot;JUPYTERHUB_SINGLEUSER_APP&amp;quot;: &amp;quot;notebook.notebookapp.NotebookApp&amp;quot;,
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or try &lt;strong&gt;RetroLab&lt;/strong&gt;, a classic-style UI built on the newer Jupyter Server:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;c.Spawner.default_url = &amp;quot;/retro/&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(assuming you’ve installed it in the user environment, with e.g. &lt;code&gt;pip install retrolab&lt;/code&gt;).&lt;/p&gt;
&lt;h2 id="and-more"&gt;And more&lt;/h2&gt;
&lt;p&gt;There are various other changes and improvements in 2.0, including a new admin UI, API pagination, and no longer relying on any command-line arguments for single-user servers. Check out the &lt;a href="https://jupyterhub.readthedocs.io/en/2.0.0/changelog.html"&gt;changelog&lt;/a&gt; for more details.&lt;/p&gt;
&lt;p&gt;We encourage you to take 2.0 for a spin, try out the roles and scopes, and &lt;a href="https://discourse.jupyter.org/c/jupyterhub/10"&gt;let us know&lt;/a&gt; how it goes.&lt;/p&gt;
&lt;p&gt;Thanks to all the contributors who helped us with 2.0, especially prerelease testers!&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>IPython Parallel in 2021</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2021/ipython-parallel-in-2021/" rel="alternate"/><published>2021-11-30T07:55:00+00:00</published><updated>2021-11-30T07:55:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2021-11-30:/medium-archive/pelican/posts/2021/ipython-parallel-in-2021/</id><summary type="html">&lt;p&gt;Updates on IPython Parallel; new features, future direction&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;This post describes work funded by Bodo, Inc.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ipyparallel.readthedocs.io/"&gt;IPython Parallel&lt;/a&gt;’s a bit of an odd duck in the parallel computing space. In a world with &lt;a href="https://dask.org/"&gt;dask&lt;/a&gt;, &lt;a href="https://ray.io/"&gt;ray&lt;/a&gt;, &lt;a href="https://docs.bodo.ai/latest/source/getting_started.html"&gt;bodo&lt;/a&gt;, &lt;a href="https://spark.apache.org/docs/latest/api/python/index.html"&gt;pyspark&lt;/a&gt;, and other parallel computing tools, what is IPython Parallel’s role in 2021?&lt;/p&gt;
&lt;h2 id="context"&gt;Context&lt;/h2&gt;
&lt;p&gt;IPython Parallel began (in 2006!) as a natural extension of the &lt;a href="https://jupyter-client.readthedocs.io/en/stable/messaging.html#general-message-format"&gt;Jupyter messaging protocol&lt;/a&gt;(&lt;em&gt;though it predates the Jupyter name by a few years&lt;/em&gt;): when you have a protocol for &lt;a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop"&gt;REPL&lt;/a&gt;-style remote code execution, what can you do with &lt;em&gt;multiple&lt;/em&gt; remote execution environments? This question led to the development two basic models for parallel execution in IPython Parallel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;“multiplexed execution” — where you send code explicitly to different workers, and&lt;/li&gt;
&lt;li&gt;“load-balanced execution” — where you tell IPython Parallel what to run, and a scheduler takes care of assigning each task to an available worker.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="what-ipython-parallel-doesnt-do"&gt;What IPython Parallel doesn’t do&lt;/h2&gt;
&lt;p&gt;To IPython, your tasks are black boxes (either Python functions or blocks of Python code as text) and you are in complete control of where and when your tasks run, as well as any dependencies or side effects they may have.&lt;/p&gt;
&lt;p&gt;IPython Parallel specifically doesn’t and won’t do several things that other tools might:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;infer dependencies or relationships between tasks (execution graph)&lt;/li&gt;
&lt;li&gt;manage data distribution or locality&lt;/li&gt;
&lt;li&gt;construct parallel algorithms or represent ‘natively’ parallel distributed data structures, such as distributed arrays or data frames&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything is very explicit and implemented at the client-level in IPython Parallel, meaning that while &lt;em&gt;it&lt;/em&gt; doesn’t do these things, &lt;em&gt;you&lt;/em&gt; can. It also means that many of the things that have overhead costs associated with parallel computing (communication, data movement) have particularly high costs in IPython Parallel.&lt;/p&gt;
&lt;p&gt;IPython Parallel doesn’t hide anything from you, for better &lt;em&gt;and&lt;/em&gt; worse.&lt;/p&gt;
&lt;h2 id="what-ipython-parallel-does"&gt;What IPython Parallel does&lt;/h2&gt;
&lt;p&gt;IPython Parallel &lt;strong&gt;makes &lt;em&gt;explicit&lt;/em&gt; parallel computations interactive&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;IPython Parallel is not just parallel Python, it’s parallel &lt;em&gt;IPython&lt;/em&gt;. That means you have all the power of IPython’s inspection, interactivity, magics, and debugging available on all of your distributed workers. This makes it especially well-suited to prototyping and experimentation.&lt;/p&gt;
&lt;p&gt;IPython Parallel also presents standard APIs such as &lt;a href="https://ipyparallel.readthedocs.io/en/8.0.0/examples/Futures.html#Executors"&gt;Python Executors&lt;/a&gt;, compatible with many other implementations, to make it easy to migrate to &lt;em&gt;and from&lt;/em&gt; IPython Parallel, enabling developers to write code that uses a single multicore laptop or a thousand cores on an HPC cluster or cloud.&lt;/p&gt;
&lt;h2 id="ipython-parallel-in-2021"&gt;IPython Parallel in 2021&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt="Interactive progress across parallel engines. Those progress bars are interactive widgets running locally, and on each remote engine!" src="https://jasongrout.github.io/medium-archive/pelican/posts/2021/ipython-parallel-in-2021/images/001-1_5W4lCfV_HPwSmFP88niiJg.mp4" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;Interactive progress across parallel engines. Those progress bars are interactive widgets running locally, &lt;em&gt;and on each remote engine!&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;For a lot of today’s workloads, my default recommendation is: use &lt;a href="https://dask.org/"&gt;dask&lt;/a&gt; or bodo or another modern tool. IPython even makes this easier if you already happen to have an IPython Parallel cluster, you can tell it to “&lt;a href="https://ipyparallel.readthedocs.io/en/8.0.0/examples/dask.html"&gt;become dask&lt;/a&gt;,” and off you go:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;dask_client = rc.become_dask(ncores=1)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, where IPython Parallel can shine, is making traditional &lt;a href="https://en.wikipedia.org/wiki/SPMD"&gt;SPMD&lt;/a&gt; (e.g., MPI) workloads interactive, especially for prototyping and debugging.&lt;/p&gt;
&lt;p&gt;If you have an MPI simulation and you wish you could pause it in the middle, poke around and make plots and interact with just one node or all of them with all the interactive tools available to you in Jupyter, IPython Parallel may be the tool for you.&lt;/p&gt;
&lt;h2 id="recent-developments"&gt;Recent developments&lt;/h2&gt;
&lt;p&gt;Because I see the main problem IPython Parallel solves well is adding interactivity to direct parallel execution, the focus of recent developments has been on improving that story, based on feedback from users who are often in traditional HPC environments like SLURM or PBS, or using MPI in the cloud.&lt;/p&gt;
&lt;p&gt;Much of the feedback on challenges over the years have been around:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scaling to larger numbers of engines (1,–10,000)&lt;/li&gt;
&lt;li&gt;Better feedback and recovery when things go wrong&lt;/li&gt;
&lt;li&gt;Security requirements&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to &lt;a href="https://bodo.ai/"&gt;Bodo&lt;/a&gt;, I’ve been able to spend a lot of time this year addressing several of those issues, now available as IPython Parallel 8.0.&lt;/p&gt;
&lt;p&gt;Last year, Tom-Olav Bøyum developed a broadcast scheduler as part of his &lt;a href="https://www.duo.uio.no/handle/10852/81515"&gt;Master’s thesis&lt;/a&gt;, which vastly improves the efficiency of sending the same task to all engines (the main thing we do in IPP+MPI). This work is now mostly done and landed in IPython Parallel 7. We’ve also addressed longstanding issues with registering large numbers of engines (5–10,000) at once, a common source of failure on HPC clusters.&lt;/p&gt;
&lt;p&gt;One of the most frustrating / challenging things when working with MPI or other parallel computations is dealing with hangs and restarting tasks. Through the new &lt;a href="https://ipyparallel.readthedocs.io/en/8.0.0/examples/Cluster%20API.html"&gt;Cluster API&lt;/a&gt;, IPython Parallel now has one of its most requested features: the ability to send signals to one or all engines, and forcefully restart the cluster if it’s stuck.&lt;/p&gt;
&lt;p&gt;We’ve also improved interactive feedback with progress bars, live streaming output, a newJupyterLab plugin based on dask-labextension, and support for the Jupyter widget protocol, so you can instantiate widgets on engines and interact with them directly from a notebook.&lt;/p&gt;
&lt;p&gt;Security questions have also been raised, because IPython (and Jupyter’s) use of ZeroMQ assumes a ‘trusted network’, typically localhost or BSD sockets for notebooks. Since IPython Parallel usually runs across a network, security can be more of a concern (whereas notebooks typically only use standard HTTPS connections over a network), and the level of security in the Jupyter protocol alone may not be sufficient. In the past, we’ve resorted to tunneling TCP over SSH for more secure network traffic, but still always implicitly trusting localhost.&lt;/p&gt;
&lt;p&gt;IPython Parallel 7.1 enables &lt;a href="https://rfc.zeromq.org/spec/26/"&gt;CurveZMQ&lt;/a&gt; for full authentication, encryption, and &lt;a href="https://en.wikipedia.org/wiki/Forward_secrecy"&gt;forward-secrecy&lt;/a&gt; at the transport level, solving a longstanding security shortcoming of IPython Parallel.&lt;/p&gt;
&lt;h2 id="ipython-parallel-in-the-future"&gt;IPython Parallel in the future&lt;/h2&gt;
&lt;p&gt;Scaling is the biggest challenge for IPython Parallel due to its communication model. However, that scaling is on the order of IPython &lt;em&gt;engines&lt;/em&gt;, which is not the same as the number of cores. You can benefit from this today if your tasks are already multi-threaded, e.g., through OpenMP threads in numpy, or your own Python threads or multiprocess breakdown of tasks.&lt;/p&gt;
&lt;p&gt;Better support of multi-level parallelism, where each “engine” may represent a multicore node, as individual nodes get bigger and bigger would mean moving the bar where IPython Parallel communication is the bottleneck back two orders of magnitude on large machines, because one ‘engine’ could represent 128 cores or more. Good support for 1000 128-core nodes would give IPython Parallel some pretty comfortable headroom for our target use cases, scale-wise.&lt;/p&gt;
&lt;p&gt;Also related to scaling, bringing the BroadcastView to maturity should allow us to completely replace the DirectView scheduler, as it should be able to match or beat it in almost every scenario with some further development. There are some features lacking, especially when it comes to error handling, but those can certainly be addressed in time.&lt;/p&gt;
&lt;p&gt;Finally, I’ll invite you to get involved. Especially if you are interested in prototyping parallel code, or making traditional MPI-style code interactive, check out IPython Parallel 8 and &lt;a href="https://github.com/ipython/ipyparallel/issues"&gt;let us know&lt;/a&gt; how it goes, or contribute your use case as an example.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install --upgrade ipyparallel
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
</content><category term="IPython"/></entry><entry><title>nbviewer has a new host: OVHcloud</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2020/nbviewer-has-a-new-host-ovhcloud/" rel="alternate"/><published>2020-03-06T19:46:00+00:00</published><updated>2020-03-06T19:46:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2020-03-06:/medium-archive/pelican/posts/2020/nbviewer-has-a-new-host-ovhcloud/</id><summary type="html">&lt;p&gt;nbviewer has moved from Rackspace to OVHcloud. That means moving from Docker to Kubernetes+Helm&lt;/p&gt;
</summary><content type="html">&lt;p&gt;For several years, &lt;a href="https://nbviewer.jupyter.org"&gt;nbviewer&lt;/a&gt; has been generously hosted by Rackspace. That sponsorship program appears to be ending, so nbviewer needed a new home; it has found one in &lt;a href="https://ovhcloud.com"&gt;OVHcloud&lt;/a&gt;. We are extremely grateful to OVHcloud for their support in keeping nbviewer running, building on their existing participation in the &lt;a href="/posts/2019/the-international-binder-federation/"&gt;Binder Federation&lt;/a&gt; (literally—nbviewer is now running on the same kubernetes cluster as ovh.mybinder.org).&lt;/p&gt;
&lt;h2 id="how-we-moved"&gt;How we moved&lt;/h2&gt;
&lt;h3 id="where-were-we-before"&gt;Where were we before?&lt;/h3&gt;
&lt;p&gt;nbviewer was previously deployed using a private repo (because it contained credentials) and various commands using &lt;a href="http://www.pyinvoke.org"&gt;invoke&lt;/a&gt;. It was a mixture of custom steps, using the openstack Python API and docker machine to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;allocate two VMs&lt;/li&gt;
&lt;li&gt;build docker images&lt;/li&gt;
&lt;li&gt;deploy two nbviewer instances per node&lt;/li&gt;
&lt;li&gt;deploy memcached via nbcache on each node&lt;/li&gt;
&lt;li&gt;deploy statuspage publisher as a separate step&lt;/li&gt;
&lt;li&gt;update fastly to point to the running nbviewer instances&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The upside was that we had a repo that could immediately deploy nbviewer to anywhere with docker. With this repo, we moved our deployment strategy from a CoreOS cluster &lt;a href="https://github.com/jupyter/nbviewer.org-deploy/commit/2bf42cdfd54552d2669549bda002a8bf5f3df60d"&gt;to&lt;/a&gt; Rackspace’s short-lived &lt;a href="https://www.rackspace.com/newsroom/carina-by-rackspace-simplifies-containers-with-easy-to-use-instant-on-native-container-environment"&gt;Carina service&lt;/a&gt; to &lt;a href="https://github.com/jupyter/nbviewer.org-deploy/commit/22f54fc971941764ec3e14a68174823397a56163"&gt;deploying VMs ourselves&lt;/a&gt; with docker-machine. Migrating to a new source of VMs would not have been hard, but it wouldn’t have solved any of our challenges.&lt;/p&gt;
&lt;p&gt;Known downsides of this deployment that we’ve experienced over the years:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;only a few folks ever knew how to use it and could thus deploy updates to nbviewer&lt;/li&gt;
&lt;li&gt;it was private, contributing to above. It’s hard to onboard folks in an open community to a private repo!&lt;/li&gt;
&lt;li&gt;independent machines meant cache was not shared (minor, but contributes to our consumption of the GitHub API rate limit)&lt;/li&gt;
&lt;li&gt;no automatic recovery based on health monitoring, so when a container had issues, some humans got automated emails but no action was automatically taken. Extra frustrating because the fix was ~always to restart the container.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The credentials used by this repo have been revoked and an archived version of the repo is &lt;a href="https://github.com/jupyter/nbviewer.org-deploy/tree/archive-rackspace"&gt;available&lt;/a&gt; (with credentials redacted from history) in the new, &lt;a href="https://github.com/jupyter/nbviewer.org-deploy"&gt;public nbviewer.org-deploy repo&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="what-have-we-learned"&gt;What have we learned?&lt;/h3&gt;
&lt;p&gt;We’ve learned a lot about open, automatic, and sustainable deployments, and can now comfortably address all of the downsides above. Most of this has been learned from the communities participating in the JupyterHub and Binder projects, as seen in the &lt;a href="https://github.com/jupyterhub/mybinder.org-deploy"&gt;mybinder.org-deploy&lt;/a&gt; repo. mybinder.org-deploy is a public repo that automatically deploys and tests updates to at least four different Kubernetes clusters at the push of a button (the Big Green Merge Button, to be precise). Some things we have learned in the years since we set up our nbviewer deployment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kubernetes and helm are great :)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/AGWA/git-crypt"&gt;git-crypt&lt;/a&gt; allows us to have public repos with some secret contents, so deployment repos don’t need to be fully private just to protect a couple api keys.&lt;/li&gt;
&lt;li&gt;Continuous Deployment via services like Travis or Circle lowers the bar to adding maintainers on a given deployment since all that’s needed is to press the Big Green Button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The OVH sponsorship came in the form of a slice of a kubernetes cluster; that meant we had to migrate the nbviewer deployment tools from using docker-machine to kubernetes, which for us means helm.&lt;/p&gt;
&lt;h3 id="step-1-helm-chart-for-nbviewer"&gt;Step 1: helm chart for nbviewer&lt;/h3&gt;
&lt;p&gt;The first step was to create a helm chart for nbviewer, which is done &lt;a href="https://github.com/jupyter/nbviewer/pull/905"&gt;here&lt;/a&gt;. Before, nbviewer was two docker containers running nbviewer and one running memcached per server. To turn this into a helm chart we need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dependency on &lt;a href="https://github.com/helm/charts/tree/master/stable/memcached"&gt;memcached helm chart&lt;/a&gt; for easy deployment of the cache. All the nbviewer instances will talk to this memcache, which should improve our GitHub rate limit consumption, since the cache will be shared. This means the deprecation of our own &lt;a href="https://github.com/jupyter/nbcache"&gt;nbcache repo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Deployment&lt;/code&gt; for nbviewer. This is (one of) the kubernetes wrappers around containers. It has a nice ‘replicas’ field to easily scale nbviewer up and down. We are currently running with 3 replicas.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Service&lt;/code&gt; to expose nbviewer to the Internet (likely also need an ingress in the future)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Deployment&lt;/code&gt; for the statuspage publisher, which updates &lt;a href="https://status.jupyter.org"&gt;https://status.jupyter.org&lt;/a&gt; with the remaining github rate limit available&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We also needed helm charts and docker images for some other components, such as cdn.jupyter.org and the nbviewer statuspage data source.&lt;/p&gt;
&lt;h3 id="step-1b-helm-chart-for-cdnjupyterorg"&gt;Step 1b: helm chart for cdn.jupyter.org&lt;/h3&gt;
&lt;p&gt;nbviewer and some other services interact with cdn.jupyter.org, which is a lightweight nginx configuration to serve the classic notebook’s javascript and css as static files(this is no longer needed for npm-based jupyterlab, which can use &lt;a href="https://unpkg.com"&gt;unpkg&lt;/a&gt; as a CDN). This used to run on a multipurpose server VM, but that is also being retired for the same reason. The result was creating a docker image and helm chart for serving the contents of cdn.jupyter.org. The scripts used to run the existing CDN were already in &lt;a href="http://github.com/jupyter/cdn.jupyter.org/"&gt;a repo&lt;/a&gt;, so it was a small amount of work to adapt this to run in a container instead of on a server. We may retire cdn.jupyter.org in the future, so please don’t rely on it :)&lt;/p&gt;
&lt;p&gt;This is where we are right now — nbviewer.jupyter.org and cdn.jupyter.org are being served by OVH and the Rackspace machines are being retired.&lt;/p&gt;
&lt;h3 id="step-2-automatic-deployment"&gt;Step 2: automatic deployment&lt;/h3&gt;
&lt;p&gt;It would have been ideal for this to be step 1, but that’s now how it happened. Sometimes you need to get it done quickly before you get it done right. The task here will be to make a new, public nbviewer-deploy repo following the patterns we have learned in mybinder.org-deploy. That will mean:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;create public nbviewer.org-deploy repo with configuration and git-crypt encrypted secrets for deploying nbviewer on the ovh cluster (&lt;a href="https://github.com/jupyter/nbviewer.org-deploy"&gt;done&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;adopting &lt;a href="https://github.com/jupyterhub/chartpress"&gt;chartpress&lt;/a&gt; to publish our helm charts for nbviewer and version-tagged images from the nbviewer repo&lt;/li&gt;
&lt;li&gt;configure Travis-CI or other CI service to automatically deploy updates with helm, so merging a PR is all we need to do to deploy updates to nbviewer&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to OVHCloud for their continued support of Jupyter and Binder, and the JupyterHub and Binder communities for teaching me how to operate services in the open with kubernetes and helm.&lt;/p&gt;
</content><category term="Kubernetes"/><category term="nbviewer"/></entry><entry><title>Announcing JupyterHub 1.0</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/announcing-jupyterhub-1-0/" rel="alternate"/><published>2019-05-04T03:58:00+00:00</published><updated>2019-05-04T03:58:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2019-05-04:/medium-archive/pelican/posts/2019/announcing-jupyterhub-1-0/</id><summary type="html">&lt;p&gt;Today, we are pleased to announce the 1.0 release of JupyterHub. We’ve come a long way since our first release in March, 2015. There are…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Today, we are pleased to announce the 1.0 release of JupyterHub. We’ve come a long way since our first release in March, 2015. There are loads of new features and improvements covered in the &lt;a href="https://jupyterhub.readthedocs.io/en/latest/changelog.html"&gt;changelog&lt;/a&gt;, but we’ll cover a few of the highlights here.&lt;/p&gt;
&lt;p&gt;You can upgrade jupyterhub with conda or pip:&lt;/p&gt;
&lt;p&gt;(Before upgrading, always make sure to backup your database!)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install -c conda-forge jupyterhub==1.0.*
pip install --upgrade jupyterhub==1.0.*
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="ui-for-named-servers"&gt;UI for named servers&lt;/h2&gt;
&lt;p&gt;Named servers are a concept in JupyterHub that allows each user to have more than one server (e.g. a ‘gpu’ server with access to gpus, or a ‘cs284’ server with the necessary resources for a given class). JupyterHub 1.0 introduces UI for managing these servers, so users can create/start/stop/delete their servers from the hub home page:&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="UI for managing named servers" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/announcing-jupyterhub-1-0/images/001-1_VozbyUHN9qTICXzQSKQvfw.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;UI for managing named servers&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="improved-pending-spawn-handling-and-less-implicit-spawn-behavior"&gt;Improved pending spawn handling and less implicit spawn behavior&lt;/h2&gt;
&lt;p&gt;When users launch their server, they will be faced with a progress bar showing the progress. Spawners can emit custom messages to indicate the stages of launch, which are especially useful when it can take a while.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt="a spawn taking a bit" src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/announcing-jupyterhub-1-0/images/002-1_e5FwpbvGl7SFbl9vGevBwA.webp" loading="lazy" data-body-image=""&gt;
&lt;figcaption&gt;a spawn taking a bit&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="support-for-total-internal-encryption-with-ssl"&gt;Support for total internal encryption with SSL&lt;/h2&gt;
&lt;p&gt;If your JupyterHub is publicly accessible, we hope you are using HTTPS (it’s never been easier, thanks to &lt;a href="https://letsencrypt.org"&gt;letsencrypt&lt;/a&gt;)! However, JupyterHub exclusively used HTTP for internal communication. This is usually fine, especially for single-machines, but for deployments on distributed or shared infrastructure it’s a good idea to encrypt communication between your components. With 1.0, you can &lt;a href="https://jupyterhub.readthedocs.io/en/latest/reference/websecurity.html#encrypt-internal-connections-with-ssl-tls"&gt;enable SSL encryption&lt;/a&gt; and authentication of all internal communication as well. Spawners must support this feature by defining &lt;a href="https://jupyterhub.readthedocs.io/en/latest/api/spawner.html?highlight=move_certs#jupyterhub.spawner.Spawner.move_certs"&gt;Spawner.move_certs&lt;/a&gt;. Currently, local spawners and DockerSpawner support internal ssl.&lt;/p&gt;
&lt;h2 id="support-for-checking-and-refreshing-authentication"&gt;Support for checking and refreshing authentication&lt;/h2&gt;
&lt;p&gt;JupyterHub authentication is most often managed by an external authority, e.g. GitHub OAuth. &lt;a href="https://jupyterhub.readthedocs.io/en/latest/reference/authenticators.html#authentication-state"&gt;Auth state&lt;/a&gt; can be used to persist credentials, such as enabling push access to repositories or access to resources. These credentials can sometimes expire or need refreshing. Until now, expiring or refreshing authentication was not well supported by JupyterHub. 1.0 introduces new configuration to refresh or expire authentication info:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c.Authenticator.auth_refresh_age&lt;/code&gt; allows authentication to expire after a number of seconds.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;c.Authenticator.refresh_pre_spawn&lt;/code&gt; forces a refresh of authentication prior to spawning a server, effectively requiring a user to have up-to-date authentication when they start their server, which can be important when the user environment should have credentials with access to external resources.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Authenticator.refresh_auth&lt;/code&gt; defines what it means to refresh authentication (default is nothing, but can check with external authorities to re-load info such as tokens or group membership) and can be customized by Authenticator implementations.&lt;/p&gt;
&lt;h2 id="thanks"&gt;Thanks!&lt;/h2&gt;
&lt;p&gt;Huge thanks to the many people who have contributed to this release, whether it was through discussion, testing, documentation, or development.&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>Open Redirect Vulnerability in Jupyter notebook, JupyterHub</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/open-redirect-vulnerability-in-jupyter-jupyterhub/" rel="alternate"/><published>2019-03-28T14:03:00+00:00</published><updated>2019-04-01T11:20:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2019-03-28:/medium-archive/pelican/posts/2019/open-redirect-vulnerability-in-jupyter-jupyterhub/</id><summary type="html">&lt;p&gt;JupyterHub ≤ 0.9.4 and Notebook ≤ 5.7.6 are affected. Versions 0.9.5 and 5.7.7 are released today with fixes. Upgrade with pip or conda:&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;Update: notebook 5.7.7 and JupyterHub 0.9.5 contained incomplete fixes for this issue. 5.7.8 and 0.9.6 are released with more complete fixes.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;JupyterHub ≤ 0.9.5 and Notebook ≤ 5.7.8 are affected. Versions 0.9.6 and 5.7.8 are released with fixes. Upgrade with pip or conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install --upgrade &amp;#39;notebook&amp;gt;=5.7.8&amp;#39;
pip install --upgrade &amp;#39;jupyterhub&amp;gt;=0.9.6&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install -c conda-forge &amp;#39;notebook&amp;gt;=5.7.8&amp;#39;
conda install -c conda-forge &amp;#39;jupyterhub&amp;gt;=0.9.6&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;notebook 5.7.8 also fixes some compatibility issues and regressions introduced in 5.7.5 and 5.7.6, especially on some Windows systems. If you use the &lt;a href="https://zero-to-jupyterhub.readthedocs.io"&gt;jupyterhub helm chart&lt;/a&gt;, version 0.8.2 upgrades JupyterHub to 0.9.6.&lt;/p&gt;
&lt;h2 id="what-is-an-open-redirect-vulnerability"&gt;What is an Open Redirect Vulnerability?&lt;/h2&gt;
&lt;p&gt;Login pages tend to take a parameter for redirecting back to a page after successful login, e.g. &lt;code&gt;/login?next=/notebooks/mynotebook.ipynb&lt;/code&gt;, so that you aren’t disrupted too much if you try to visit a page, but have to authenticate first. An &lt;a href="https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.md"&gt;Open Redirect Vulnerability&lt;/a&gt; is when a malicious person crafts a link pointing to the login page of a trusted site, but setting the “redirect after successful login” parameter to send the user to their own site, instead of a page on the authenticated site (the notebook or JupyterHub server), e.g. &lt;code&gt;/login?next=http://badwebsite.biz&lt;/code&gt;. This doesn’t necessarily compromise anything immediately, but it enables phishing if users don’t notice that the domain has changed, e.g. by showing a fake “re-enter your password” page. Servers generally have to validate the redirect URL to avoid this. Both JupyterHub and Notebook already do this, but the validation didn’t take into account all possible ways to redirect to other sites, so some malicious URLs could still be crafted to redirect away from the server (the above example does not work in any recent version of either package). Only certain browsers (Chrome and Firefox, not Safari) could be redirected from the JupyterHub login page, but all browsers could be redirected away from a standalone notebook server.&lt;/p&gt;
&lt;p&gt;This vulnerability has been assigned CVE-2019–10255. This post will be updated when it has been assigned. Thanks to Ronak Odhaviya for reporting the issue. You can report security issues by emailing &lt;a href="mailto:ipython-security@googlegroups.com"&gt;ipython-security@googlegroups.com&lt;/a&gt;.&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="JupyterHub"/><category term="security"/></entry><entry><title>Jupyter notebook XSSI security fix</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/jupyter-notebook-xssi-security-fix/" rel="alternate"/><published>2019-03-09T20:46:00+00:00</published><updated>2019-03-09T20:46:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2019-03-09:/medium-archive/pelican/posts/2019/jupyter-notebook-xssi-security-fix/</id><summary type="html">&lt;p&gt;We have just released Jupyter notebook 5.7.6 with a security fix for a cross-site inclusion (XSSI) vulnerability, where content from a…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We have just released Jupyter notebook 5.7.6 with a security fix for a cross-site inclusion (&lt;a href="https://www.scip.ch/en/?labs.20160414"&gt;XSSI&lt;/a&gt;) vulnerability, where content from a Jupyter server could be included in another page if the visitor is logged in to the Jupyter server and the author of the page knows the URL of the server and the path within the server’s notebook directory that they would like to include. Further, it has been demonstrated with the Internet Explorer browser that some content from the accessed file can be retrieved by the attacking page. This has not yet been demonstrated with other browsers, however.&lt;/p&gt;
&lt;p&gt;To upgrade:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;pip install --upgrade &amp;#39;notebook&amp;gt;=5.7.6&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install &amp;#39;notebook&amp;gt;=5.7.6&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This vulnerability was reported by the HackerOne hacker Abhishek Bundela, via Jonathan Kamens at Quantopian. The patch was developed by Min Ragan-Kelley with help from Devdatta Akhawe. Jupyter is grateful to Quantopian’s bug bounty program, which has found and reported this and other security flaws in Jupyter.&lt;/p&gt;
&lt;p&gt;This vulnerability has been assigned CVE-2019–9644.&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="security"/></entry><entry><title>Zero to JupyterHub helm chart 0.8</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2019/zero-to-jupyterhub-helm-chart-0-8/" rel="alternate"/><published>2019-02-21T18:34:00+00:00</published><updated>2019-02-21T18:34:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2019-02-21:/medium-archive/pelican/posts/2019/zero-to-jupyterhub-helm-chart-0-8/</id><summary type="html">&lt;p&gt;We’ve just released version 0.8 of the jupyterhub helm chart.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We’ve just released version 0.8 of the jupyterhub helm chart.&lt;/p&gt;
&lt;p&gt;For those who may not know, &lt;a href="https://zero-to-jupyterhub.readthedocs.io"&gt;Zero to JupyterHub&lt;/a&gt; is a guide and &lt;a href="https://helm.sh"&gt;helm chart&lt;/a&gt; for deploying &lt;a href="https://jupyterhub.readthedocs.io"&gt;JupyterHub&lt;/a&gt; on &lt;a href="https://kubernetes.io"&gt;Kubernetes&lt;/a&gt;. Helm is like a package manager for Kubernetes, which aims to make it easy to install and manage applications on your cluster.&lt;/p&gt;
&lt;p&gt;There are loads of bugfixes and improvements to this release, and we encourage you to check out the &lt;a href="https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/master/CHANGELOG.md"&gt;change log&lt;/a&gt;. Here, we’ll discuss two highlights from the new features: profiles and autoscaling.&lt;/p&gt;
&lt;h2 id="profiles"&gt;&lt;strong&gt;Profiles&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;By adopting the profiles pattern established in &lt;a href="https://github.com/jupyterhub/wrapspawner"&gt;WrapSpawner&lt;/a&gt;, Kubernetes users can now pick from a selection of “profiles” or pre-set configurations, defined by the operator of a cluster. For example, defining small, medium, and large resource requests, or special profiles for GPUs.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jasongrout.github.io/medium-archive/pelican/posts/2019/zero-to-jupyterhub-helm-chart-0-8/images/001-1_XRxmQ4Mmd48iE7ST-zdcUg.webp" alt="" loading="lazy" data-body-image=""&gt;&lt;/p&gt;
&lt;p&gt;The above sample is created from the following snippet in your values.yaml:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;singleuser&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;profileList&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;display_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Small: default&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;small&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CPU&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GPU&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;True&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;kubespawner_override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cpu_limit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cpu_guarantee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem_limit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1G&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem_guarantee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;512M&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;extra_resource_limits&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;display_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Big: 8 CPUs&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;big&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CPUs&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GPU&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="n"&gt;GB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RAM&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;kubespawner_override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cpu_limit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cpu_guarantee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem_limit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;64G&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem_guaranttee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;64G&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;extra_resource_limits&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;display_name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;GPU job&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;This&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;configuration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gives&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;CPUs&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="n"&gt;GB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RAM&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;GPU&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="n"&gt;kubespawner_override&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;consideratio&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;singleuser&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;3.0&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cpu_limit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;cpu_guarantee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem_limit&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;16G&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;mem_guarantee&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;16G&amp;quot;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;extra_resource_limits&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="n"&gt;nvidia&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id="autoscaling"&gt;Autoscaling&lt;/h2&gt;
&lt;p&gt;Autoscaling is a big focus of this release, and many of the new features in 0.8 of the chart relate to scaling in some way. At the center is a new ‘user scheduler’ a custom kubernetes scheduler responsible for assigning user pods to nodes. To enable the user scheduler:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;scheduling&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;userScheduler&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are two major pain-points for autoscaling with JupyterHub. First, is &lt;strong&gt;scaling up&lt;/strong&gt;. Kubernetes scale-up is very basic: If scheduling a pod fails due insufficient resources, a new node is requested. This isn’t a great fit for JupyterHub. If you happen to be the unlucky user who is the first to need a new node, launching your server can take several minutes as the new node is requested and warming up. To prevent this, the chart adds the notion of “placeholder pods,” which are pods that request the same resources as user pods, but have lower priority and will thus be evicted immediately if a user pod needs their resources. This allows a cluster to always have a certain amount of “headroom” of vacant slots available for users so that requesting a new node occurs not when there are 0 slots available for users, but instead when there are 5 or 10 (you choose) slots available. See &lt;a href="https://discourse.jupyter.org/t/planning-placeholders-with-jupyterhub-helm-chart-0-8-tested-on-mybinder-org/213"&gt;this exploration&lt;/a&gt; of how mybinder.org arrived at 25 placeholders for its deployment. To enable 10 user placeholders:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;scheduling&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;podPriority&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="n"&gt;userPlaceholder&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;replicas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The second major improvement to autoscaling that comes with the user scheduler is in &lt;strong&gt;scaling down&lt;/strong&gt;. If your cluster is automatically scaling up, you probably want it to scale down as well when you no longer need the added capacity. Unfortunately, because user pods cannot be kicked out, Kubernetes’ default scheduler doesn’t manage to drain nodes that are no longer needed without some manual intervention. The user scheduler adds this feature, ensuring that, over time, your newest nodes eventually drain when they are no longer needed. It accomplishes this by prioritizing the node with the most other user pods when picking a node to start new users. If there is an extra node, over time eventually all the users on that node will stop and the node will become idle and culled by the cluster autoscaler. This is the default behavior . There’s loads more scheduling optimizations available &lt;a href="https://zero-to-jupyterhub.readthedocs.io/en/latest/optimization.html#optimizations"&gt;in the updated documentation&lt;/a&gt;, including dedicating certain nodes to users so that user pods never run on the same node as others, etc.&lt;/p&gt;
&lt;p&gt;And finally, a huge thanks to the numerous contributors who helped make this release, be it via documentation contributions, testing, discussion, or code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://zero-to-jupyterhub.readthedocs.io/"&gt;Check it out&lt;/a&gt;&lt;/p&gt;
</content><category term="JupyterHub"/><category term="Kubernetes"/></entry><entry><title>Jupyter Notebook security fixes</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2018/jupyter-notebook-security-fixes/" rel="alternate"/><published>2018-11-19T22:20:00+00:00</published><updated>2018-11-19T22:20:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2018-11-19:/medium-archive/pelican/posts/2018/jupyter-notebook-security-fixes/</id><summary type="html">&lt;p&gt;Two security issues have been found and fixed this week, where untrusted javascript could be executed if malicious files could be…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;Two security issues have been found and fixed this week, where untrusted javascript could be executed if malicious files could be delivered to the users system and the user takes specific actions with those malicious files.&lt;/p&gt;
&lt;p&gt;The first allowed nbconvert endpoints (such as Print Preview) to render untrusted HTML and javascript with access to the notebook server. This is fixed in notebook 5.7.1. All notebook versions prior to 5.7.1 are affected. Thanks to Jonathan Kamens of Quantopian for reporting. This issue has been assigned CVE-2018–19351.&lt;/p&gt;
&lt;p&gt;The second issue allowed maliciously crafted directory names to execute javascript when opened in the tree view. This is fixed in notebook 5.7.2. All versions of notebook from 5.3.0 to 5.7.1 are affected. Thanks to Marvin Solano Quesada for reporting. This issue has been assigned CVE-2018–19352.&lt;/p&gt;
&lt;p&gt;You can check your version of the notebook package by issuing the following command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jupyter notebook --version&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Whether you are using classic notebook, JupyterLab or any other notebook server extensions, we recommend that you update the &lt;code&gt;notebook&lt;/code&gt; package with :&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install --upgrade notebook&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;or if you are using conda-forge&lt;/p&gt;
&lt;p&gt;&lt;code&gt;conda upgrade notebook&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Thanks especially to Jonathan and Marvin for reporting these issues! If you find a security issue in a Jupyter project, please report it to &lt;code&gt;security@ipython.org&lt;/code&gt;.&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="security"/></entry><entry><title>Security fix for Jupyter Notebook</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2018/security-fix-for-jupyter-notebook/" rel="alternate"/><published>2018-07-18T04:31:00+00:00</published><updated>2018-08-16T17:33:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2018-07-18:/medium-archive/pelican/posts/2018/security-fix-for-jupyter-notebook/</id><summary type="html">&lt;p&gt;We have just released Jupyter Notebook 5.6.0. This release fixes a vulnerability that could allow a maliciously crafted notebook to execute…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We have just released Jupyter Notebook 5.6.0. This release fixes a vulnerability that could allow a maliciously crafted notebook to execute JavaScript when it is opened, bypassing the trusted-notebook mechanism.&lt;/p&gt;
&lt;p&gt;We recommend updating the notebook immediately, via pip:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pip install notebook&amp;gt;=5.6.0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;or conda:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;conda install notebook&amp;gt;=5.6.0&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Affected versions&lt;/strong&gt;: all releases prior to 5.6.0&lt;/p&gt;
&lt;p&gt;JupyterLab users are affected, independent of the version of JupyterLab itself. Upgrading the notebook package to 5.6.0 resolves the issue for users of both JupyterLab and the classic notebook.&lt;/p&gt;
&lt;p&gt;A CVE has been requested for the vulnerability. Release notes for 5.6.0 and this post will be updated as the CVE is assigned. More details of the vulnerability will be released in 30 days, on August 16, 2018.&lt;/p&gt;
&lt;p&gt;Security reports for Jupyter are greatly appreciated. You can &lt;a href="http://jupyter-notebook.readthedocs.io/en/stable/security.html#reporting-security-issues"&gt;report security issues&lt;/a&gt; to security@ipython.org.&lt;/p&gt;
&lt;p&gt;Thanks to Jonathan Kamens for reporting this issue to the security list.&lt;/p&gt;
&lt;p&gt;[Update July 28] The vulnerability have been assigned number CVE-2018–1999024.&lt;/p&gt;
&lt;p&gt;[Update August 16] MathJax versions prior to version 2.7.4 contains a Cross Site Scripting (XSS) vulnerability in the \unicode{} macro that can result in potentially untrusted Javascript running within a web browser, for example at notebook load. Notebook 5.6.0 ships with an updated mathjax version 2.7.4 which fixes this vulnerability.&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="security"/></entry><entry><title>JupyterHub 0.9</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2018/jupyterhub-0-9/" rel="alternate"/><published>2018-06-18T13:18:00+00:00</published><updated>2018-06-18T15:15:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2018-06-18:/medium-archive/pelican/posts/2018/jupyterhub-0-9/</id><summary type="html">&lt;p&gt;We are pleased to announce the latest release of JupyterHub. JupyterHub is the multi-user server for Jupyter notebooks, allowing students…&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are pleased to announce the latest release of JupyterHub. JupyterHub is the multi-user server for Jupyter notebooks, allowing students or researchers to have their own workspace. This release has lots of improvements, especially for stability and performance with large numbers of users.&lt;/p&gt;
&lt;p&gt;The biggest change is probably the adoption of asyncio coroutines throughout instead of tornado coroutines and improved support for asyncio coroutines in general, adopting &lt;code&gt;async def&lt;/code&gt; coroutine syntax. This means that JupyterHub 0.9 requires Python ≥ 3.5 and tornado ≥ 5.0.&lt;/p&gt;
&lt;p&gt;There are lots of improvements to the &lt;a href="https://jupyterhub.readthedocs.io/en/0.9.0/_static/rest-api/index.html"&gt;REST API&lt;/a&gt;, including more detailed time information and token management.&lt;/p&gt;
&lt;p&gt;There are also several improvements to the ability to customize JupyterHub’s HTML templates.&lt;/p&gt;
&lt;p&gt;See the &lt;a href="http://jupyterhub.readthedocs.io/en/0.9.0/changelog.html"&gt;changelog&lt;/a&gt; for a more detailed list of changes.&lt;/p&gt;
&lt;p&gt;Make sure to backup your jupyterhub database prior to upgrade, and then upgrade with pip:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3 -m pip install --upgrade &amp;#39;jupyterhub==0.9.*&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or conda:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;conda install -c conda-forge jupyterhub=0.9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For Kubernetes users, we will have an 0.7 release of the &lt;a href="https://zero-to-jupyterhub.readthedocs.io/en/latest/"&gt;helm chart&lt;/a&gt; before too long.&lt;/p&gt;
&lt;p&gt;Thanks to everyone who has contributed to JupyterHub!&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>Security fix for JupyterHub GitLab OAuthenticator Group Whitelists</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2018/security-fix-for-jupyterhub-gitlab-oauthenticator/" rel="alternate"/><published>2018-02-16T22:12:00+00:00</published><updated>2018-03-16T15:15:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2018-02-16:/medium-archive/pelican/posts/2018/security-fix-for-jupyterhub-gitlab-oauthenticator/</id><summary type="html">&lt;p&gt;This vulnerability has been assigned CVE-2018–7206&lt;/p&gt;
</summary><content type="html">&lt;p&gt;This vulnerability has been assigned CVE-2018–7206&lt;/p&gt;
&lt;p&gt;If you are using JupyterHub with the GitLab OAuthenticator and its gitlab_group_whitelist support, there is a security issue where the authenticator will allow users outside your intended group whitelist to create accounts. A fix has been released as OAuthenticator 0.6.2 and 0.7.3. No other authentication mechanism, including GitLabOAuthenticator without using the group whitelist feature, is affected. If you are using GitLab authentication with group whitelist support, upgrade oauthenticator immediately:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;python3 -m pip install --upgrade oauthenticator
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Thanks to Joseph Weston for reporting the issue and providing the fix.&lt;/p&gt;
&lt;p&gt;Timeline (all times UTC):&lt;/p&gt;
&lt;p&gt;2018–02–16 09:51 Joseph Weston reports security issue to the Jupyter security list&lt;/p&gt;
&lt;p&gt;2018–02–16 16:08 Fix is verified and applied to oauthenticator master&lt;/p&gt;
&lt;p&gt;2018–02–16 21:52 oauthenticator 0.7.3 and 0.6.2 are released with the fix&lt;/p&gt;
&lt;p&gt;2018–02–18 03:02 CVE-2018–7206 assigned&lt;/p&gt;
</content><category term="JupyterHub"/><category term="security"/></entry><entry><title>Public Notebooks and Security</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2017/public-notebooks-and-security/" rel="alternate"/><published>2017-11-28T19:33:00+00:00</published><updated>2017-11-28T19:33:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2017-11-28:/medium-archive/pelican/posts/2017/public-notebooks-and-security/</id><summary type="html">&lt;p&gt;tl;dr: don’t disable notebook authentication!&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;em&gt;tl;dr: don’t disable notebook authentication!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Let’s chat a little bit about public Jupyter notebook servers and security. A lot of folks run notebook servers that are publicly accessible, whether they are the access point to a cloud server or remote access to their home or office computer. This is great! It is important to keep in mind that, since arbitrary code execution is the main point of IPython and Jupyter, running a publicly accessible server without authentication or encryption is a very bad idea indeed. Prior to notebook 4.3, this was the &lt;em&gt;default&lt;/em&gt; behavior of Jupyter, which has led to a lot of insecure notebook servers running on cloud services. One aspect that may not be apparent is that &lt;strong&gt;people can find your public notebook server&lt;/strong&gt;, especially if it is on one of the popular cloud providers. Systematic port scans will turn up notebook servers, especially for those running on the default port of 8888. And Jupyter has become popular enough (🎉) that we know people are doing this (😠). &lt;strong&gt;Running a publicly accessible notebook server without authentication is making your computing resources free for the world to use.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The highlights to keep in mind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Always enable a password or token for publicly accessible notebook servers (this includes shared computers)&lt;/li&gt;
&lt;li&gt;Use HTTPS for authenticated notebooks over the Internet&lt;/li&gt;
&lt;li&gt;Unsecured public notebook servers are discoverable&lt;/li&gt;
&lt;li&gt;Running on a cloud service should be treated as if it’s publicly accessible&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first two points shouldn’t be new information, and anyone &lt;a href="https://jupyter-notebook.readthedocs.io/en/latest/public_server.html#running-a-public-notebook-server"&gt;following the documentation&lt;/a&gt; should be adding basic authentication when exposing a server to the public. If you are already accessing a notebook server over https with a password or token, you have nothing to worry about. However, if you are running unsecured notebook servers on the Internet, please stop and &lt;a href="http://jupyter-notebook.readthedocs.io/en/latest/public_server.html#securing-a-notebook-server"&gt;enable a password&lt;/a&gt;. The latter points are important and may not be obvious, but the gist is: don’t disable notebook authentication, even if you think your bases are covered via other means.&lt;/p&gt;
&lt;p&gt;In notebook 4.3, a token is enabled by default, making it a little bit more inconvenient to run a totally insecure notebook server. We can’t feasibly require HTTPS by default, but free, trusted HTTPS has never been easier, thanks to &lt;a href="https://letsencrypt.org/"&gt;LetsEncrypt&lt;/a&gt;. If you can’t assign a domain name to your server, self-signed certificates &lt;a href="http://jupyter-notebook.readthedocs.io/en/latest/public_server.html#using-ssl-for-encrypted-communication"&gt;can be used&lt;/a&gt;, with some caveats (notably: Safari requires self-signed certificates be trusted at the system level before it will allow websocket connections). For shared deployments, you can also use &lt;a href="https://jupyterhub.readthedocs.io"&gt;JupyterHub&lt;/a&gt;, which allows you to authenticate notebooks via the system (PAM) or OAuth providers such as GitHub, Google, etc.&lt;/p&gt;
&lt;p&gt;Be safe out there.&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="security"/></entry><entry><title>JupyterHub 0.8</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2017/jupyterhub-0-8/" rel="alternate"/><published>2017-10-03T19:42:00+00:00</published><updated>2017-10-03T19:42:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2017-10-03:/medium-archive/pelican/posts/2017/jupyterhub-0-8/</id><summary type="html">&lt;p&gt;We are pleased to announce the release of JupyterHub 0.8. This is a big release with many fixes and improvements, and some major changes.&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We are pleased to announce the release of JupyterHub 0.8. This is a big release with many fixes and improvements, and some major changes.&lt;/p&gt;
&lt;p&gt;To upgrade from jupyterhub 0.7:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop jupyterhub&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pip3 install --upgrade jupyterhub==0.8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Backup your database: e.g.&lt;code&gt;cp -v jupyterhub.sqlite jupyterhub-backup-$(date +%Y-%m-%d).sqlite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Upgrade your database: &lt;code&gt;jupyterhub upgrade-db&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See &lt;a href="http://jupyterhub.readthedocs.io/en/latest/changelog.html#changelog"&gt;the changelog&lt;/a&gt; for more details. The “Notes on Upgrading” below also provides more specifics.&lt;/p&gt;
&lt;h2 id="oauth"&gt;OAuth&lt;/h2&gt;
&lt;p&gt;Perhaps the biggest change is the switch to using OAuth 2.0 for the internal authentication mechanism in JupyterHub. This shouldn’t have much of a visible effect on your deployments other than hopefully a reduction in redirect-loops when users login.&lt;/p&gt;
&lt;p&gt;If you are writing a JupyterHub Service, then you can switch to OAuth by using the &lt;code&gt;HubOAuthenticated&lt;/code&gt; or &lt;code&gt;HubOAuth&lt;/code&gt; classes if you were previously using &lt;code&gt;HubAuthenticated&lt;/code&gt; or &lt;code&gt;HubAuth&lt;/code&gt;. All of these classes now support token-based access via the &lt;code&gt;Authorization&lt;/code&gt; header, just like the Hub itself.&lt;/p&gt;
&lt;h2 id="stability-scalability-and-performance"&gt;Stability, Scalability, and Performance&lt;/h2&gt;
&lt;p&gt;As part of supporting larger deployments, we have done some scalability stress testing of the Hub, finding and fixing several race conditions that could manifest as redirect loops, bugs, and performance improvements when the Hub is under load. This has been led largely by the Berkeley Data Science Education Program team, which has over one thousand students using a JupyterHub instance this Fall. We now know that one JupyterHub 0.8 instance can support a few thousand users logging in and attempting to spawn their servers at the same time. Once servers are active, the Hub is minimally involved, and the proxy becomes the next bottleneck.&lt;/p&gt;
&lt;h2 id="custom-proxy-implementations"&gt;Custom Proxy Implementations&lt;/h2&gt;
&lt;p&gt;Another major new feature targeted at scalability is custom proxy implementations. JupyterHub has always used Configurable-HTTP-Proxy (CHP), a single-process Node.js HTTP proxy. This is a single process bottleneck and potential single point of failure for Hub deployments. CHP can handle a lot of concurrent active users (at least several thousand), but for larger scale applications, Hub deployments may want to use a more scalable and/or robust proxy implementation.&lt;/p&gt;
&lt;p&gt;To achieve this, JupyterHub 0.8 introduces a Python API abstracting the proxy needs of JupyterHub and developers can provide their own proxy implementations as alternatives to CHP. The first such implementation uses a Kubernetes Traefik-based Ingress for the proxy, as part of &lt;a href="https://github.com/jupyterhub/kubespawner"&gt;KubeSpawner&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="persisting-authentication-state"&gt;Persisting Authentication State&lt;/h2&gt;
&lt;p&gt;Another new feature available to Authenticators is the encrypted persistence of authentication state. This is aimed at preserving and passing authenticator-related state, such as client certificates or GitHub API tokens. Authenticators provided by &lt;a href="https://github.com/jupyterhub/oauthenticator"&gt;oauthenticator&lt;/a&gt; 0.7 support persisting auth state from upstream authentication services.&lt;/p&gt;
&lt;h2 id="other-highlights"&gt;Other Highlights&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Preliminary (API-only, no GUI) support for multiple named servers per user&lt;/li&gt;
&lt;li&gt;Token-based access to the single-user server API&lt;/li&gt;
&lt;li&gt;A page for users to request new API tokens from the Hub&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="notes-on-upgrading"&gt;Notes on upgrading&lt;/h2&gt;
&lt;p&gt;As with every update to JupyterHub, make sure to back-up your database and run &lt;code&gt;upgrade-db&lt;/code&gt;. For example, if using JupyterHub with SQLite:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Backup:&lt;code&gt;cp -v jupyterhub.sqlite jupyterhub-backup-$(date +%Y-%m-%d).sqlite&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Upgrade: &lt;code&gt;jupyterhub upgrade-db&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Due to the change in internal authentication, it is &lt;strong&gt;important&lt;/strong&gt; that you make sure that single-user servers and the Hub itself are upgraded at the same time. If you are running everything in one env (e.g. the default Spawner, SudoSpawner, or similar), there is nothing to do. However, if you are using a container-based setup to launch single-user server (e.g. DockerSpawner or KubeSpawner), make sure that your user image and your Hub both get the upgrade at the same time. In most cases, this means adding to your Dockerfile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;RUN pip3 install jupyterhub==0.8.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which ensures the correct version of JupyterHub is installed.&lt;/p&gt;
&lt;p&gt;Thanks to everyone who has contributed to this release, especially users who have helped out with testing JupyterHub during the beta process, which helps make this a great release!&lt;/p&gt;
</content><category term="JupyterHub"/></entry><entry><title>Notebook 4.2.2: Security fix</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2016/security-fix-notebook-4-2-2/" rel="alternate"/><published>2016-08-03T20:47:00+00:00</published><updated>2016-08-03T20:47:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2016-08-03:/medium-archive/pelican/posts/2016/security-fix-notebook-4-2-2/</id><summary type="html">&lt;p&gt;TL;DR: upgrade to notebook 4.2.2 We just released notebook 4.2.2 with a few bugfixes and one important security fix. We (specifically Steve Sylvester) have found a longstanding security issue in the Notebook, regarding output of untrusted notebooks being able to execute code when the notebook&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;TL;DR: upgrade to notebook 4.2.2&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We just released notebook 4.2.2 with a &lt;a href="http://jupyter-notebook.readthedocs.io/en/stable/changelog.html#release-4-2-2"&gt;few bugfixes&lt;/a&gt; and one important security fix.&lt;/p&gt;
&lt;p&gt;We (specifically &lt;a href="https://github.com/blink1073"&gt;Steve Sylvester&lt;/a&gt;) have found a longstanding security issue in the Notebook, regarding output of untrusted notebooks being able to execute code when the notebook is opened.&lt;/p&gt;
&lt;p&gt;The vulnerability has been assigned the identifier CVE-2016-6524.&lt;/p&gt;
&lt;p&gt;Affected versions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2.0 ≤ IPython &amp;lt; 4.0 (all IPython notebook versions, starting with the introduction of the concept of untrusted output)&lt;/li&gt;
&lt;li&gt;notebook ≤ 4.2.1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Upgrade to notebook 4.2.2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pip install --upgrade notebook&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;or &lt;code&gt;conda install notebook&lt;/code&gt; (once conda channels propagate the release)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you cannot upgrade to 4.2.2, we have prepared a backported patch for the &lt;a href="https://github.com/ipython/ipython/commit/61c99ede7d31c1f661fba351765cac0f92c07856"&gt;2.x&lt;/a&gt; and &lt;a href="https://github.com/ipython/ipython/commit/e2ba4d66a08ae06d42ba9085b8a75ab8c4fad27b"&gt;3.x&lt;/a&gt; branches of IPython, but have no current plans for making a new release of these branches.&lt;/p&gt;
&lt;h2 id="details"&gt;Details&lt;/h2&gt;
&lt;p&gt;The vulnerability was reported on July 26, and a fix proposed on the same day. After some review and testing of the patch and a check for similar vulnerabilities, on August 3 a CVE was assigned and the 4.2.2 release made immediately thereafter.&lt;/p&gt;
&lt;p&gt;The goal of Jupyter notebooks is executing code, and outputs being able to execute code is one of the key features that enables people to do cool stuff. However, we don’t want notebooks written by not-you to be able to execute code as you on your computer as soon as you open them. For this reason, we have the concept of ‘trusted notebooks’ (introduced in IPython 2), which are notebooks where all output has been produced by you. The main result is that if you get a notebook from someone else with fancy dynamic javascript output, you might see things like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&amp;lt;Javascript uninteresting repr...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;instead of the fancy visualization. Once you either execute the notebook yourself or explicitly trust the notebook from the File menu, you can see the real results.&lt;/p&gt;
&lt;p&gt;When loading output from an untrusted notebook, we take one of three actions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the output is &lt;em&gt;unsafe&lt;/em&gt;, and ignored (Javascript)&lt;/li&gt;
&lt;li&gt;the output is &lt;em&gt;potentially unsafe&lt;/em&gt;, and sanitized (HTML)&lt;/li&gt;
&lt;li&gt;the output is &lt;em&gt;safe&lt;/em&gt;, and displayed regardless of trust&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It’s that third option of “safe” outputs where things went a bit awry. Safe outputs should be outputs that cannot run code in the browser. This includes plain-text output, which we reliably escape, and raster images, such as pngs and jpegs. So far, so good. It also includes latex, which &lt;em&gt;ought&lt;/em&gt; to be like plain text, in that it’s properly escaped before being put on the page to be rendered by &lt;a href="https://www.mathjax.org"&gt;MathJax&lt;/a&gt;. This is where we went wrong. We were putting latex output on the page with jQuery’s &lt;a href="https://api.jquery.com/append/"&gt;&lt;code&gt;$.append&lt;/code&gt;&lt;/a&gt;, which puts elements on the page verbatim. In this way, if the output that claimed to be latex was actually HTML with script tags and such, it would be able to execute code. The &lt;em&gt;right&lt;/em&gt; thing to do (all along) was to use the safe &lt;a href="https://api.jquery.com/text/"&gt;&lt;code&gt;$.text&lt;/code&gt;&lt;/a&gt;, which escapes HTML entities before putting things on the page.&lt;/p&gt;
&lt;p&gt;So that’s &lt;a href="https://github.com/jupyter/notebook/commit/d7fd3e2803afec591abbb3dc32eeab00fa095207"&gt;the fix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’m a bit surprised that we didn’t have bugs caused by inequalities and such in equations being mistakenly interpreted as HTML tags.&lt;/p&gt;
&lt;p&gt;See &lt;a href="http://jupyter-notebook.readthedocs.io/en/latest/security.html"&gt;our security docs&lt;/a&gt; for further details on the security model in Jupyter Notebooks.&lt;/p&gt;
&lt;p&gt;If you find a security issue with the notebook or other Jupyter components, please let us know at &lt;a href="http://blog.jupyter.org/cdn-cgi/l/email-protection"&gt;[email protected]&lt;/a&gt;&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="releases"/><category term="security"/></entry><entry><title>Notebook 4.2</title><link href="https://jasongrout.github.io/medium-archive/pelican/posts/2016/notebook-4-2/" rel="alternate"/><published>2016-04-15T20:31:00+00:00</published><updated>2016-04-16T05:03:00+00:00</updated><author><name>Min RK</name></author><id>tag:jasongrout.github.io,2016-04-15:/medium-archive/pelican/posts/2016/notebook-4-2/</id><summary type="html">&lt;p&gt;We have just released version 4.2 of the Jupyter Notebook. The Jupyter Notebook is a web application that allows you to create and share documents that contain live code, equations, visualizations and explanatory text (see the Jupyter project home page for more information). Version 4.2 of the notebook&lt;/p&gt;
</summary><content type="html">&lt;p&gt;We have just released version 4.2 of the &lt;a href="http://jupyter.org/"&gt;Jupyter&lt;/a&gt; Notebook. The Jupyter Notebook is a web application that allows you to create and share documents that contain live code, equations, visualizations and explanatory text (see the Jupyter project &lt;a href="http://jupyter.org"&gt;home page&lt;/a&gt; for more information).&lt;/p&gt;
&lt;p&gt;Version 4.2 of the notebook has many bugfixes and improvements. You can see a summary of changes &lt;a href="http://jupyter-notebook.readthedocs.org/en/latest/changelog.html#release-4-2-0"&gt;in the docs&lt;/a&gt;, or see all the changes &lt;a href="https://github.com/jupyter/notebook/pulls?q=is%3Apr+is%3Aclosed+milestone%3A4.2"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Altogether, 21 authors contributed to 53 pull requests to make this release.&lt;/p&gt;
&lt;p&gt;The notebook extension system was improved greatly in version 4.2. The notebook can be extended in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;nbextensions&lt;/em&gt;, which are JavaScript extensions that run in your browser. These can modify the UI or behavior of the notebook, such as adding buttons to your toolbar, new keyboard shortcuts, etc.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;serverextensions&lt;/em&gt;, which modify the Python webserver. These can add new HTTP endpoints, register extra events to run when you save your notebook, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The improvements to the extension system are focused on making installing and enabling extensions easier, including:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;better symmetry between handling of serverextensions and nbextensions&lt;/li&gt;
&lt;li&gt;allowing enabling nbextensions system-wide&lt;/li&gt;
&lt;li&gt;working better with the Python &lt;code&gt;sys.prefix&lt;/code&gt; for installing/enabling extensions using packages and environments, such as conda packages or python wheels&lt;/li&gt;
&lt;li&gt;allowing installation via Python packages&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In general, installing and enabling extensions is a two-step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;install the extension:&lt;br&gt;
&lt;code&gt;jupyter nbextension install /path/to/myextension&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;enable the extension&lt;br&gt;
&lt;code&gt;jupyter nbextension enable myextension/main&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Installation is system-wide by default, but you can specify an installation to be just for you with &lt;code&gt;--user&lt;/code&gt;. Enabling an extension is per-user by default, but you can enable an extension system-wide with &lt;code&gt;--system&lt;/code&gt;. You can install or enable an extension using the Python &lt;code&gt;sys.prefix&lt;/code&gt; by using the &lt;code&gt;--sys-prefix&lt;/code&gt; option available in either command. For example, to make a conda package that includes an extension, you should install the extension using &lt;code&gt;--sys-prefix&lt;/code&gt; when building the package.&lt;/p&gt;
&lt;p&gt;Python packages can specify information about what nbextensions and serverextensions they provide. These can be installed with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;jupyter&lt;/span&gt; &lt;span class="n"&gt;nbextension&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;packagename&lt;/span&gt;  
&lt;span class="n"&gt;jupyter&lt;/span&gt; &lt;span class="n"&gt;serverextension&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="n"&gt;packagename&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more information on installing extensions from Python packages, &lt;a href="https://jupyter-notebook.readthedocs.org/en/stable/examples/Notebook/rstversions/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.html"&gt;see the docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Edit: updated to add a link to the main Jupyter webpage, based on feedback from &lt;a href="https://news.ycombinator.com/item?id=11507454"&gt;HackerNews&lt;/a&gt;. Thanks!&lt;/em&gt;&lt;/p&gt;
</content><category term="Jupyter Notebook"/><category term="releases"/></entry></feed>