selivan.github.io Just another IT blog https://selivan.github.io/ Sun, 08 Mar 2026 02:24:07 +0300 Sun, 08 Mar 2026 02:24:07 +0300 Jekyll v3.10.0 Execute script before starting service in docker-compose <p>Sometimes you need to execute shell commands before starting service in docker compose.</p> <p>You can create oneshot service using the image you need for those commands, entrypoint <code class="language-plaintext highlighter-rouge">/bin/sh</code> and shell commands in <code class="language-plaintext highlighter-rouge">command</code> option.</p> <p>YAML multi-line syntax allows to conviniently place the script inside <code class="language-plaintext highlighter-rouge">docker-compose.yml</code>. Use <code class="language-plaintext highlighter-rouge">set -x</code> to get verbose logs of script execution. Don’t forget that the last command should have exit code 0.</p> <p>Then you make the main service dependent on oneshot service, with option <code class="language-plaintext highlighter-rouge">condition: service_completed_successfully</code>. Unlike default dependency, this one requires service to successfully finish execution, not to be runnig. Other possible conditions are <code class="language-plaintext highlighter-rouge">service_started</code>(default) and <code class="language-plaintext highlighter-rouge">service_healthy</code>(healthcheck ok).</p> <p><code class="language-plaintext highlighter-rouge">docker-compose.yml</code>:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span> <span class="na">create-mysql-socket-dir</span><span class="pi">:</span> <span class="na">image</span><span class="pi">:</span> <span class="s2">"</span><span class="s">busybox:stable"</span> <span class="na">restart</span><span class="pi">:</span> <span class="s2">"</span><span class="s">no"</span> <span class="na">entrypoint</span><span class="pi">:</span> <span class="s">/bin/sh</span> <span class="na">command</span><span class="pi">:</span> <span class="pi">-</span> <span class="s2">"</span><span class="s">-c"</span> <span class="pi">-</span> <span class="pi">|</span> <span class="s">set -x</span> <span class="s">mkdir -p /var/run/mysqld</span> <span class="s">chown ${MYSQL_UID}:${MYSQL_GID} /var/run/mysqld</span> <span class="na">volumes</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">/var/run:/var/run</span> <span class="na">mysql</span><span class="pi">:</span> <span class="na">image</span><span class="pi">:</span> <span class="s">percona/percona-server:5.7</span> <span class="na">depends_on</span><span class="pi">:</span> <span class="na">create-mysql-socket-dir</span><span class="pi">:</span> <span class="na">condition</span><span class="pi">:</span> <span class="s">service_completed_successfully</span> <span class="na">user</span><span class="pi">:</span> <span class="s2">"</span><span class="s">${MYSQL_UID}:${MYSQL_GID}"</span> <span class="na">volumes</span><span class="pi">:</span> <span class="pi">-</span> <span class="s">/var/run/mysqld/:/var/run/mysqld/</span> <span class="na">command</span><span class="pi">:</span> <span class="s2">"</span><span class="s">--socket=/var/run/mysqld/mysqld.sock"</span> <span class="s">...</span> </code></pre></div></div> Sat, 28 Sep 2024 00:00:00 +0300 https://selivan.github.io/2024/09/28/execute-script-before-starting-service-in-docker-compose.html https://selivan.github.io/2024/09/28/execute-script-before-starting-service-in-docker-compose.html docker docker-compose Simple bash debugger using trap DEBUG <p>Inspired by <a href="https://habr.com/ru/post/666982/">this post</a> by <a href="https://habr.com/ru/users/tminnigaliev/"> @tminnigaliev</a>.</p> <p>Besides traps(handlers) for signals, bash have 4 special traps:</p> <ul> <li><code class="language-plaintext highlighter-rouge">EXIT</code> to run on exit from the shell.</li> <li><code class="language-plaintext highlighter-rouge">RETURN</code> to run each time a function or a sources script finishes.</li> <li><code class="language-plaintext highlighter-rouge">ERR</code> to run each time command failure would cause the shell to exit if <code class="language-plaintext highlighter-rouge">set -e</code> is used.</li> <li><code class="language-plaintext highlighter-rouge">DEBUG</code> to execute before every command.</li> </ul> <p>The last one allows to create a simple debugger inside a bash script:</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">function </span>_trap_DEBUG<span class="o">()</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">"# </span><span class="nv">$BASH_COMMAND</span><span class="s2">"</span><span class="p">;</span> <span class="k">while </span><span class="nb">read</span> <span class="nt">-r</span> <span class="nt">-e</span> <span class="nt">-p</span> <span class="s2">"debug&gt; "</span> _command<span class="p">;</span> <span class="k">do if</span> <span class="o">[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="nv">$_command</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then </span><span class="nb">eval</span> <span class="s2">"</span><span class="nv">$_command</span><span class="s2">"</span><span class="p">;</span> <span class="k">else </span><span class="nb">break</span><span class="p">;</span> <span class="k">fi</span><span class="p">;</span> <span class="k">done</span> <span class="o">}</span> <span class="nb">trap</span> <span class="s1">'_trap_DEBUG'</span> DEBUG </code></pre></div></div> <p>Now before executing every command, it is printed and we have a command prompt. There we can print any command to execute in the script context or an empty line to continue.</p> <p>Also it is possible to run another script in this debugging mode without modifying it. The only catch is that bash functions and scripts inlined by <code class="language-plaintext highlighter-rouge">source</code> do not inherit DEBUG, RETURN and ERR traps. We can use <code class="language-plaintext highlighter-rouge">set -T</code> to allow inheriting DEBUG and RETURN traps.</p> <p>Here is such a simple debugger with some bells and whistles added: <a href="https://github.com/selivan/bash-debug">selivan/bash-debug</a></p> <p>Also it is worth mentioning that a gdb-like debugger for bash scripts exists: <a href="http://bashdb.sourceforge.net/">bashdb</a>. It has way more functionality but requires building from source and installation.</p> Sat, 21 May 2022 00:00:00 +0300 https://selivan.github.io/2022/05/21/bash-debug.html https://selivan.github.io/2022/05/21/bash-debug.html bash WSL fix using random private subnets <p>WSL is a nice way to work with Linux development environment from Windows. It works pretty decently after version 2, that switched to using proper virtualization instead of translating syscalls and other weird magic.</p> <p>Unfortunately, it has one serious problem: subnet for WSL is selected randomly from all available private subnets, preferably from <code class="language-plaintext highlighter-rouge">172.16.0.0/12</code> range. So you turn on your notebook, start WSL, connect to your work VPN and oops - that subnet is already used 🤷‍♂️.</p> <p><a href="https://github.com/microsoft/WSL/issues/4467">Issue on Github</a> to add subnet configuration support in WSL is opened since 2019. People in comments contacted developers and their position is that this by design, to make WSL networking transparent for newbies, and this won’t be changed.</p> <p>Funny thing, VirtualBox selects random private subnet for host-only networking adapters. But these subnets are fixed after initial creation and easily can be changed later. Newbies are happy, advanced users are happy, and nobody is having problems. But that’s not how WSL developers like to do it.</p> <p>WSL selects random private subnet on first start, subnet will not be changed after <code class="language-plaintext highlighter-rouge">wsl --shutdown</code>, only reboot will help. But it does ignore subnets that are already in use. In comments to <a href="https://github.com/microsoft/WSL/issues/4467">the issue</a> people proposed to create dummy network interfaces using all subnets except what we need WSL to start using. @jgregmac made this script: <a href="https://github.com/jgregmac/hyperv-fix-for-devs">github.com/jgregmac/hyperv-fix-for-devs</a>.</p> <p>For some reason it does not work for me, so I made my own version. It brings up dummy network interfaces, starts WSL and then brings them down.</p> <p>First, I already have VirtualBox on my machine. So I will use it’s host-only network interfaces to protect my subnets.</p> <p>VirtualBox:</p> <ul> <li>File - Host NetworkManager <ul> <li>Create - IPv4: <code class="language-plaintext highlighter-rouge">172.16.0.1</code> Network Mask: <code class="language-plaintext highlighter-rouge">255.240.0.0</code> DHCP Server: <code class="language-plaintext highlighter-rouge">[ ]</code></li> <li>Create - IPv4: <code class="language-plaintext highlighter-rouge">10.0.0.1</code> Network Mask: <code class="language-plaintext highlighter-rouge">255.0.0.0</code> DHCP Server: <code class="language-plaintext highlighter-rouge">[ ]</code></li> </ul> </li> </ul> <p>⚙Settings - Network &amp; Internet - Change Adapter Options</p> <ul> <li>Rename interfaces to something more meaningful, like “Vbox-WSL-fix-10” and “Vbox-WSL-fix-172-16”.</li> </ul> <p>Create script <code class="language-plaintext highlighter-rouge">C:\Users\&lt;YOUR USERNAME&gt;\bin\WSL-subnet-fix.ps1</code>:</p> <figure class="highlight"><pre><code class="language-powershell" data-lang="powershell"><span class="c"># Start dummy interfaces protecting required private subnets </span><span class="w"> </span><span class="n">netsh</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="s2">"Vbox-WSL-fix-10"</span><span class="w"> </span><span class="nx">enable</span><span class="w"> </span><span class="n">netsh</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="s2">"Vbox-WSL-fix-172-16"</span><span class="w"> </span><span class="nx">enable</span><span class="w"> </span><span class="c"># start WSL, that is forced to select subnet not overlapping with protected subnets </span><span class="w"> </span><span class="n">wsl</span><span class="w"> </span><span class="nx">ip</span><span class="w"> </span><span class="nx">a</span><span class="w"> </span><span class="c"># Stop dummy interfaces protecting required private subnets </span><span class="w"> </span><span class="n">netsh</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="s2">"Vbox-WSL-fix-10"</span><span class="w"> </span><span class="nx">disable</span><span class="w"> </span><span class="n">netsh</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="nx">set</span><span class="w"> </span><span class="nx">interface</span><span class="w"> </span><span class="s2">"Vbox-WSL-fix-172-16"</span><span class="w"> </span><span class="nx">disable</span></code></pre></figure> <p>Winkey+R - <code class="language-plaintext highlighter-rouge">taskschd.msc</code></p> <p>Create New Task</p> <ul> <li>General <ul> <li>Name: WSL-subnet-fix</li> <li><code class="language-plaintext highlighter-rouge">(*)</code> Run only when user is logged in</li> <li><code class="language-plaintext highlighter-rouge">[x]</code> Run with highest privileges</li> </ul> </li> <li>Triggers <ul> <li>At log on: Specific user: your user</li> </ul> </li> <li>Actions <ul> <li>Start a program <ul> <li>Program: <code class="language-plaintext highlighter-rouge">powershell.exe</code></li> <li>Arguments: <code class="language-plaintext highlighter-rouge">-windowstyle hidden -file "C:\Users\&lt;YOUR USERNAME&gt;\bin\WSL-subnet-fix.ps1"</code></li> </ul> </li> </ul> </li> </ul> <p>Voila, you can use WSL and don’t have it screw up your work VPN.</p> <p>If you need fixed static IP for your WSL machine, you may look at <a href="https://github.com/microsoft/WSL/issues/4210#issuecomment-648570493">this solution</a> by @protang. It adds secondary fixed private subnet to WSL network interface.</p> Mon, 12 Jul 2021 00:00:00 +0300 https://selivan.github.io/2021/07/12/wsl-set-static-subnet-hack.html https://selivan.github.io/2021/07/12/wsl-set-static-subnet-hack.html Windows WSL Change TLS/SSL version and cypher parameters for Linux service using openssl library <p>Sometimes you need to change TLS/SSL parameters for a service using <code class="language-plaintext highlighter-rouge">libssl</code> library from <a href="https://www.openssl.org/">openssl</a>, but the service config does not accept that parameters. In this example, I had to change rsyslog forwarder parameters to send logs to the target that wasn’t playing nice with TLS 1.3 and modern encryption protocols.</p> <p><code class="language-plaintext highlighter-rouge">libssl</code> and applications using it take configuration parameters from configuration file set by environment variable <code class="language-plaintext highlighter-rouge">OPENSSL_CONF</code> or from default file <code class="language-plaintext highlighter-rouge">/etc/ssl/openssl.cnf</code>.</p> <p>Openssl documentation is not the easiest one to read, but <code class="language-plaintext highlighter-rouge">man 5ssl config</code> and some googling got me what I wanted.</p> <p><code class="language-plaintext highlighter-rouge">/etc/ssl/openssl.no-tls13.cnf</code>:</p> <figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="c"># This definition stops the following lines choking if HOME isn't # defined. </span><span class="py">HOME</span> <span class="p">=</span> <span class="s">.</span> <span class="py">openssl_conf</span> <span class="p">=</span> <span class="s">default_conf</span> <span class="nn">[default_conf]</span> <span class="py">ssl_conf</span> <span class="p">=</span> <span class="s">ssl_sect</span> <span class="nn">[ssl_sect]</span> <span class="py">system_default</span> <span class="p">=</span> <span class="s">system_default_sect</span> <span class="nn">[system_default_sect]</span> <span class="py">MaxProtocol</span> <span class="p">=</span> <span class="s">TLSv1.2</span> <span class="py">CipherString</span> <span class="p">=</span> <span class="s">DEFAULT@SECLEVEL=1</span></code></pre></figure> <p><code class="language-plaintext highlighter-rouge">/etc/systemd/system/rsyslog.service.d/override-openssl-params.conf</code>:</p> <figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="nn">[Service]</span> <span class="py">Environment</span><span class="p">=</span><span class="s">"OPENSSL_CONF=/etc/ssl/openssl.cnf.no-tls13"</span></code></pre></figure> <p>And finally applying the new configuration:</p> <figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl daemon-reload systemctl restart rsyslog</code></pre></figure> Fri, 14 May 2021 00:00:00 +0300 https://selivan.github.io/2021/05/14/service-using-openssl-change-ssl-cypher-version-parameters.html https://selivan.github.io/2021/05/14/service-using-openssl-change-ssl-cypher-version-parameters.html openssl ssl tls systemd Systemd broken network.online target workaround <p><a href="https://www.freedesktop.org/software/systemd/man/systemd.special.html">man systemd.special</a> says:</p> <blockquote> <p>Units that strictly require a configured network connection should pull in network-online.target (via a Wants= type dependency) and order themselves after it.</p> </blockquote> <p>Despite that, I ran into a problem: service that requires outside network to be immediately available fails to start with configuration like described in manual:</p> <div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span> <span class="py">Wants</span><span class="p">=</span><span class="s">network-online.target</span> <span class="py">After</span><span class="p">=</span><span class="s">network-online.target</span> </code></pre></div></div> <p>Here is an ugly workaround for that:</p> <div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[Unit]</span> <span class="py">Wants</span><span class="p">=</span><span class="s">network-online.target</span> <span class="py">After</span><span class="p">=</span><span class="s">network-online.target</span> <span class="nn">[Service]</span> <span class="py">ExecStart</span><span class="p">=</span><span class="s">sh -c "while ! ip -4 r | grep ^default; do echo waiting for ipv4 default route to appear; sleep 0.5; done"</span> <span class="py">ExecStart</span><span class="p">=</span><span class="s">&lt;actual service here&gt;</span> </code></pre></div></div> <p>If you want not just default route available, but certain IP address:</p> <div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">ExecStart</span><span class="p">=</span><span class="s">sh -c "while ! ping -c 1 -W 0.5 &lt;IP&gt;; do true; done"</span> </code></pre></div></div> Wed, 23 Dec 2020 00:00:00 +0300 https://selivan.github.io/2020/12/23/systemd-broken-network-online-target-workaround.html https://selivan.github.io/2020/12/23/systemd-broken-network-online-target-workaround.html systemd Syncronize time by NTP before starting any services in Linux <p>Servers often have wrong clock on startup. NTP services, like <code class="language-plaintext highlighter-rouge">ntp</code>, <code class="language-plaintext highlighter-rouge">chrony</code> and <code class="language-plaintext highlighter-rouge">systemd-timesyncd</code> try to correct clock gradually to avoid weird bugs in software. Therefore, if server has a large clock offset on startup, it works with incorrect clock for several minutes.</p> <p>In my experience, AWS instances may have clock error up to 5 minutes on startup. Server writing log timestamps 5 minutes in the past or in the future is not always a good idea.</p> <p>Solution is to force NTP time syncronization once before starting any other services. I prefer to use <a href="https://chrony.tuxfamily.org/">chrony</a>: it can act both as always runnig NTP client and one-time syncronization tool; <code class="language-plaintext highlighter-rouge">chronyc</code> clearly reports syncronization status, making it easy to monitor; it is <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/set-time.html">recommended by AWS</a>.</p> <p>All other services in <code class="language-plaintext highlighter-rouge">multi-user.target</code> will start after time is syncronized or failed to syncronize after given timeout.</p> <p><code class="language-plaintext highlighter-rouge">/etc/systemd/system/ntp-sync-once.service</code> :</p> <figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="nn">[Unit]</span> <span class="py">Description</span><span class="p">=</span><span class="s">Quick sync NTP one time and exit</span> <span class="py">Wants</span><span class="p">=</span><span class="s">network-online.target</span> <span class="py">After</span><span class="p">=</span><span class="s">network-online.target</span> <span class="py">Before</span><span class="p">=</span><span class="s">multi-user.target</span> <span class="nn">[Service]</span> <span class="py">Type</span><span class="p">=</span><span class="s">oneshot</span> <span class="py">RemainAfterExit</span><span class="p">=</span><span class="s">True</span> <span class="c"># Ugly workaround for not working properly network-online.target </span><span class="py">ExecStartPre</span><span class="p">=</span><span class="s">sh -c "while ! ip -4 r | grep ^default; do echo waiting for ipv4 default route to appear; sleep 0.5; done"</span> <span class="c"># -t &lt;timeout in seconds&gt; timeout after which chronyd will exit even if clock is not syncronized </span><span class="py">ExecStart</span><span class="p">=</span><span class="s">/usr/sbin/chronyd -q -t 20</span> <span class="nn">[Install]</span> <span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span></code></pre></figure> <p><code class="language-plaintext highlighter-rouge">network-online.target</code> sometimes is <a href="/2020/12/23/systemd-broken-network-online-target-workaround.html">not working as expected</a>, <code class="language-plaintext highlighter-rouge">ExecStartPre</code> line is a workaround for that.</p> Wed, 23 Dec 2020 00:00:00 +0300 https://selivan.github.io/2020/12/23/ntp-sync-time-before-starting-any-services.html https://selivan.github.io/2020/12/23/ntp-sync-time-before-starting-any-services.html ntp chrony systemd Letsencrypt: auto generate bundle certificates for Haproxy <p>I was surprised to not get instant answer googling “letsencrypt automatically generate bundle certificates for haproxy”.</p> <p>So here it is, to save 5 minutes of your time.</p> <p>Generates bundle(fullchain + privkey) certificates for everything in <code class="language-plaintext highlighter-rouge">/etc/letserncrypt/live/*/fullchain_and_privkey.pem</code>, does not update generated files if not necessary.</p> <p><em>NOTE</em>: renew hooks are not triggered when you run <code class="language-plaintext highlighter-rouge">certbot certonly</code>, so for the first time you should run it manually.</p> <script src="https://gist.github.com/selivan/a65b2c8dfe1a2563b50d822727fa7d0f.js"></script> <noscript><a href="https://gist.github.com/selivan/a65b2c8dfe1a2563b50d822727fa7d0f">gist generate-bundle-certs.sh </a></noscript> Wed, 02 Dec 2020 00:00:00 +0300 https://selivan.github.io/2020/12/02/letsencrypt-auto-generate-bundle-certificates-for-haproxy.html https://selivan.github.io/2020/12/02/letsencrypt-auto-generate-bundle-certificates-for-haproxy.html letsencrypt ssl tls haproxy Using Ansible with bastion or jump host with variables <p>Sometimes due to network configuration or security reasons you can’t access a host directly, but should use intermediate host, so-called bastion or jump host.</p> <p>My presious article on this topic, using ssh config file: <a href="/2018/01/29/ansible-ssh-bastion-host.html">Using Ansible with bastion host</a>.</p> <p>Another option is using variables. This approach is more flexible: it is easier to define different bastion hosts for different hosts and groups with variables.</p> <p>Simplest option is to directly mention bastion host address and user:</p> <p><code class="language-plaintext highlighter-rouge">group_vars/all/ansible_ssh.yml</code>:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">ansible_ssh_common_args</span><span class="pi">:</span> <span class="s2">"</span><span class="s">-o</span><span class="nv"> </span><span class="s">ProxyCommand=</span><span class="se">\"</span><span class="s">ssh</span><span class="nv"> </span><span class="s">&lt;bastion</span><span class="nv"> </span><span class="s">user&gt;@&lt;bastion</span><span class="nv"> </span><span class="s">address&gt;</span><span class="nv"> </span><span class="s">-o</span><span class="nv"> </span><span class="s">Port=&lt;bastion</span><span class="nv"> </span><span class="s">ssh</span><span class="nv"> </span><span class="s">port&gt;</span><span class="nv"> </span><span class="s">-W</span><span class="nv"> </span><span class="s">%h:%p</span><span class="se">\"</span><span class="s">"</span> </code></pre></div></div> <p>And of course don’t forget to reset that variable for the bastion host itself:</p> <p><code class="language-plaintext highlighter-rouge">host_vars/bastion_host/ansible_ssh.yml</code>:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">ansible_ssh_common_args</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span> </code></pre></div></div> <p>Downside of simple option is duplicating bastion host coordinates in variables and in inventory. More complex setup to avoid the duplication:</p> <p><code class="language-plaintext highlighter-rouge">group_vars/all/ansible_ssh.yml</code>:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">ansible_ssh_proxy_command</span><span class="pi">:</span> <span class="pi">&gt;-</span> <span class="s">{% if bastion_host is defined and bastion_host != '' %}</span> <span class="s">ssh {{ hostvars[bastion_host]['ansible_ssh_user'] }}@{{ hostvars[bastion_host]['ansible_ssh_host'] }}</span> <span class="s">-o Port={{ hostvars[bastion_host]['ansible_ssh_port'] | default(22) }}</span> <span class="s">-W %h:%p</span> <span class="s">{% endif %}</span> <span class="na">ansible_ssh_common_args</span><span class="pi">:</span> <span class="pi">&gt;-</span> <span class="s">{% if bastion_host is defined and bastion_host != '' %}</span> <span class="s">-o ProxyCommand="{{ ansible_ssh_proxy_command }}"</span> <span class="s">{% endif %}</span> <span class="c1"># Default bastion host for all hosts</span> <span class="s">bastion_host=bastion1</span> </code></pre></div></div> <p>And you still have to mention that bastion host itself doesn’t need this:</p> <p><code class="language-plaintext highlighter-rouge">host_vars/bastion_host/ansible_ssh.yml</code>:</p> <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">bastion_host</span><span class="pi">:</span> <span class="s2">"</span><span class="s">"</span> </code></pre></div></div> Fri, 30 Oct 2020 00:00:00 +0300 https://selivan.github.io/2020/10/30/ansible-ssh-bastion-host-with-variables.html https://selivan.github.io/2020/10/30/ansible-ssh-bastion-host-with-variables.html ansible ssh bastion Smooth deploy of a loaded PHP web application with php-fpm and nginx, without downtime or errors <p><strong>UPDATE 2020-05-10</strong> Added the cachetool option, thanks to reddit users <a href="https://www.reddit.com/u/ds11">ds11</a> and <a href="https://www.reddit.com/u/SevereHeight">SevereHeight</a> for mentioning it.</p> <p>Generally, the smoothest way to deploy a loaded web app without downtime or erros is by adding instances(hosts, containers) with the new version to balancer and then removing old ones, when they have served all running requests. But sometimes it is easier to switch app versions on instances without re-configuring balancer.</p> <p>Here is how to do that with php-fpm and nginx.</p> <p>After deploying the application(with <a href="https://deployer.org">deployer</a> or whatever tool you prefer) you have directories with old and new application versions, shared files(like logs), and symlink <code class="language-plaintext highlighter-rouge">current</code> pointing to the new version. Like this:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/var/www/example.com: releases/ 2020-04-08-12-00-00/ 2020-04-08-15-00-00/ shared/ logs/ current -&gt; releases/2020-04-08-15-00-00 </code></pre></div></div> <p>Using the <code class="language-plaintext highlighter-rouge">current</code> symlink directly in nginx config may cause some troubles.</p> <p>php-fpm is not designed to have app files suddenly change/disappear. Some of requests in progress when switching symlink may return 500 errors.</p> <p>I suggest instead to use the absolute path to the required app version in nginx configs:</p> <p><code class="language-plaintext highlighter-rouge">/etc/nginx/sites-enabled/example.com:</code></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>include /var/www/example.com/root_dir.nginx.conf; root $root_dir; </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">/var/www/example.com/root_dir.nginx.conf:</code></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>set $root_dir /var/www/example.com/2020-04-08-15-00-00/public; </code></pre></div></div> <p>File <code class="language-plaintext highlighter-rouge">root_dir.nginx.conf</code> is updated on deploy, then nginx is reloaded. Nginx reload is done very carefully, all requests in progress are completed by old workers with the old config version, pointing to the old application version. Users won’t experience any errors or slowdown.</p> <p>Now the only thing left to do is to clear the old code from php opcache. You can do that with <a href="https://www.php.net/manual/en/function.opcache-reset.php">opcache_reset()</a> function. This function should be called from a PHP process using the fpm pool you want to reset the opcache for.</p> <p>First option is to create a php file and make it accessible from localhost:</p> <p><code class="language-plaintext highlighter-rouge">/var/www/localhost/opcache_reset.php:</code></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;?php header('Content-Type:text/plain'); try { if (opcache_reset()){ print "SUCCESS: opcache_reset\n"; } else { throw new Exception("ERROR: opcache is disabled\n", 500); } } catch ( Exception $e) { http_response_code($e-&gt;getCode()?:500); print $e-&gt;getMessage(); } </code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">/etc/nginx/sites-enabled/localhost:</code></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server { listen 127.0.0.1:80; location / { return 403; } location = /opcache_reset { include fastcgi_params.conf; fastcgi_split_path_info ^(.+\.php)(/.*)$; fastcgi_pass unix:/var/run/php/phpXX-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root/opcache_reset.php; } </code></pre></div></div> <p>Second option is using <a href="https://gordalina.github.io/cachetool/">cachetool</a>. It can connect directly to php-fpm unix socket or tcp port, no need to serve special php files. There is a ready <a href="https://deployer.org/recipes/cachetool.html">deployer recipie</a> to use it.</p> <p>Finally, here is <a href="https://deployer.org">deployer</a> config to do all that, example extending the symfony4 recipie with separate php file:</p> <p><code class="language-plaintext highlighter-rouge">deploy-example-com.php:</code></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>namespace Deployer; require 'recipe/symfony4.php'; desc('generate nginx config and reload'); task('nginx:update_config_and_reload', function () { run('echo "set \$root_dir /public;" &gt; /root_dir.nginx.conf'); run('sudo --non-interactive nginx -t'); // avoid reloading with incorrect config run('sudo --non-interactive systemctl reload nginx.service'); }); desc('call php-fpm opcache_reset from localhost'); task('php-fpm:localhost:opcache_reset', function () { run('curl http://localhost/opcache_reset'); }); after('deploy:symlink', 'nginx:update_config_and_reload'); after('nginx:update_config_and_reload', 'php-fpm:localhost:opcache_reset'); inventory('inventory.yml'); set('deploy_path','/var/www/example.com/'); set('repository', '[email protected]:my/example-com.git'); ... // some other perameters </code></pre></div></div> <p>And example with cachetool:</p> <p><code class="language-plaintext highlighter-rouge">deploy-example-com.php:</code></p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>namespace Deployer; require 'recipe/symfony4.php'; require 'recipe/cachetool.php'; desc('generate nginx config and reload'); task('nginx:update_config_and_reload', function () { run('echo "set \$root_dir /public;" &gt; /root_dir.nginx.conf'); run('sudo --non-interactive nginx -t'); // avoid reloading with incorrect config run('sudo --non-interactive systemctl reload nginx.service'); }); after('deploy:symlink', 'nginx:update_config_and_reload'); after('nginx:update_config_and_reload', 'cachetool:clear:opcache'); inventory('inventory.yml'); set('deploy_path','/var/www/example.com/'); set('repository', '[email protected]:my/example-com.git'); set('cachetool', '/var/run/php/phpXX-fpm.sock'); ... // some other perameters </code></pre></div></div> <p>Note that deploy user should have rights to reload nginx service with sudo and to check nginx config(run <code class="language-plaintext highlighter-rouge">nginx -t</code>).</p> Thu, 09 Apr 2020 00:00:00 +0300 https://selivan.github.io/2020/04/09/smooth-no-downtime-deploy-with-php-fpm-and-nginx.html https://selivan.github.io/2020/04/09/smooth-no-downtime-deploy-with-php-fpm-and-nginx.html php nginx deploy Removing all unnecessary bloatware from Xiaomi MIUI 11/12 (Android 9/10) without root <p><em>TLDR: If you just want the list of bloatware app package names for Xiaomi MIUI - scroll to the end of the artice.</em></p> <p><strong>UPDATE 2020-05-09</strong>: Added SIM Toolkit description.<br /> <strong>UPDATE 2020-07-04</strong> Added some apps, thanks to @thecrazyblack.<br /> <strong>UPDATE 2020-11-23</strong> Updated list of apps, thanks to new comments by @toast254, @swiesmann, @MarcelloDM, @satnited.</p> <p>Xiaomi phones have impressive parameters for given price, but they come with a lot of unnecessary software. It eats battery and memory, sometimes shows annoying advertisement, and may have security issues. You can not uninstall this pre-installed apps like usual ones, and you can not even disable them from settings like in earlier Android versions.</p> <p>Here is how to remove or disable unnecessary software without rooting phone. Works for MIUI 11 and 12 (based on Android 9 and 10), should work for other phones for recent Android versions. Thanks to <a href="https://stackoverflow.com/a/56968886/890863">this</a> very useful but severely under-voted stackoverflow answer.</p> <p><strong>NOTE</strong>: I do not guarantee that this instructions won’t break your phone, blow it to flaming pieces or cause a sentient machines rebellion against humanity. You were warned.</p> <p>To manage phone from command-line via USB you need <code class="language-plaintext highlighter-rouge">adb</code> - Android Debug Bridge, part of Android platform-tools. You can download the recent one for your OS <a href="https://developer.android.com/studio/releases/platform-tools">here</a>.</p> <p>If you are on Windows, you also need <a href="https://developer.android.com/studio/run/oem-usb.html">USB drivers</a> for your device. You may also try <a href="https://github.com/koush/UniversalAdbDriver">Universal Adb Driver</a>. If you are using <a href="https://chocolatey.org/">chocolatey</a> package manager, you can get both like that:</p> <p><code class="language-plaintext highlighter-rouge">choco install -y adb universal-adb-drivers</code></p> <ul> <li>⚙️ Settings - About phone(or My Device). Tap “MIUI version” multiple times. Now Developer Options are unlocked.</li> <li>⚙️ Settings - Additional settings - Developer Options - [✔️] USB debugging.</li> <li>Connect the phone to your computer via USB. Choose “File Transfer” mode instead of default “No data transfer”.</li> <li>Open console in a directory where you unpacked platform tools.</li> <li><code class="language-plaintext highlighter-rouge">./adb devices</code></li> <li>Phone will prompt to add your computer’s key to allowed, agree to it.</li> <li><code class="language-plaintext highlighter-rouge">./adb shell</code> you have a shell on your device.</li> </ul> <p>Now you need app package names, like <code class="language-plaintext highlighter-rouge">com.miui.yellowpage</code> for Mi Yellow Pages.</p> <ul> <li>⚙️ Settings - Apps - Manage Apps. Tap on application, then tap info(ℹ️) button in the right corner. There you can see “APK name”, that’s what we need.</li> </ul> <p>There are 2 options: disable app and uninstall app. I prefer disabling them, it’s easier to enable them back if you’ve broken something.</p> <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Disable app</span> pm disable-user app.package.name <span class="c"># Re-enable it</span> pm <span class="nb">enable </span>app.package.name <span class="c"># Uninstall app</span> <span class="c"># Sometimes uninstall command may not work without -k option on un-rooted devices</span> <span class="c"># -k: Keep the data and cache directories around after package removal. </span> pm uninstall <span class="nt">--user</span> 0 app.package.name <span class="c"># Install uninstalled system app</span> pm <span class="nb">install</span> <span class="nt">--user</span> 0 <span class="si">$(</span>pm dump app.package.name | <span class="nb">awk</span> <span class="s1">'/path/{ print $2 }'</span><span class="si">)</span> <span class="c"># Another way to install uninstalled system app</span> pm install-existing app.package.name </code></pre></div></div> <p>More details here: <a href="https://developer.android.com/studio/command-line/adb#pm">pm commad manual</a>.</p> <p>To be able to install apps back, you need to enable</p> <ul> <li>⚙️ Settings - Additional settings - Developer Options - [✔️] Install via USB</li> </ul> <p>On Xiaomi phone to enable this setting you need to sign in into Mi Account. You may just use your Google account to sign into it and then sign-out when you don’t need it anymore:</p> <ul> <li>⚙️ Settings - Mi Account - sign-out.</li> </ul> <p>Here is a list of Xiaomi and Google apps that I find unnecessary:</p> <p><strong>UNSAFE TO DISABLE/UNINSTALL</strong></p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.xiaomi.finddevice</code></td> <td>Result: endless boot loop, some time after it will ask to erase the phone and start over. Guess how I learned that?</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.securitycenter</code></td> <td>Result: phone reboots only in recovery mode</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.contacts</code></td> <td>Result: you lose the phone icon</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.mi.android.globalminusscreen</code></td> <td>Xiaomi App Vault. Result: if you are logged in with Mi account, device becomes locked, to unlock you should bring it to Xiaomi Services Center. Settings -&gt; Home Screen crashes Settings app. See comment by @satnited.</td> </tr> </tbody> </table> <hr /> <p><strong>Xiaomi</strong>:</p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.analytics</code></td> <td>Mi Analytics (Spyware?)</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.xiaomi.mipicks</code></td> <td>GetApps - app store like Google Play from Xiaomi. The most annoying one, periodically shows advertisement.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.msa.global</code></td> <td>MIUI Ad Services - also responsible for showing ads.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.cloudservice</code> <code class="language-plaintext highlighter-rouge">com.miui.cloudservice.sysbase</code> <code class="language-plaintext highlighter-rouge">com.miui.newmidrive</code></td> <td>Mi Cloud</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.cloudbackup</code></td> <td>Mi Cloud Backup</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.backup</code></td> <td>MIUI Backup</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.xiaomi.glgm</code></td> <td>Games</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.xiaomi.payment</code> <code class="language-plaintext highlighter-rouge">com.mipay.wallet.in</code></td> <td>Mi Credit</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.tencent.soter.soterserver</code></td> <td>Authorize payments in WeChat and other Chinese services, useless if you don’t live in China.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">cn.wps.xiaomi.abroad.lite</code></td> <td>Mi DocViewer(Powered by WPS Office)</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.videoplayer</code></td> <td>Mi Video</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.player</code></td> <td>Mi Music</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.mi.globalbrowser</code></td> <td>Mi Browser</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.xiaomi.midrop</code></td> <td>Mi ShareMe</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.yellowpage</code></td> <td>Mi YellowPages. Some kind of caller id app.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.gallery</code></td> <td>MIUI Gallery - if you use another gallery app <em>WARNING</em>: @nihalanand697 reports disabling it isn’t safe. But I had no problems after uninstalling it.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.android.fashiongallery</code></td> <td>Wallpaper Carousel</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.bugreport</code> <code class="language-plaintext highlighter-rouge">com.miui.miservice</code></td> <td>Mi Bug Report - if you not using this feature</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.weather2</code></td> <td>MIUI Weather. I prefer another weather app.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.hybrid</code> <code class="language-plaintext highlighter-rouge">com.miui.hybrid.accessory</code></td> <td>Quick apps.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.miui.global.packageinstaller</code></td> <td>MIUI package installer. Without it Play Store app will be used. It shows ads, but I like that you can manage app permissions before starting it.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.xiaomi.joyose</code></td> <td>?? Some junk</td> </tr> </tbody> </table> <hr /> <p><strong>Google</strong>:</p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.gms.location.history</code></td> <td>Location History</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.videos</code></td> <td>Google Movies</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.music</code></td> <td>Google Music</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.apps.photos</code></td> <td>Google Photos</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.youtube</code></td> <td>Youtube - I prefer to use a browser</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.apps.tachyon</code></td> <td>Google Duo - video calling app</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.ar.lens</code></td> <td>Google Lens - identify things on camera</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.googlequicksearchbox</code></td> <td>Google search box - I prever to use a browser or widget</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.apps.wellbeing</code></td> <td>Digital wellbeing</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.google.android.apps.googleassistant</code></td> <td>Google Assistant</td> </tr> </tbody> </table> <hr /> <p><strong>Facebook</strong>:</p> <p>What the @#$%? I just got a fresh phone, didn’t install any Facebook apps and I still have a bunch of Facebook services eating my battery and memory.</p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.facebook.katana</code></td> <td>Facebook mobile app</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.facebook.services</code></td> <td>Facebook Services</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.facebook.system</code></td> <td>Facebook App Installer</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.facebook.appmanager</code></td> <td>Facebook app manager</td> </tr> </tbody> </table> <hr /> <p><strong>Default Android Apps</strong></p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.browser</code></td> <td>Default Browser - not necessary if you use Firefox or Chrome</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.wallpaper.livepicker</code></td> <td>Wallpaper live picker</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.dreams.basic</code></td> <td>Screen saver</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.dreams.phototable</code></td> <td>Screen saver</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.providers.downloads.ui</code></td> <td>Downloads UI. Periodically show notifications about files you downloaded. I find no use for it.</td> </tr> </tbody> </table> <hr /> <p><strong>Miscelanneous Promoted Apps</strong></p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.netflix.partner.activation</code></td> <td>Some Netflix stuff</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.zhiliaoapp.musically</code></td> <td>TikTok</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">ru.yandex.searchplugin</code></td> <td>Yandex Search</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.yandex.zen</code></td> <td>Yandex Zen</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.ebay.mobile</code> <code class="language-plaintext highlighter-rouge">com.ebay.carrier</code></td> <td>Ebay Store</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">ru.ozon.app.android</code></td> <td>Ozon Store</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.alibaba.aliexpresshd</code></td> <td>Aliexpress Store</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">sg.bigo.live</code></td> <td>Some social media</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">ru.auto.ara</code></td> <td>auto.ru app</td> </tr> </tbody> </table> <p>And some additional steps to disable Xiaomi ads and collecting data:</p> <ul> <li>⚙️ Settings - Passwords &amp; Security - Authorization &amp; revocation. Revoke authorization from msa(MIUI System Ads) application. Not necessary if you already disabled <code class="language-plaintext highlighter-rouge">com.miui.msa.global</code>.</li> <li>⚙️ Settings - Passwords &amp; Security - Privacy. Disable “User Experience Program” and “Send diagnostic data automatically”.</li> <li>⚙️ Settings - Passwords &amp; Security - Privacy - Ad services. Disable “Personalized ad recommendations”.</li> </ul> <p><strong>SIM Toolkit</strong></p> <p>Some Russian mobile operators use SIM card built-in application to promote paid services. They show pop-up windows with OK/Cancel buttons. Hit the wrong button - and you are suddenly subscribed to some freaking SMS horoscope with daily fee.</p> <table> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.stk</code></td> <td>SIM Toolkit</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">com.android.stk2</code></td> <td>SIM Toolkit for the second SIM card</td> </tr> </tbody> </table> <p>Note, that some mobile operators may use SIM toolkit for useful things, and you will lose that functions. In my experience I have never seen anything useful there.</p> <p>Another good articles on de-bloating MIUI:</p> <ul> <li><a href="https://technastic.com/xiaomi-bloatware-list-miui/">technastic.com/xiaomi-bloatware-list-miui/</a></li> <li><a href="https://devcondition.com/article/removing-unneeded-miui-applications/">devcondition.com/article/removing-unneeded-miui-applications/</a></li> </ul> Tue, 25 Feb 2020 00:00:00 +0300 https://selivan.github.io/2020/02/25/removing-bloatware-from-xiaomi-miui-android.html https://selivan.github.io/2020/02/25/removing-bloatware-from-xiaomi-miui-android.html android xiaomi miui