123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- <!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>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">
- <div class="postindex">
-
- <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
- <header>
- <h1 class="p-name entry-title" itemprop="headline">
- <a href="/posts/ssh-git-repo-docker.html" class="u-url">SSH Access to Git Repository in Docker</a>
- </h1>
- </header>
- <div class="e-content entry-content">
- <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>
- <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2022-01-23">2022-01-23</time></small>
- | <small class="commentline"><a href="/posts/ssh-git-repo-docker.html#isso-thread">Comments</a></small>
- </article>
- </article>
-
- <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
- <header>
- <h1 class="p-name entry-title" itemprop="headline">
- <a href="/posts/android-multisim.html" class="u-url">Android Multisim Pre-5.1</a>
- </h1>
- </header>
- <div class="e-content entry-content">
- <p><strong>NOTE</strong> if you’re just looking for a library to use, there’s <a href="https://github.com/UseHover/MultiSim">MultiSim</a>. I’ve never used this so I can’t guarantee anything about it. It also only supports SIM information and not SMS.</p>
- <p>Phones that can take multiple SIM cards are quite popular in the Philippines. The two major telecoms would have unlimited SMS packages for messages within their networks. It was quite common to have a SIM for each telco and use the appropriate one depending on who you were sending to.</p>
- <p>Android’s API only officially supported multiple SIM cards in 5.1 (API level 22) but Android phones with dual-SIM (and even triple-SIM) capabilities were already available at least as far back as 2.3 (API level 10) when I first needed to support it. Since there was no official API for this, the manufacturers just invented their own and of course each one implemented it in a different way.</p>
- <h2 id="mediatek">Mediatek</h2>
- <p>The first phone we started working on was a <a href="https://www.gsmarena.com/lenovo_a60-4711.php">Lenovo A60</a> which used a Mediatek SOC. We somehow got a library from the manufacturer that let us use the dual-SIM functionality, but it was quite a pain to get working as there was limited documentation and we were quite new to Android development at the time.</p>
- <p>When we disassembled the library that they gave us, we noticed that the names they used for the additional functions were quite interesting. They were all the <code>TelephonyManager</code> and <code>SmsManager</code> methods with a <code>Gemini</code> suffix and they would take an additional <code>int</code> parameter in addition to the original.</p>
- <p>It turned out that these were available on the standard <code>TelephonyManager</code> instance and could be accessed via reflection. The <code>SmsManager</code> was a bit trickier but we ended up figuring out that there was a <code>android.telephony.gemini.GeminiSmsManager</code> class that had the functionality.</p>
- <p>In a different phone with a Mediatek SOC, this got renamed to <code>com.mediatek.telephony.gemini.SmsManager</code> for some reason and dropped the <code>Gemini</code> suffix only for the <code>SmsManager</code>.</p>
- <h2 id="intel">Intel</h2>
- <p>It was also around this time that Intel started making SOCs for smartphones. We had an <a href="https://www.gsmarena.com/asus_fonepad_7_%282014%29-6394.php">ASUS Fonepad 7</a>. Unlike with the Mediatek device, we didn’t have a library to use here and had to use reflection to find the hidden classes / methods.</p>
- <p>What we found was that instead of having a single instance with every method taking a <code>sim</code> parameter, they instead had separate instances of <code>TelephonyManager</code> and <code>SmsManager</code> for each SIM. You would call <code>TelephonyManager.get2ndTm()</code> and <code>SmsManager.get2ndSmsManager()</code> to have access to the 2nd SIM.</p>
- <h2 id="qualcomm">Qualcomm</h2>
- <p>The last phone I looked at was a <a href="https://www.gsmarena.com/motorola_moto_g_dual_sim-5978.php">dual-SIM Moto G</a>. What’s interesting about this one is that the API completely changed in the upgrade from 4.4 to 5.0.</p>
- <p>On Android 4.4, the API was pretty close to the Mediatek one. You had a single instance that could dispatch to other SIMs by having an extra parameter on all the methods. These were in <code>android.telephony.MSimTelephonyManager</code> and <code>android.telephony.MSimSmsManager</code>.</p>
- <p>On Android 5.0, the API was a weird mix of all the above and also the introduction of <code>android.telephony.SubscriptionManager</code> which was quite close but not exactly the same as what ended up in the official API. Instead of <code>getActiveSubscriptionInfoList</code> there was <code>getActiveSubIdList</code> which only returned <code>long[]</code>.</p>
- <p>For the information that would normally exist in <code>SubscriptionInfo</code>, you had to query the main <code>TelephonyManager</code> instance which had methods with an extra <code>long</code> parameter for the subscription id. The <code>SmsManager</code> was simpler with just <code>getSmsManagerForSubscriber</code>.</p>
- <p>With Android 5.1, I assume they just switched to using the official API so this phone would have gone through 3 different multi-SIM APIs over the course of it’s life.</p>
- <h2 id="epilogue">Epilogue</h2>
- <p>Around the release of Android 5.1, we stopped work on the app so I never actually got to use the official API myself ironically. We also never really got a big deployment so while I saw quite the variety of multi-SIM implementations, that’s probably not all that’s been out in the wild.</p>
- </div>
- <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2021-09-12">2021-09-12</time></small>
- | <small class="commentline"><a href="/posts/android-multisim.html#isso-thread">Comments</a></small>
- </article>
- </article>
-
- <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
- <header>
- <h1 class="p-name entry-title" itemprop="headline">
- <a href="/posts/isp-issues.html" class="u-url">ISP Issues</a>
- </h1>
- </header>
- <div class="e-content entry-content">
- <p>At the first office I worked at, we had 2 different ISPs. This was supposed to be for reliability, as one was fast but spotty, and the other was slow but reliable. Since they weren’t <em>too</em> expensive, we just went and got both.</p>
- <p>We have monitoring setup to watch our office IPs from the outside so we could see how often the connection goes down. The interesting thing we found was that the fast and spotty connection had perfect uptime. Even when there was clearly no internet from the office, it was still “up” according to our monitoring.</p>
- <p>So we tried pinging our office IP using the other connection and to our surprise it was indeed up. There was even a webserver running on it (we only have VPN exposed). Apparently, it was someone elses CCTV admin page. We could actually see a hallway with people walking by sometimes!</p>
- <p>Apparently someone else had our IP address and nothing good comes from an IP conflict. This was completely baffling as our internet line was supposed to be a “business line” and that came with a static IP address. So the only scenarios where this could happen is, the ISP mistakenly gave the same IP to 2 different lines or the ISP allows some clients to freely set their own IP.</p>
- <p>We complained to the ISP and eventually got it resolved. They just gave us an entirely new IP address, but they never explained what went wrong. We already had quite a negative opinion of that particular ISP though, and they somehow managed to outdo themselves.</p>
- </div>
- <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2018-08-16">2018-08-16</time></small>
- | <small class="commentline"><a href="/posts/isp-issues.html#isso-thread">Comments</a></small>
- </article>
- </article>
-
- <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
- <header>
- <h1 class="p-name entry-title" itemprop="headline">
- <a href="/posts/audventure.html" class="u-url">Audventure</a>
- </h1>
- </header>
- <div class="e-content entry-content">
- <p>Sometime around 2013 I wrote a clone of the GBA game <a href="https://www.nintendo.co.jp/n08/bit_g/">bit Generations
- SoundVoyager</a> called
- <a href="https://audventure.pleasantprogrammer.com">audventure</a>. SoundVoyager is
- actually a collection of mini-games where sound is the main focus. You can
- actually play the game blind, and at some point, that’s pretty much what
- happens.</p>
- <h2 id="sound-catcher">sound catcher</h2>
- <p>The signature mini-game in SoundVoyager is sound catcher. In the mini-game, you
- can only move left and right at the bottom of the stage, while a “sound” falls
- from the top. Your goal is to catch the sound which is signified by a green dot.
- When you catch it, the sound or beat becomes part of the BGM and a new dot
- appears with a different sound.</p>
- <p>You can of course use your eyes and move accordingly, but if you put on
- earphones, you can actually hear where the dot is, either on your left or right,
- with it getting louder as it gets close to you. As you collect more sounds, the
- dot gets more and more transparent. Eventually (and this is where it gets fun),
- you won’t be able to see the sounds anymore and will have to rely mostly on your
- ears.</p>
- <p>You can see what the original game looks like in <a href="https://www.youtube.com/watch?v=C12WRgfIOC8">this
- video</a> or you can play it under
- <em>sound safari</em> in <a href="https://audventure.pleasantprogrammer.com">audventure</a>.</p>
- <h2 id="webaudio-vs-flash">WebAudio vs Flash</h2>
- <p>At the time I wrote audventure, only Chrome supported WebAudio. Also, the API
- looked (and still looks) quite complicated. Flash on the other hand, was
- starting to die, but still well-supported so I went with that. For the most
- part, it worked okay though Chrome actually had timing issues when playing
- sounds. Now, it doesn’t work in any browser. I tried to debug the issues but
- ultimately ended up just rewriting it to use WebAudio instead.</p>
- <p>For the game, I needed to simulate the source of the sound in 2D/3D space. Flash
- only really gives you stereo panning and volume control. With some maths, we can
- actually get an acceptable solution. Less importantly, I needed to be able to
- get frequency data of the currently playing “sound” to pulse the background. For
- this, I actually had to implement the feature in the Flash library I was using.</p>
- <p>With WebAudio, spatial audio is already built-in and you can simply give it the
- coordinates of the sounds and the listener. There are some other options to
- tweak, but for the most part, no complex math is needed. Getting frequency data
- for a sound is also actually built-in and didn’t take too long to integrate.</p>
- <p>Overall, I was impressed by how much you can do with WebAudio out-of-the-box. I
- kind of understand why it’s complicated, but there’s some simple functionality
- that I wish was included. For example, there is no API to pause and then resume
- playing an audio buffer. You have to manually save the elapsed time and play
- from there.</p>
- <h2 id="other-mini-games">Other mini-games</h2>
- <p>So far I’ve only actually implemented the sound catcher mini-game. There are
- around 4 different categories with slight variations in between.</p>
- <h3 id="sound-catcher--sound-slalom">sound catcher / sound slalom</h3>
- <p>I’ve explained sound catcher a while ago; sound slalom is a minor variation on
- that. Instead of waiting for the “sound” to reach you, you now have to guide
- yourself in between 2 “poles” of sound, as in <a href="https://en.wikipedia.org/wiki/Slalom_skiing">slalom
- skiing</a>. But this time, you can
- also accelerate forward. The goal is to finish the course before the time runs
- out.</p>
- <h3 id="sound-drive--sound-chase">sound drive / sound chase</h3>
- <p>In sound drive, you’re driving against the flow on a 5 lane road. You have to
- avoid oncoming cars, trucks and animals until you reach the end. You’re allowed
- to change lanes and accelerate, and the game tracks your best time. Sound chase
- is pretty much the same, except you’re trying to catch up to a “sound”.</p>
- <h3 id="sound-cannon">sound cannon</h3>
- <p>In sound cannon, you’re immobile but can rotate within a 180 degree angle. Your
- goal is too shoot down “sounds” which are heading your way. If a sound reaches
- you, it’s game over. You win when you kill all the sounds.</p>
- <h3 id="sound-picker--sound-cock">sound picker / sound cock</h3>
- <p>In sound picker, you can move in a giant square field where various sounds are
- scattered around. Your goal is to pick up all the sounds within the time limit.
- Sound cock is similar, except the sounds are chickens and you have to chase them
- around.</p>
- <h2 id="source-code">Source Code</h2>
- <p>If you want to see the source code, you can check it out
- <a href="https://git.pleasantprogrammer.com/games/audventure">here</a>. The sound files
- aren’t in the repo though, since I’m not quite sure about the licensing. If you
- want to contribute music or sound effects, I’d gladly appreciate it.</p>
- </div>
- <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2017-11-19">2017-11-19</time></small>
- | <small class="commentline"><a href="/posts/audventure.html#isso-thread">Comments</a></small>
- </article>
- </article>
-
- </div>
- <nav class="postindexpager">
- <ul class="pager clearfix">
-
-
- <li class="next">
- <a href="/page/2.html">Older posts →</a>
- </li>
-
- </ul>
- </nav>
- </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>
- <script
- data-isso="https://isso.pleasantprogrammer.com/"
- src="https://isso.pleasantprogrammer.com/js/count.min.js">
- </script>
- </body>
- </html>
|