index.html 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <!DOCTYPE html>
  2. <html lang="en-us">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="generator" content="Hugo 0.92.0" />
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <link rel="stylesheet" href="/assets/css/theme.css">
  8. <link rel="alternate" href="/rss.xml" type="application/rss+xml" title="Pleasant Programmer">
  9. <script type="text/javascript" src="//use.typekit.net/iwm5axp.js"></script>
  10. <script type="text/javascript">try{Typekit.load();}catch(e){}</script>
  11. <title>Pleasant Programmer</title>
  12. </head>
  13. <body>
  14. <header id="header" role="banner">
  15. <div id="thomas">
  16. <img src="/assets/img/thomas.gif" alt="DJ THOMAS IN DA HAUS">
  17. <img src="/assets/img/thomas.png" alt="Pleasant Programmer">
  18. </div>
  19. <h1 class="site-title"><a href="/">Pleasant Programmer</a></h1>
  20. <nav id="menu" role="navigation">
  21. <ul>
  22. <li><a href="/pages/projects.html">projects</a></li>
  23. <li><a href="/posts.html">archives</a></li>
  24. <li><a href="/tags.html">tags</a></li>
  25. <li><a href="/rss.xml">rss</a></li>
  26. </ul>
  27. </nav>
  28. </header>
  29. <div id="container">
  30. <main id="content" role="main">
  31. <div class="postindex">
  32. <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
  33. <header>
  34. <h1 class="p-name entry-title" itemprop="headline">
  35. <a href="/posts/ssh-git-repo-docker.html" class="u-url">SSH Access to Git Repository in Docker</a>
  36. </h1>
  37. </header>
  38. <div class="e-content entry-content">
  39. <p>With the likes of <a href="https://gogs.io/">Gogs</a> and <a href="https://gitea.io">Gitea</a>,
  40. self-hosting a personal git service has become quite common. It&rsquo;s also not
  41. unlikely that the software is run via docker but this brings a problem with
  42. regards to SSH access.</p>
  43. <p>Ideally, the git service container just exposes port 22 but this would conflict
  44. with the host&rsquo;s own SSH service. One solution would be to just use different
  45. ports for the git SSH and host SSH and that&rsquo;s perfectly fine. But we can also
  46. just have the host SSH service forward the request to the git service itself
  47. using the <code>command</code> option in <code>authorized_keys</code>. And as we&rsquo;ll find out later,
  48. the git service itself is using this functionality.</p>
  49. <h2 id="how-git-over-ssh-works">How git over SSH works</h2>
  50. <p>When you do a <code>git push</code>, what actually happens is it runs <code>git-send-pack</code> which
  51. runs <code>git-receive-pack &lt;directory&gt;</code> on the remote using SSH. The actual
  52. communication then just simply happens via stdin/stdout. Conversely, doing a
  53. <code>git pull</code> just runs <code>git-fetch-pack</code> and the somewhat confusingly named
  54. <code>git-upload-pack</code> on the remote.</p>
  55. <p>So far so good, but did you notice that when cloning via SSH, the remote is
  56. typically <code>git@github.com:org/repo</code>? If everyone SSHs in as the <code>git</code> user, how
  57. does the git service know which user is which? And how does it prevent users
  58. from accessing each other&rsquo;s repositories?</p>
  59. <p>One typically thinks of <code>authorized_keys</code> as just a list of allowed SSH keys but
  60. it can do <a href="https://linux.die.net/man/8/sshd">much more than that</a>. Of particular
  61. interest is the <code>command</code> directive which gets run instead of the user supplied
  62. command. The original command is passed in as an environment variable
  63. <code>SSH_ORIGINAL_COMMAND</code> which can be used to check if we allow it to be run or
  64. not.</p>
  65. <p>So with an <code>authorized_keys</code> file like so:</p>
  66. <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=&#34;verify-user user-1&#34; ssh-rsa ...
  67. command=&#34;verify-user user-1&#34; ssh-ed25519 ...
  68. command=&#34;verify-user user-2&#34; ssh-rsa ...
  69. command=&#34;verify-user user-3&#34; ssh-rsa ...
  70. </code></pre></div><p>Each key is tied to a particular user by virtue of <code>command</code>. And <code>verify-user</code>
  71. can check the <code>SSH_ORIGINAL_COMMAND</code> if the particular user is allowed access to
  72. the particular repository. You can also implement additional restrictions like
  73. pull-only or push-only permissions with this setup. This is how both Gogs and
  74. Gitea work.</p>
  75. <h2 id="forwarding-git-ssh-to-docker">Forwarding git SSH to Docker</h2>
  76. <p>When running Gogs on the host, it&rsquo;s typically run as the <code>git</code> user and when an
  77. SSH key is added or removed, it simply rewrites <code>~git/.ssh/authorized_keys</code>.
  78. Thus it just works with the host SSH service without problems. When running
  79. inside docker, one thing we can do is bind mount the host&rsquo;s <code>~git/.ssh</code> folder
  80. into the docker container so that the host can authorize the SSH connections.</p>
  81. <p>The problem lies with the <code>command</code> which only exists inside the docker
  82. container itself. So any user trying to connect can authenticate successfully
  83. but will get a <code>command not found</code> error. For the Gogs docker image, the command
  84. looks like <code>/app/gogs/gogs serv key-1</code>. So we can just make <code>/app/gogs/gogs</code>
  85. available on the host and forward the command to the docker container.</p>
  86. <p><a href="https://docs.gitea.io/en-us/install-with-docker/#ssh-container-passthrough">Most</a>
  87. <a href="http://www.ateijelo.com/blog/2016/07/09/share-port-22-between-docker-gogs-ssh-and-local-system">instructions</a>
  88. I&rsquo;ve seen with regards to this involves using <code>ssh</code> to connect to the internal
  89. docker SSH service but this just seems overly complicated to me. If you
  90. remember, at it&rsquo;s core git really only communicates over stdin/stdout and SSH is
  91. just a means to get that.</p>
  92. <p>If all we need is for our shim <code>/app/gogs/gogs</code> to be able to run a command
  93. inside the docker container with stdin/stdout attached, then we can actually
  94. just do that with <code>docker exec</code>. So it can be something like this:</p>
  95. <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
  96. </span><span style="color:#0f0;font-weight:bold"></span>
  97. <span style="color:#007f7f"># Requires the following in sudoers</span>
  98. <span style="color:#007f7f"># git ALL=(ALL) NOPASSWD: /app/gogs/gogs</span>
  99. <span style="color:#007f7f"># Defaults:git env_keep=SSH_ORIGINAL_COMMAND</span>
  100. GOGS_CONTAINER=git-gogs-1
  101. <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>
  102. <span style="color:#fff;font-weight:bold">exec</span> sudo <span style="color:#0ff;font-weight:bold">&#34;</span>$0<span style="color:#0ff;font-weight:bold">&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;</span>$@<span style="color:#0ff;font-weight:bold">&#34;</span>
  103. <span style="color:#fff;font-weight:bold">fi</span>
  104. <span style="color:#fff;font-weight:bold">if</span> [ <span style="color:#0ff;font-weight:bold">&#34;</span>$1<span style="color:#0ff;font-weight:bold">&#34;</span> != <span style="color:#0ff;font-weight:bold">&#34;serv&#34;</span> ]; <span style="color:#fff;font-weight:bold">then</span>
  105. <span style="color:#fff;font-weight:bold">exit</span> <span style="color:#ff0;font-weight:bold">1</span>
  106. <span style="color:#fff;font-weight:bold">fi</span>
  107. <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">&#34;SSH_ORIGINAL_COMMAND=</span>$SSH_ORIGINAL_COMMAND<span style="color:#0ff;font-weight:bold">&#34;</span> <span style="color:#0ff;font-weight:bold">&#34;</span>$GOGS_CONTAINER<span style="color:#0ff;font-weight:bold">&#34;</span> /app/gogs/gogs <span style="color:#0ff;font-weight:bold">&#34;</span>$@<span style="color:#0ff;font-weight:bold">&#34;</span>
  108. </code></pre></div><p>So for git SSH access to Gogs running in docker, the necessary steps here are:</p>
  109. <ol>
  110. <li>Have a <code>git</code> user on the host</li>
  111. <li>Bind mount <code>~git/.ssh</code> to <code>/data/git/.ssh</code> in the Gogs container</li>
  112. <li>Add the shim script to <code>/app/gogs/gogs</code> (make sure it&rsquo;s owned by root and
  113. is chmod-ed <code>0755</code>)</li>
  114. <li>Add the listed sudoers rules</li>
  115. </ol>
  116. </div>
  117. <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2022-01-23">2022-01-23</time></small>
  118. | <small class="commentline"><a href="/posts/ssh-git-repo-docker.html#isso-thread">Comments</a></small>
  119. </article>
  120. </article>
  121. <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
  122. <header>
  123. <h1 class="p-name entry-title" itemprop="headline">
  124. <a href="/posts/android-multisim.html" class="u-url">Android Multisim Pre-5.1</a>
  125. </h1>
  126. </header>
  127. <div class="e-content entry-content">
  128. <p><strong>NOTE</strong> if you&rsquo;re just looking for a library to use, there&rsquo;s <a href="https://github.com/UseHover/MultiSim">MultiSim</a>. I&rsquo;ve never used this so I can&rsquo;t guarantee anything about it. It also only supports SIM information and not SMS.</p>
  129. <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>
  130. <p>Android&rsquo;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>
  131. <h2 id="mediatek">Mediatek</h2>
  132. <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>
  133. <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>
  134. <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>
  135. <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>
  136. <h2 id="intel">Intel</h2>
  137. <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&rsquo;t have a library to use here and had to use reflection to find the hidden classes / methods.</p>
  138. <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>
  139. <h2 id="qualcomm">Qualcomm</h2>
  140. <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&rsquo;s interesting about this one is that the API completely changed in the upgrade from 4.4 to 5.0.</p>
  141. <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>
  142. <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>
  143. <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>
  144. <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&rsquo;s life.</p>
  145. <h2 id="epilogue">Epilogue</h2>
  146. <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&rsquo;s probably not all that&rsquo;s been out in the wild.</p>
  147. </div>
  148. <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2021-09-12">2021-09-12</time></small>
  149. | <small class="commentline"><a href="/posts/android-multisim.html#isso-thread">Comments</a></small>
  150. </article>
  151. </article>
  152. <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
  153. <header>
  154. <h1 class="p-name entry-title" itemprop="headline">
  155. <a href="/posts/isp-issues.html" class="u-url">ISP Issues</a>
  156. </h1>
  157. </header>
  158. <div class="e-content entry-content">
  159. <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&rsquo;t <em>too</em> expensive, we just went and got both.</p>
  160. <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 &ldquo;up&rdquo; according to our monitoring.</p>
  161. <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>
  162. <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 &ldquo;business line&rdquo; 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>
  163. <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>
  164. </div>
  165. <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2018-08-16">2018-08-16</time></small>
  166. | <small class="commentline"><a href="/posts/isp-issues.html#isso-thread">Comments</a></small>
  167. </article>
  168. </article>
  169. <article class="h-entry post-text" itemscope itemtype="http://schema.org/Blog">
  170. <header>
  171. <h1 class="p-name entry-title" itemprop="headline">
  172. <a href="/posts/audventure.html" class="u-url">Audventure</a>
  173. </h1>
  174. </header>
  175. <div class="e-content entry-content">
  176. <p>Sometime around 2013 I wrote a clone of the GBA game <a href="https://www.nintendo.co.jp/n08/bit_g/">bit Generations
  177. SoundVoyager</a> called
  178. <a href="https://audventure.pleasantprogrammer.com">audventure</a>. SoundVoyager is
  179. actually a collection of mini-games where sound is the main focus. You can
  180. actually play the game blind, and at some point, that&rsquo;s pretty much what
  181. happens.</p>
  182. <h2 id="sound-catcher">sound catcher</h2>
  183. <p>The signature mini-game in SoundVoyager is sound catcher. In the mini-game, you
  184. can only move left and right at the bottom of the stage, while a &ldquo;sound&rdquo; falls
  185. from the top. Your goal is to catch the sound which is signified by a green dot.
  186. When you catch it, the sound or beat becomes part of the BGM and a new dot
  187. appears with a different sound.</p>
  188. <p>You can of course use your eyes and move accordingly, but if you put on
  189. earphones, you can actually hear where the dot is, either on your left or right,
  190. with it getting louder as it gets close to you. As you collect more sounds, the
  191. dot gets more and more transparent. Eventually (and this is where it gets fun),
  192. you won&rsquo;t be able to see the sounds anymore and will have to rely mostly on your
  193. ears.</p>
  194. <p>You can see what the original game looks like in <a href="https://www.youtube.com/watch?v=C12WRgfIOC8">this
  195. video</a> or you can play it under
  196. <em>sound safari</em> in <a href="https://audventure.pleasantprogrammer.com">audventure</a>.</p>
  197. <h2 id="webaudio-vs-flash">WebAudio vs Flash</h2>
  198. <p>At the time I wrote audventure, only Chrome supported WebAudio. Also, the API
  199. looked (and still looks) quite complicated. Flash on the other hand, was
  200. starting to die, but still well-supported so I went with that. For the most
  201. part, it worked okay though Chrome actually had timing issues when playing
  202. sounds. Now, it doesn&rsquo;t work in any browser. I tried to debug the issues but
  203. ultimately ended up just rewriting it to use WebAudio instead.</p>
  204. <p>For the game, I needed to simulate the source of the sound in 2D/3D space. Flash
  205. only really gives you stereo panning and volume control. With some maths, we can
  206. actually get an acceptable solution. Less importantly, I needed to be able to
  207. get frequency data of the currently playing &ldquo;sound&rdquo; to pulse the background. For
  208. this, I actually had to implement the feature in the Flash library I was using.</p>
  209. <p>With WebAudio, spatial audio is already built-in and you can simply give it the
  210. coordinates of the sounds and the listener. There are some other options to
  211. tweak, but for the most part, no complex math is needed. Getting frequency data
  212. for a sound is also actually built-in and didn&rsquo;t take too long to integrate.</p>
  213. <p>Overall, I was impressed by how much you can do with WebAudio out-of-the-box. I
  214. kind of understand why it&rsquo;s complicated, but there&rsquo;s some simple functionality
  215. that I wish was included. For example, there is no API to pause and then resume
  216. playing an audio buffer. You have to manually save the elapsed time and play
  217. from there.</p>
  218. <h2 id="other-mini-games">Other mini-games</h2>
  219. <p>So far I&rsquo;ve only actually implemented the sound catcher mini-game. There are
  220. around 4 different categories with slight variations in between.</p>
  221. <h3 id="sound-catcher--sound-slalom">sound catcher / sound slalom</h3>
  222. <p>I&rsquo;ve explained sound catcher a while ago; sound slalom is a minor variation on
  223. that. Instead of waiting for the &ldquo;sound&rdquo; to reach you, you now have to guide
  224. yourself in between 2 &ldquo;poles&rdquo; of sound, as in <a href="https://en.wikipedia.org/wiki/Slalom_skiing">slalom
  225. skiing</a>. But this time, you can
  226. also accelerate forward. The goal is to finish the course before the time runs
  227. out.</p>
  228. <h3 id="sound-drive--sound-chase">sound drive / sound chase</h3>
  229. <p>In sound drive, you&rsquo;re driving against the flow on a 5 lane road. You have to
  230. avoid oncoming cars, trucks and animals until you reach the end. You&rsquo;re allowed
  231. to change lanes and accelerate, and the game tracks your best time. Sound chase
  232. is pretty much the same, except you&rsquo;re trying to catch up to a &ldquo;sound&rdquo;.</p>
  233. <h3 id="sound-cannon">sound cannon</h3>
  234. <p>In sound cannon, you&rsquo;re immobile but can rotate within a 180 degree angle. Your
  235. goal is too shoot down &ldquo;sounds&rdquo; which are heading your way. If a sound reaches
  236. you, it&rsquo;s game over. You win when you kill all the sounds.</p>
  237. <h3 id="sound-picker--sound-cock">sound picker / sound cock</h3>
  238. <p>In sound picker, you can move in a giant square field where various sounds are
  239. scattered around. Your goal is to pick up all the sounds within the time limit.
  240. Sound cock is similar, except the sounds are chickens and you have to chase them
  241. around.</p>
  242. <h2 id="source-code">Source Code</h2>
  243. <p>If you want to see the source code, you can check it out
  244. <a href="https://git.pleasantprogrammer.com/games/audventure">here</a>. The sound files
  245. aren&rsquo;t in the repo though, since I&rsquo;m not quite sure about the licensing. If you
  246. want to contribute music or sound effects, I&rsquo;d gladly appreciate it.</p>
  247. </div>
  248. <small class="dateline">Posted: <time class="published dt-published" itemprop="datePublished" datetime="2017-11-19">2017-11-19</time></small>
  249. | <small class="commentline"><a href="/posts/audventure.html#isso-thread">Comments</a></small>
  250. </article>
  251. </article>
  252. </div>
  253. <nav class="postindexpager">
  254. <ul class="pager clearfix">
  255. <li class="next">
  256. <a href="/page/2.html">Older posts &rarr;</a>
  257. </li>
  258. </ul>
  259. </nav>
  260. </main>
  261. <footer id="footer" role="contentinfo">
  262. <p>
  263. <a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/deed.en_US">
  264. <img alt="CC-BY-SA" style="border-width:0" src="https://licensebuttons.net/l/by-sa/3.0/80x15.png">
  265. </a> &copy; 2022 Thomas Dy - Powered by <a href="http://gohugo.io">Hugo</a></p>
  266. </footer>
  267. </div>
  268. <script src="/assets/js/konami.js"></script>
  269. <script>
  270. var easter_egg = new Konami();
  271. easter_egg.code = function() {
  272. var el = document.getElementById('thomas');
  273. if(el.className == "whoa") {
  274. el.className = "";
  275. }
  276. else {
  277. el.className = "whoa";
  278. }
  279. document.body.scrollTop = document.documentElement.scrollTop = 0;
  280. }
  281. easter_egg.load();
  282. </script>
  283. <script
  284. data-isso="https://isso.pleasantprogrammer.com/"
  285. src="https://isso.pleasantprogrammer.com/js/count.min.js">
  286. </script>
  287. </body>
  288. </html>