123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187 |
- <!DOCTYPE html>
- <html lang="en-us">
- <head>
- <meta charset="utf-8">
- <meta name="generator" content="Hugo 0.92.0" />
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" href="/assets/css/theme.css">
- <link rel="alternate" href="/rss.xml" type="application/rss+xml" title="Pleasant Programmer">
- <script type="text/javascript" src="//use.typekit.net/iwm5axp.js"></script>
- <script type="text/javascript">try{Typekit.load();}catch(e){}</script>
- <title>SSH Access to Git Repository in Docker - Pleasant Programmer</title>
- </head>
- <body>
- <header id="header" role="banner">
- <div id="thomas">
- <img src="/assets/img/thomas.gif" alt="DJ THOMAS IN DA HAUS">
- <img src="/assets/img/thomas.png" alt="Pleasant Programmer">
- </div>
- <h1 class="site-title"><a href="/">Pleasant Programmer</a></h1>
- <nav id="menu" role="navigation">
- <ul>
- <li><a href="/pages/projects.html">projects</a></li>
- <li><a href="/posts.html">archives</a></li>
- <li><a href="/tags.html">tags</a></li>
- <li><a href="/rss.xml">rss</a></li>
- </ul>
- </nav>
- </header>
- <div id="container">
- <main id="content" role="main">
- <article itemscope itemtype="http://schema.org/BlogPosting">
- <h1 class="p-name entry-title" itemprop="headline name">
- <a href="/posts/ssh-git-repo-docker.html">SSH Access to Git Repository in Docker</a></h1>
- <small>
- <span class="dateline">Posted: <time itemprop="datePublished" datetime="2022-01-23">2022-01-23</time></span>
- | More posts about
-
- <a class="tag p-category" href="/tags/sysadmin.html" rel="tag">
- sysadmin
- </a>
-
- </small>
- <div class="e-content entry-content" itemprop="entry-text">
- <p>With the likes of <a href="https://gogs.io/">Gogs</a> and <a href="https://gitea.io">Gitea</a>,
- self-hosting a personal git service has become quite common. It’s also not
- unlikely that the software is run via docker but this brings a problem with
- regards to SSH access.</p>
- <p>Ideally, the git service container just exposes port 22 but this would conflict
- with the host’s own SSH service. One solution would be to just use different
- ports for the git SSH and host SSH and that’s perfectly fine. But we can also
- just have the host SSH service forward the request to the git service itself
- using the <code>command</code> option in <code>authorized_keys</code>. And as we’ll find out later,
- the git service itself is using this functionality.</p>
- <h2 id="how-git-over-ssh-works">How git over SSH works</h2>
- <p>When you do a <code>git push</code>, what actually happens is it runs <code>git-send-pack</code> which
- runs <code>git-receive-pack <directory></code> on the remote using SSH. The actual
- communication then just simply happens via stdin/stdout. Conversely, doing a
- <code>git pull</code> just runs <code>git-fetch-pack</code> and the somewhat confusingly named
- <code>git-upload-pack</code> on the remote.</p>
- <p>So far so good, but did you notice that when cloning via SSH, the remote is
- typically <code>git@github.com:org/repo</code>? If everyone SSHs in as the <code>git</code> user, how
- does the git service know which user is which? And how does it prevent users
- from accessing each other’s repositories?</p>
- <p>One typically thinks of <code>authorized_keys</code> as just a list of allowed SSH keys but
- it can do <a href="https://linux.die.net/man/8/sshd">much more than that</a>. Of particular
- interest is the <code>command</code> directive which gets run instead of the user supplied
- command. The original command is passed in as an environment variable
- <code>SSH_ORIGINAL_COMMAND</code> which can be used to check if we allow it to be run or
- not.</p>
- <p>So with an <code>authorized_keys</code> file like so:</p>
- <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">command="verify-user user-1" ssh-rsa ...
- command="verify-user user-1" ssh-ed25519 ...
- command="verify-user user-2" ssh-rsa ...
- command="verify-user user-3" ssh-rsa ...
- </code></pre></div><p>Each key is tied to a particular user by virtue of <code>command</code>. And <code>verify-user</code>
- can check the <code>SSH_ORIGINAL_COMMAND</code> if the particular user is allowed access to
- the particular repository. You can also implement additional restrictions like
- pull-only or push-only permissions with this setup. This is how both Gogs and
- Gitea work.</p>
- <h2 id="forwarding-git-ssh-to-docker">Forwarding git SSH to Docker</h2>
- <p>When running Gogs on the host, it’s typically run as the <code>git</code> user and when an
- SSH key is added or removed, it simply rewrites <code>~git/.ssh/authorized_keys</code>.
- Thus it just works with the host SSH service without problems. When running
- inside docker, one thing we can do is bind mount the host’s <code>~git/.ssh</code> folder
- into the docker container so that the host can authorize the SSH connections.</p>
- <p>The problem lies with the <code>command</code> which only exists inside the docker
- container itself. So any user trying to connect can authenticate successfully
- but will get a <code>command not found</code> error. For the Gogs docker image, the command
- looks like <code>/app/gogs/gogs serv key-1</code>. So we can just make <code>/app/gogs/gogs</code>
- available on the host and forward the command to the docker container.</p>
- <p><a href="https://docs.gitea.io/en-us/install-with-docker/#ssh-container-passthrough">Most</a>
- <a href="http://www.ateijelo.com/blog/2016/07/09/share-port-22-between-docker-gogs-ssh-and-local-system">instructions</a>
- I’ve seen with regards to this involves using <code>ssh</code> to connect to the internal
- docker SSH service but this just seems overly complicated to me. If you
- remember, at it’s core git really only communicates over stdin/stdout and SSH is
- just a means to get that.</p>
- <p>If all we need is for our shim <code>/app/gogs/gogs</code> to be able to run a command
- inside the docker container with stdin/stdout attached, then we can actually
- just do that with <code>docker exec</code>. So it can be something like this:</p>
- <div class="highlight"><pre tabindex="0" style="color:#e5e5e5;background-color:#000;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell"><span style="color:#0f0;font-weight:bold">#!/usr/bin/env bash
- </span><span style="color:#0f0;font-weight:bold"></span>
- <span style="color:#007f7f"># Requires the following in sudoers</span>
- <span style="color:#007f7f"># git ALL=(ALL) NOPASSWD: /app/gogs/gogs</span>
- <span style="color:#007f7f"># Defaults:git env_keep=SSH_ORIGINAL_COMMAND</span>
- GOGS_CONTAINER=git-gogs-1
- <span style="color:#fff;font-weight:bold">if</span> [[ $EUID -ne <span style="color:#ff0;font-weight:bold">0</span> ]]; <span style="color:#fff;font-weight:bold">then</span>
- <span style="color:#fff;font-weight:bold">exec</span> sudo <span style="color:#0ff;font-weight:bold">"</span>$0<span style="color:#0ff;font-weight:bold">"</span> <span style="color:#0ff;font-weight:bold">"</span>$@<span style="color:#0ff;font-weight:bold">"</span>
- <span style="color:#fff;font-weight:bold">fi</span>
- <span style="color:#fff;font-weight:bold">if</span> [ <span style="color:#0ff;font-weight:bold">"</span>$1<span style="color:#0ff;font-weight:bold">"</span> != <span style="color:#0ff;font-weight:bold">"serv"</span> ]; <span style="color:#fff;font-weight:bold">then</span>
- <span style="color:#fff;font-weight:bold">exit</span> <span style="color:#ff0;font-weight:bold">1</span>
- <span style="color:#fff;font-weight:bold">fi</span>
- <span style="color:#fff;font-weight:bold">exec</span> docker <span style="color:#fff;font-weight:bold">exec</span> -i -u git -e <span style="color:#0ff;font-weight:bold">"SSH_ORIGINAL_COMMAND=</span>$SSH_ORIGINAL_COMMAND<span style="color:#0ff;font-weight:bold">"</span> <span style="color:#0ff;font-weight:bold">"</span>$GOGS_CONTAINER<span style="color:#0ff;font-weight:bold">"</span> /app/gogs/gogs <span style="color:#0ff;font-weight:bold">"</span>$@<span style="color:#0ff;font-weight:bold">"</span>
- </code></pre></div><p>So for git SSH access to Gogs running in docker, the necessary steps here are:</p>
- <ol>
- <li>Have a <code>git</code> user on the host</li>
- <li>Bind mount <code>~git/.ssh</code> to <code>/data/git/.ssh</code> in the Gogs container</li>
- <li>Add the shim script to <code>/app/gogs/gogs</code> (make sure it’s owned by root and
- is chmod-ed <code>0755</code>)</li>
- <li>Add the listed sudoers rules</li>
- </ol>
- </div>
- <aside class="postpromonav">
- <nav>
- <ul class="pager clearfix">
-
- <li class="previous">
- <a href="/posts/android-multisim.html" rel="prev" title="Android Multisim Pre-5.1">← Previous post</a>
- </li>
-
-
- </ul>
- </nav>
- </aside>
- <section class="comments">
- <script
- data-isso="https://isso.pleasantprogrammer.com/"
- data-isso-require-author="true"
- data-isso-vote="false"
- src="https://isso.pleasantprogrammer.com/js/embed.min.js">
- </script>
- <section id="isso-thread"></section>
- </section>
- </article>
- </main>
- <footer id="footer" role="contentinfo">
- <p>
- <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
- <img alt="CC-BY-SA" style="border-width:0" src="https://licensebuttons.net/l/by-sa/3.0/80x15.png">
- </a> © 2022 Thomas Dy - Powered by <a href="http://gohugo.io">Hugo</a></p>
- </footer>
- </div>
- <script src="/assets/js/konami.js"></script>
- <script>
- var easter_egg = new Konami();
- easter_egg.code = function() {
- var el = document.getElementById('thomas');
- if(el.className == "whoa") {
- el.className = "";
- }
- else {
- el.className = "whoa";
- }
- document.body.scrollTop = document.documentElement.scrollTop = 0;
- }
- easter_egg.load();
- </script>
- </body>
- </html>
|