<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>2019 on Ricky</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/</link><description>Recent content in 2019 on Ricky</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 30 Dec 2019 14:31:45 +0800</lastBuildDate><atom:link href="https://9855cc0f.linzeyan.pages.dev/posts/2019/index.xml" rel="self" type="application/rss+xml"/><item><title>How to Create Temporary Files in Bash: mktemp and trap</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191230-mktemp/</link><pubDate>Mon, 30 Dec 2019 14:31:45 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191230-mktemp/</guid><description>&lt;ul>
&lt;li>&lt;a href="http://www.ruanyifeng.com/blog/2019/12/mktemp.html" target="_blank" rel="noopener">How to Create Temporary Files in Bash: mktemp and trap&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="mktemp-command">&lt;code>mktemp&lt;/code> command&lt;/h4>
&lt;ul>
&lt;li>The generated temp file name is random and readable/writable only by the user.&lt;/li>
&lt;li>To ensure creation succeeds, append OR (&lt;code>||&lt;/code>) after &lt;code>mktemp&lt;/code> to exit on failure.&lt;/li>
&lt;li>To delete temp files on script exit, use &lt;code>trap&lt;/code> to register cleanup.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>trap &lt;span style="color:#e6db74">&amp;#39;rm -f &amp;#34;$TMPFILE&amp;#34;&amp;#39;&lt;/span> EXIT
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>TMPFILE&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">$(&lt;/span>mktemp&lt;span style="color:#66d9ef">)&lt;/span> &lt;span style="color:#f92672">||&lt;/span> exit &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>echo &lt;span style="color:#e6db74">&amp;#34;Our temp file is &lt;/span>$TMPFILE&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="mktemp-options">mktemp options&lt;/h5>
&lt;ul>
&lt;li>&lt;code>-d&lt;/code> creates a temporary directory.&lt;/li>
&lt;li>&lt;code>-p&lt;/code> specifies the directory for temp files. Default is &lt;code>$TMPDIR&lt;/code>. If not set, &lt;code>/tmp&lt;/code> is used.&lt;/li>
&lt;li>&lt;code>-t&lt;/code> specifies a filename template. The template must end with at least three consecutive &lt;code>X&lt;/code> characters for randomness; six &lt;code>X&lt;/code> are recommended. The default template is &lt;code>tmp.&lt;/code> followed by ten random characters.&lt;/li>
&lt;/ul>
&lt;h4 id="trap-usage">&lt;code>trap&lt;/code> usage&lt;/h4>
&lt;p>The trap command handles system signals in Bash scripts.&lt;/p></description></item><item><title>Getting Started with GitHub Actions</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191223-getting-started-with-github-actions/</link><pubDate>Mon, 23 Dec 2019 10:16:01 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191223-getting-started-with-github-actions/</guid><description>&lt;ul>
&lt;li>&lt;a href="http://www.ruanyifeng.com/blog/2019/09/getting-started-with-github-actions.html" target="_blank" rel="noopener">Getting Started with GitHub Actions&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>How Does Nginx Defend Against DDoS?</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191220-nginx-defend-ddos/</link><pubDate>Fri, 20 Dec 2019 09:42:50 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191220-nginx-defend-ddos/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://magiclen.org/nginx-defend-ddos/" target="_blank" rel="noopener">How Does Nginx Defend Against DDoS?&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.itread01.com/content/1547474225.html" target="_blank" rel="noopener">Nginx limit module for access rate and max concurrent connections (DDoS protection)&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="ngx_http_limit_req_module">ngx_http_limit_req_module&lt;/h4>
&lt;p>&lt;code>limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;&lt;/code>&lt;/p></description></item><item><title>Golang Service Exceeded File Handle Limit (too many open files)</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191216-23828/</link><pubDate>Mon, 16 Dec 2019 10:14:33 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191216-23828/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://studygolang.com/articles/23828" target="_blank" rel="noopener">Golang Service Exceeded File Handle Limit (too many open files)&lt;/a>&lt;/li>
&lt;/ul>
&lt;ol>
&lt;li>
&lt;p>Check system config: &lt;code>ulimit -a | grep open&lt;/code>. The system config is normal.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Check the service file limit: &lt;code>cat /proc/40636/limits&lt;/code>. The service did not inherit the system settings and is still limited to 1024.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Check open file count: &lt;code>lsof -p 40636 | wc -l&lt;/code>. It exceeds the limit, so the error occurs.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Check which connections are open: &lt;code>lsof -p 40636 &amp;gt; openfiles.log&lt;/code>. Many HTTP connections were left open to the alerting service. The root cause is that the app failed to parse config, sent errors to the alerting service, and did not close connections, leading to too many open files. Why it did not inherit system limits needed more investigation.&lt;/p></description></item><item><title>Trellis Ansible Bad Interpreter Error</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191206-trellis-ansible-bad-interpreter-error/</link><pubDate>Fri, 06 Dec 2019 22:34:31 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191206-trellis-ansible-bad-interpreter-error/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://wpvilla.in/trellis-ansible-bad-interpreter-error/" target="_blank" rel="noopener">Trellis Ansible Bad Interpreter Error&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="bad-interpreter-error">Bad Interpreter Error&lt;/h4>
&lt;p>Error we got using Ansible was a bad interpreter error. Python 2.7 is not to be found:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>zsh: /usr/local/bin/ansible-vault: bad interpreter: /usr/local/opt/python@2/bin/python2.7: no such file or directory
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And that was correct because when we checked /usr/local/opt we only had Python 3.&lt;/p>
&lt;h4 id="installation-python-2">Installation Python 2&lt;/h4>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>brew install python@2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h5 id="python-crashing-hard">Python Crashing Hard&lt;/h5>
&lt;p>Next, on Ansible version check I got another error&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>➜ trellis git:&lt;span style="color:#f92672">(&lt;/span>master&lt;span style="color:#f92672">)&lt;/span> ansible --version
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span>1&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#ae81ff">19153&lt;/span> abort ansible --version
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It was somehow crashing Python 2.7 though it should just work with it. I decided to upgrade Ansible as well&lt;/p></description></item><item><title>Load Balancing with iptables and ip rule</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191204-ip-tables-rule-load-balance/</link><pubDate>Wed, 04 Dec 2019 11:08:04 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191204-ip-tables-rule-load-balance/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://blog.outv.im/2019/ip-tables-rule-load-balance/" target="_blank" rel="noopener">Load Balancing with iptables and ip rule&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="steps">Steps&lt;/h4>
&lt;p>This example uses an Arch Linux device with two Internet uplinks: eth0 and eth1. The mapping is:&lt;/p>
&lt;ul>
&lt;li>Mark 10 (0xa) - Routing table #110 - use eth0&lt;/li>
&lt;li>Mark 11 (0xb) - Routing table #111 - use eth1&lt;/li>
&lt;/ul>
&lt;p>We decide which uplink to use based on the packet mark. First, use ip rule to map each mark to its routing table.&lt;/p>
&lt;p>The default routing table priority is 32768. To ensure our tables are used, set a higher priority (for example 31000).&lt;/p></description></item><item><title>htop explained</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191114-htop/</link><pubDate>Thu, 14 Nov 2019 10:05:35 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191114-htop/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://peteris.rocks/blog/htop/" target="_blank" rel="noopener">htop explained&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Some Jenkinsfile examples</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191106-228cdb1893fca91f0663bab7b095757c/</link><pubDate>Wed, 06 Nov 2019 17:29:46 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191106-228cdb1893fca91f0663bab7b095757c/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://gist.github.com/merikan/228cdb1893fca91f0663bab7b095757c" target="_blank" rel="noopener">Some Jenkinsfile examples&lt;/a>&lt;/li>
&lt;/ul>
&lt;h5 id="parallel">Parallel&lt;/h5>
&lt;pre tabindex="0">&lt;code>#!/usr/bin/env groovy
pipeline {
agent any
stages {
stage(&amp;#34;Build&amp;#34;) {
steps {
sh &amp;#39;mvn -v&amp;#39;
}
}
stage(&amp;#34;Testing&amp;#34;) {
parallel {
stage(&amp;#34;Unit Tests&amp;#34;) {
agent { docker &amp;#39;openjdk:7-jdk-alpine&amp;#39; }
steps {
sh &amp;#39;java -version&amp;#39;
}
}
stage(&amp;#34;Functional Tests&amp;#34;) {
agent { docker &amp;#39;openjdk:8-jdk-alpine&amp;#39; }
steps {
sh &amp;#39;java -version&amp;#39;
}
}
stage(&amp;#34;Integration Tests&amp;#34;) {
steps {
sh &amp;#39;java -version&amp;#39;
}
}
}
}
stage(&amp;#34;Deploy&amp;#34;) {
steps {
echo &amp;#34;Deploy!&amp;#34;
}
}
}
}
&lt;/code>&lt;/pre>&lt;h5 id="when">When&lt;/h5>
&lt;pre tabindex="0">&lt;code>#!/usr/bin/env groovy
pipeline {
agent any
environment {
VALUE_ONE = &amp;#39;1&amp;#39;
VALUE_TWO = &amp;#39;2&amp;#39;
VALUE_THREE = &amp;#39;3&amp;#39;
}
stages {
// skip a stage while creating the pipeline
stage(&amp;#34;A stage to be skipped&amp;#34;) {
when {
expression { false } //skip this stage
}
steps {
echo &amp;#39;This step will never be run&amp;#39;
}
}
// Execute when branch = &amp;#39;master&amp;#39;
stage(&amp;#34;BASIC WHEN - Branch&amp;#34;) {
when {
branch &amp;#39;master&amp;#39;
}
steps {
echo &amp;#39;BASIC WHEN - Master Branch!&amp;#39;
}
}
// Expression based when example with AND
stage(&amp;#39;WHEN EXPRESSION with AND&amp;#39;) {
when {
expression {
VALUE_ONE == &amp;#39;1&amp;#39; &amp;amp;&amp;amp; VALUE_THREE == &amp;#39;3&amp;#39;
}
}
steps {
echo &amp;#39;WHEN with AND expression works!&amp;#39;
}
}
// Expression based when example
stage(&amp;#39;WHEN EXPRESSION with OR&amp;#39;) {
when {
expression {
VALUE_ONE == &amp;#39;1&amp;#39; || VALUE_THREE == &amp;#39;2&amp;#39;
}
}
steps {
echo &amp;#39;WHEN with OR expression works!&amp;#39;
}
}
// When - AllOf Example
stage(&amp;#34;AllOf&amp;#34;) {
when {
allOf {
environment name:&amp;#39;VALUE_ONE&amp;#39;, value: &amp;#39;1&amp;#39;
environment name:&amp;#39;VALUE_TWO&amp;#39;, value: &amp;#39;2&amp;#39;
}
}
steps {
echo &amp;#34;AllOf Works!!&amp;#34;
}
}
// When - Not AnyOf Example
stage(&amp;#34;Not AnyOf&amp;#34;) {
when {
not {
anyOf {
branch &amp;#34;development&amp;#34;
environment name:&amp;#39;VALUE_TWO&amp;#39;, value: &amp;#39;4&amp;#39;
}
}
}
steps {
echo &amp;#34;Not AnyOf - Works!&amp;#34;
}
}
}
}
&lt;/code>&lt;/pre></description></item><item><title>Jinja docx template, avoiding new line in nested for</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191029-jinja-docx-template-avoiding-new-line-in-nested-for/</link><pubDate>Tue, 29 Oct 2019 10:17:35 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191029-jinja-docx-template-avoiding-new-line-in-nested-for/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/45719062/jinja-docx-template-avoiding-new-line-in-nested-for#_=_" target="_blank" rel="noopener">Jinja docx template, avoiding new line in nested for&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>What the f*ck Python! 🐍</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191011-wtfpython-cn/</link><pubDate>Fri, 11 Oct 2019 14:32:20 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191011-wtfpython-cn/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://github.com/leisurelicht/wtfpython-cn" target="_blank" rel="noopener">What the f*ck Python! 🐍&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Fighting ISP Cache Hijacking Again with iptables</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191007-fuck-cmcc/</link><pubDate>Mon, 07 Oct 2019 10:41:08 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191007-fuck-cmcc/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://v2c.tech/Article/FUCK-CMCC" target="_blank" rel="noopener">Fighting ISP Cache Hijacking Again with iptables&lt;/a>&lt;/li>
&lt;/ul>
&lt;h5 id="cause">Cause&lt;/h5>
&lt;p>The fight against the carrier cache problem started two years ago. The carrier even cached cnpm data. Worse, their cache servers were not only slow like a turtle in a marathon, they also crashed frequently, so I just wanted to write code but had to face a wall of red errors.&lt;/p>
&lt;h5 id="fix">Fix&lt;/h5>
&lt;p>&lt;code>iptables -I FORWARD -p tcp -m tcp -m ttl --ttl-gt 20 -m ttl --ttl-lt 30 -j DROP&lt;/code>&lt;/p></description></item><item><title>Use Nginx and mod_pagespeed to Convert Images to WebP on the Fly</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191007-serve-webp-on-the-fly-with-nginx-and-mod_pagespeed/</link><pubDate>Mon, 07 Oct 2019 10:35:22 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191007-serve-webp-on-the-fly-with-nginx-and-mod_pagespeed/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://nova.moe/serve-webp-on-the-fly-with-nginx-and-mod_pagespeed/" target="_blank" rel="noopener">Use Nginx and mod_pagespeed to Convert Images to WebP on the Fly&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="compile-ngx_pagespeed">Compile ngx_pagespeed&lt;/h4>
&lt;blockquote>
&lt;p>First make sure Nginx is built with &lt;code>--with-compat&lt;/code>, so we do not need to rebuild Nginx from scratch.&lt;/p>
&lt;p>incubator: &lt;a href="https://github.com/apache/incubator-pagespeed-ngx.git" target="_blank" rel="noopener">https://github.com/apache/incubator-pagespeed-ngx.git&lt;/a>&lt;/p>&lt;/blockquote>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Switch to the nginx source directory and configure the build&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>./configure --with-compat --add-dynamic-module&lt;span style="color:#f92672">=&lt;/span>../incubator-pagespeed-ngx
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Build modules&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>make modules
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Copy the built module into the nginx modules directory&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo cp objs/ngx_pagespeed.so /etc/nginx/modules/
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Create the cache directory for converted images&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo mkdir -p /var/ngx_pagespeed_cache
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo chown -R www-data:www-data /var/ngx_pagespeed_cache
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-nginx" data-lang="nginx">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">load_module&lt;/span> &lt;span style="color:#e6db74">modules/ngx_pagespeed.so&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># enable pagespeed module on this server block
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">pagespeed&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Needs to exist and be writable by nginx. Use tmpfs for best performance.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">pagespeed&lt;/span> &lt;span style="color:#e6db74">FileCachePath&lt;/span> &lt;span style="color:#e6db74">/var/ngx_pagespeed_cache&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Ensure requests for pagespeed optimized resources go to the pagespeed handler
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># and no extraneous headers get set.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">location&lt;/span> ~ &lt;span style="color:#e6db74">&amp;#34;\.pagespeed\.([a-z]\.)?[a-z]&lt;/span>{&lt;span style="color:#f92672">2}\.[^.]{10}\.[^.]+&amp;#34;&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">add_header&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">location&lt;/span> ~ &lt;span style="color:#e6db74">&amp;#34;^/pagespeed_static/&amp;#34;&lt;/span> { }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">location&lt;/span> ~ &lt;span style="color:#e6db74">&amp;#34;^/ngx_pagespeed_beacon$&amp;#34;&lt;/span> { }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">pagespeed&lt;/span> &lt;span style="color:#e6db74">RewriteLevel&lt;/span> &lt;span style="color:#e6db74">CoreFilters&lt;/span>;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The last line (&lt;code>pagespeed RewriteLevel CoreFilters;&lt;/code>) specifies the enabled optimizations. It includes basic filters such as:&lt;/p></description></item><item><title>Is there a regular expression to detect a valid regular expression?</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191004-is-there-a-regular-expression-to-detect-a-valid-regular-expression/</link><pubDate>Fri, 04 Oct 2019 14:44:38 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20191004-is-there-a-regular-expression-to-detect-a-valid-regular-expression/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://stackoverflow.com/questions/172303/is-there-a-regular-expression-to-detect-a-valid-regular-expression" target="_blank" rel="noopener">Is there a regular expression to detect a valid regular expression?&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>This is a recursive regex, and is not supported by many regex engines. PCRE based ones should support it.&lt;/p>
&lt;pre tabindex="0">&lt;code>/
^ # start of string
( # first group start
(?:
(?:[^?+*{}()[\]\\|]+ # literals and ^, $
| \\. # escaped characters
| \[ (?: \^?\\. | \^[^\\] | [^\\^] ) # character classes
(?: [^\]\\]+ | \\. )* \]
| \( (?:\?[:=!]|\?&amp;lt;[=!]|\?&amp;gt;)? (?1)?? \) # parenthesis, with recursive content
| \(\? (?:R|[+-]?\d+) \) # recursive matching
)
(?: (?:[?+*]|\{\d+(?:,\d*)?\}) [?+]? )? # quantifiers
| \| # alternative
)* # repeat content
) # end first group
$ # end of string
/
&lt;/code>&lt;/pre>&lt;p>Without whitespace and comments&lt;/p></description></item><item><title>Force file download with Nginx</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190819-force-file-download-with-nginx/</link><pubDate>Mon, 19 Aug 2019 12:12:32 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190819-force-file-download-with-nginx/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://coderwall.com/p/3yb8vg/force-file-download-with-nginx" target="_blank" rel="noopener">Force file download with Nginx&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;code>add_header Content-Disposition 'attachment;';&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-nginx" data-lang="nginx">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">server&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">listen&lt;/span> &lt;span style="color:#ae81ff">80&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">server_name&lt;/span> &lt;span style="color:#e6db74">my.domain.com&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">location&lt;/span> ~ &lt;span style="color:#e6db74">^.*/(?P&amp;lt;request_basename&amp;gt;[^/]+\.(mp3))$&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">root&lt;/span> &lt;span style="color:#e6db74">/path/to/mp3/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">add_header&lt;/span> &lt;span style="color:#e6db74">Content-Disposition&lt;/span> &lt;span style="color:#e6db74">&amp;#39;attachment&lt;/span>; &lt;span style="color:#f92672">filename=&amp;#34;$request_basename&amp;#34;&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-nginx" data-lang="nginx">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">listen&lt;/span> &lt;span style="color:#ae81ff">80&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">server_name&lt;/span> &lt;span style="color:#e6db74">backup.baifu-tech.net&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">root&lt;/span> &lt;span style="color:#e6db74">/data/backup/rechargecent-mago&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">location&lt;/span> &lt;span style="color:#e6db74">/&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">auth_basic&lt;/span> &lt;span style="color:#e6db74">&amp;#34;baifu&lt;/span> &lt;span style="color:#e6db74">backup&lt;/span> &lt;span style="color:#e6db74">center&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">auth_basic_user_file&lt;/span> &lt;span style="color:#e6db74">/etc/nginx/ssl/htpasswd&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">autoindex&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">autoindex_exact_size&lt;/span> &lt;span style="color:#66d9ef">off&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">autoindex_localtime&lt;/span> &lt;span style="color:#66d9ef">on&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Shell Script Study Notes</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190807-mylxsw-growing-up-shell/</link><pubDate>Wed, 07 Aug 2019 15:14:08 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190807-mylxsw-growing-up-shell/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://github.com/mylxsw/growing-up/blob/master/doc/Shell%E8%84%9A%E6%9C%AC%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0.md" target="_blank" rel="noopener">Shell Script Study Notes&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="arithmetic-operations">Arithmetic operations&lt;/h3>
&lt;pre>&lt;code>val=`expr $a + $b`
&lt;/code>&lt;/pre>
&lt;h3 id="operators">Operators&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Symbol&lt;/th>
&lt;th>Description&lt;/th>
&lt;th>Example&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>!&lt;/td>
&lt;td>NOT&lt;/td>
&lt;td>[ ! false ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-o&lt;/td>
&lt;td>OR&lt;/td>
&lt;td>[ $a -lt 20 -o $b -gt 20 ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-a&lt;/td>
&lt;td>AND&lt;/td>
&lt;td>[ $a -lt 20 -a $b -gt 20 ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>=&lt;/td>
&lt;td>equality check&lt;/td>
&lt;td>[ $a = $b ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>!=&lt;/td>
&lt;td>inequality check&lt;/td>
&lt;td>[ $a != $b ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-z&lt;/td>
&lt;td>string length is 0, returns true if 0&lt;/td>
&lt;td>[ -z $a ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-n&lt;/td>
&lt;td>string length is not 0, returns true if not 0&lt;/td>
&lt;td>[ -n $a ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>str&lt;/td>
&lt;td>check whether string is empty, true if not&lt;/td>
&lt;td>[ $a ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-b&lt;/td>
&lt;td>check whether file is a block device&lt;/td>
&lt;td>[ -b $file ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-c&lt;/td>
&lt;td>check whether file is a character device&lt;/td>
&lt;td>..&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-d&lt;/td>
&lt;td>check whether file is a directory&lt;/td>
&lt;td>[ -d $file ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-f&lt;/td>
&lt;td>check whether file is a regular file&lt;/td>
&lt;td>[ -f $file ]&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-r&lt;/td>
&lt;td>check whether file is readable&lt;/td>
&lt;td>..&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-w&lt;/td>
&lt;td>check whether file is writable&lt;/td>
&lt;td>..&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-x&lt;/td>
&lt;td>check whether file is executable&lt;/td>
&lt;td>..&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-s&lt;/td>
&lt;td>check whether file is empty&lt;/td>
&lt;td>..&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>-e&lt;/td>
&lt;td>check whether file exists&lt;/td>
&lt;td>..&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="special-variables">Special variables&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Variable&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$0&lt;/td>
&lt;td>file name of the current script&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$n&lt;/td>
&lt;td>arguments passed to a script or function; n is the position&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$#&lt;/td>
&lt;td>number of arguments passed to a script or function&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$*&lt;/td>
&lt;td>all arguments as a single word, e.g., &amp;ldquo;1 2 3&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$@&lt;/td>
&lt;td>all arguments as separate words, e.g., &amp;ldquo;1&amp;rdquo; &amp;ldquo;2&amp;rdquo; &amp;ldquo;3&amp;rdquo;&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$?&lt;/td>
&lt;td>exit status of the last command or return value of a function&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$$&lt;/td>
&lt;td>current shell process ID; for scripts, the PID of the script process&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="posix-program-exit-statuses">POSIX program exit statuses&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Code&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0&lt;/td>
&lt;td>command exited successfully&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;gt; 0&lt;/td>
&lt;td>failure during redirection or word expansion (~, variables, commands, arithmetic, and word splitting)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>1 - 125&lt;/td>
&lt;td>command exited unsuccessfully; specific meanings are command-defined&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>126&lt;/td>
&lt;td>command found but file is not executable&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>127&lt;/td>
&lt;td>command not found&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;gt; 128&lt;/td>
&lt;td>command died from a signal&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="inputoutput-redirection">Input/output redirection&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Command&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>command &amp;gt; file&lt;/td>
&lt;td>redirect output to file&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>command &amp;gt; file&lt;/td>
&lt;td>redirect output to file by appending&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n &amp;gt; file&lt;/td>
&lt;td>redirect file descriptor n to file&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n &amp;raquo; file&lt;/td>
&lt;td>redirect file descriptor n to file by appending&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n &amp;gt;&amp;amp; m&lt;/td>
&lt;td>merge output file m and n&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>n &amp;lt;&amp;amp; m&lt;/td>
&lt;td>merge input file m and n&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&amp;laquo; tag&lt;/td>
&lt;td>use content between start tag and end tag as input&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="file-include">File include&lt;/h3>
&lt;p>Use &lt;code>.&lt;/code> or &lt;code>source&lt;/code> to include files.&lt;/p></description></item><item><title>Excessive Errors from `sentry.lang.javascript.processor: SoftTimeLimitExceeded()`</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190725-getsentry-sentry-issues-4386/</link><pubDate>Thu, 25 Jul 2019 14:25:56 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190725-getsentry-sentry-issues-4386/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://github.com/getsentry/sentry/issues/4386" target="_blank" rel="noopener">Excessive Errors from &lt;code>sentry.lang.javascript.processor: SoftTimeLimitExceeded()&lt;/code>&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://forum.sentry.io/t/counters-0-queue-is-rising-continuously/5655/6" target="_blank" rel="noopener">Counters-0 queue is rising continuously&lt;/a>&lt;/li>
&lt;/ul>
&lt;h4 id="excessive-errors-from-sentrylangjavascriptprocessor-softtimelimitexceeded">Excessive Errors from &lt;code>sentry.lang.javascript.processor: SoftTimeLimitExceeded()&lt;/code>&lt;/h4>
&lt;p>mattrobenolt:&lt;/p>
&lt;p>Was just going to propose this. :) Unfortunately, this is just how this feature works. If it&amp;rsquo;s not useful and is just wasting resources, it&amp;rsquo;s easy to disable globally by setting &lt;code>SENTRY_SCRAPE_JAVASCRIPT_CONTEXT = False&lt;/code> in your &lt;code>sentry.conf.py&lt;/code> or per project in the UI.&lt;/p>
&lt;p>Other than that, there&amp;rsquo;s not much else we can do here since the feature is effectively working as intended.&lt;/p></description></item><item><title>Python Telegram Bot</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190711-python-telegram-bot/</link><pubDate>Thu, 11 Jul 2019 14:03:25 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190711-python-telegram-bot/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://zaoldyeck.medium.com/%E6%89%8B%E6%8A%8A%E6%89%8B%E6%95%99%E4%BD%A0%E6%80%8E%E9%BA%BC%E6%89%93%E9%80%A0-telegram-bot-a7b539c3402a" target="_blank" rel="noopener">Part 1: Build a Telegram Bot Step by Step&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zaoldyeck.medium.com/%E5%88%A9%E7%94%A8-olami-open-api-%E7%82%BA-chatbot-%E5%A2%9E%E5%8A%A0-nlp-%E5%8A%9F%E8%83%BD-e6b37940913d" target="_blank" rel="noopener">Part 2: Add NLP to the Chatbot&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://zaoldyeck.medium.com/add-custom-skill-into-chatbot-cef9bfeeef52" target="_blank" rel="noopener">Part 3: Add New Skills to the Chatbot&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://hackmd.io/@truckski/HkgaMUc24" target="_blank" rel="noopener">Python Telegram Bot Tutorial&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://kaive.me/2018/07/15/Python-telegram-bot/" target="_blank" rel="noopener">Write a Simple Telegram Bot with Python&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/python-telegram-bot/python-telegram-bot/wiki/Code-snippets" target="_blank" rel="noopener">Code snippets&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Fixing Disk Space Not Freed on Linux</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190710-linux-command-line-du-dh-lsof/</link><pubDate>Wed, 10 Jul 2019 09:57:33 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190710-linux-command-line-du-dh-lsof/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://www.itread01.com/content/1542767890.html" target="_blank" rel="noopener">Fixing Disk Space Not Freed on Linux&lt;/a>&lt;/li>
&lt;/ul>
&lt;h5 id="use-df--ah-and-du--h---max-depth1">Use &lt;code>df -ah&lt;/code> and &lt;code>du -h --max-depth=1&lt;/code>&lt;/h5>
&lt;p>The total from &lt;code>du&lt;/code> is far smaller than the total reported by &lt;code>df&lt;/code>.&lt;/p>
&lt;p>When a process deletes files but keeps running, the files are not actually removed, so disk space is not freed, and those files are not counted.&lt;/p>
&lt;p>&lt;code>lsof |grep delete&lt;/code>&lt;/p></description></item><item><title>Sentry Source Code Development Notes</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190703-python-sentry-develop-notes-and-uwsgi-log-format/</link><pubDate>Wed, 03 Jul 2019 11:53:15 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190703-python-sentry-develop-notes-and-uwsgi-log-format/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://chroming.gitlab.io/2018/09/26/edit_sentry_source_code/" target="_blank" rel="noopener">Sentry Source Code Development Notes&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://uwsgi-docs.readthedocs.io/en/latest/LogFormat.html" target="_blank" rel="noopener">Formatting uWSGI requests logs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.postgresql.org/docs/9.3/libpq-pgpass.html" target="_blank" rel="noopener">PostgreSQL: Documentation: 9.3: The Password File&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>OpenResty + Redis: Block High-Frequency IPs</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190627-openrestyredis/</link><pubDate>Thu, 27 Jun 2019 11:18:46 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190627-openrestyredis/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://www.centos.bz/2018/11/openrestyredis%E6%8B%A6%E6%88%AA%E9%AB%98%E9%A2%91%E8%AE%BF%E9%97%AEip/" target="_blank" rel="noopener">OpenResty + Redis: Block High-Frequency IPs&lt;/a>&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-nginx" data-lang="nginx">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">init_by_lua_block&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">redis&lt;/span> = &lt;span style="color:#e6db74">require&lt;/span> &lt;span style="color:#e6db74">&amp;#34;redis&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">client&lt;/span> = &lt;span style="color:#e6db74">redis.connect(&amp;#39;127.0.0.1&amp;#39;,&lt;/span> &lt;span style="color:#ae81ff">6379&lt;/span>&lt;span style="color:#e6db74">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#960050;background-color:#1e0010">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">server&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">listen&lt;/span> &lt;span style="color:#ae81ff">8080&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">location&lt;/span> &lt;span style="color:#e6db74">/&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">access_by_lua_file&lt;/span> &lt;span style="color:#e6db74">/usr/local/nginx/conf/lua/block.lua&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">proxy_pass&lt;/span> &lt;span style="color:#e6db74">http://192.168.1.102:8000&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-lua" data-lang="lua">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- Redis-based IP rate limiting / blocking for OpenResty (ngx_lua)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- NOTE:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- This script assumes a global `client` variable is used/stored.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- Make sure `redis` module is available and `client` is initialized somewhere.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">isConnected&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> client:ping()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> &lt;span style="color:#66d9ef">function&lt;/span> &lt;span style="color:#a6e22e">createRedisConnection&lt;/span>()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> redis.connect(&lt;span style="color:#e6db74">&amp;#34;127.0.0.1&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">6379&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">-- If the Redis connection fails, stop blocking (allow traffic)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> pcall(isConnected) &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- already connected (or ping succeeded)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- not connected; try reconnect&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> pcall(createRedisConnection) &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- Reconnect: this will reconnect Redis on every request&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- For high traffic, consider disabling reconnect (skip pcall), and allow/terminate directly via ngx.exit&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client &lt;span style="color:#f92672">=&lt;/span> createRedisConnection()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ngx.exit(ngx.OK)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> ttl &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">60&lt;/span> &lt;span style="color:#75715e">-- sampling window (seconds)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> bktimes &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">30&lt;/span> &lt;span style="color:#75715e">-- requests within window to trigger block&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> block_ttl &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">600&lt;/span> &lt;span style="color:#75715e">-- block duration after trigger (seconds)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> ip &lt;span style="color:#f92672">=&lt;/span> ngx.var.remote_addr
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">local&lt;/span> ipvtimes &lt;span style="color:#f92672">=&lt;/span> client:get(ip)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> ipvtimes &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> ipvtimes &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;-1&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- blocked&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ngx.exit(&lt;span style="color:#ae81ff">403&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> last_ttl &lt;span style="color:#f92672">=&lt;/span> client:ttl(ip)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- ngx.say(&amp;#34;key exist.ttl is &amp;#34;, last_ttl)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> last_ttl &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:set(ip, &lt;span style="color:#ae81ff">0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:expire(ip, ttl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- ngx.say(&amp;#34;ttl &amp;amp; vtimes recount&amp;#34;)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ngx.exit(ngx.OK)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">local&lt;/span> vtimes &lt;span style="color:#f92672">=&lt;/span> tonumber(client:get(ip)) &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> vtimes &lt;span style="color:#f92672">&amp;lt;&lt;/span> bktimes &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:set(ip, vtimes)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:expire(ip, last_ttl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- ngx.say(ip, &amp;#34; view &amp;#34;, vtimes, &amp;#34; times&amp;#34;)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ngx.exit(ngx.OK)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- ngx.say(ip, &amp;#34; will be block next time.&amp;#34;)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:set(ip, &lt;span style="color:#f92672">-&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:expire(ip, block_ttl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ngx.exit(ngx.OK)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- key does not exist&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:set(ip, &lt;span style="color:#ae81ff">1&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">-- ngx.say(ip, &amp;#34; view 1 times&amp;#34;)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> client:expire(ip, ttl)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> ngx.exit(ngx.OK)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">end&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Terraform Getting Started Notes</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190626-terraform-getting-started/</link><pubDate>Wed, 26 Jun 2019 17:22:52 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190626-terraform-getting-started/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://godleon.github.io/blog/DevOps/terraform-getting-started/" target="_blank" rel="noopener">Terraform Getting Started Notes&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>設定 Haproxy 以防止 DDOS 攻擊</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190617-haproxy-ddos/</link><pubDate>Mon, 17 Jun 2019 11:07:13 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190617-haproxy-ddos/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://blog.maxkit.com.tw/2016/05/haproxy-ddos.html" target="_blank" rel="noopener">設定 Haproxy 以防止 DDOS 攻擊&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="tcp-syn-flood-attacks">TCP syn flood attacks&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>vi /etc/sysctl.conf
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Protection from SYN flood&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>net.ipv4.tcp_syncookies &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>net.ipv4.conf.all.rp_filter &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>net.ipv4.tcp_max_syn_backlog &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">1024&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="slowloris-like-attacks">Slowloris like attacks&lt;/h3>
&lt;pre tabindex="0">&lt;code>defaults
option http-server-close
timeout http-request 5s
timeout connect 5s
timeout client 30s
timeout server 10s
timeout tunnel 1h
&lt;/code>&lt;/pre>&lt;h3 id="限制每一個-user-的連線數量">限制每一個 user 的連線數量&lt;/h3>
&lt;p>普通用戶瀏覽網站的網頁，或是從網站下載東西時，瀏覽器一般會建立 5-7 個 TCP 鏈接。當一個惡意 client 打開了大量 TCP 連線時，耗費大量資源，因此我們必須要限制同一個用戶的連線數量。&lt;/p>
&lt;p>但如果有很多使用者，是從某一個私有網段，透過 NAT 的方式連線到 Server 時，且實際上我們也不知道，到底哪一個會是 NAT 的轉址後的 IP，不知道該將哪個 IP 設定為白名單，這樣的限制就會造成問題，因此我們認為實際的環境，這樣的設定應該要保留不處理。&lt;/p>
&lt;p>以下是一個設定的範例，最重要的地方是在 frontend ft_web 區塊的設定。&lt;/p>
&lt;pre tabindex="0">&lt;code>global
stats socket ./haproxy.stats level admin
defaults
option http-server-close
mode http
timeout http-request 5s
timeout connect 5s
timeout server 10s
timeout client 30s
listen stats
bind 0.0.0.0:8880
stats enable
stats hide-version
stats uri /
stats realm HAProxy Statistics
stats auth admin:admin
frontend ft_web
bind 0.0.0.0:8080
# Table definition
stick-table type ip size 100k expire 30s store conn_cur
# Allow clean known IPs to bypass the filter
tcp-request connection accept if { src -f /etc/haproxy/whitelist.lst }
# Shut the new connection as long as the client has already 10 opened
tcp-request connection reject if { src_conn_cur ge 10 }
tcp-request connection track-sc1 src
# Split static and dynamic traffic since these requests have different impacts on the servers
use_backend bk_web_static if { path_end .jpg .png .gif .css .js }
default_backend bk_web
# Dynamic part of the application
backend bk_web
balance roundrobin
cookie MYSRV insert indirect nocache
server srv1 192.168.1.2:80 check cookie srv1 maxconn 100
server srv2 192.168.1.3:80 check cookie srv2 maxconn 100
# Static objects
backend bk_web_static
balance roundrobin
server srv1 192.168.1.2:80 check maxconn 1000
server srv2 192.168.1.3:80 check maxconn 1000
&lt;/code>&lt;/pre>&lt;h3 id="限制每個-user-產生新連線的速率-limiting-the-connection-rate-per-user">限制每個 user 產生新連線的速率 Limiting the connection rate per user&lt;/h3>
&lt;p>惡意的使用者會在短時間內建立很多連線，但如果產生新連線的速度太高，就會消耗掉過多的資源服務一個使用者。&lt;/p></description></item><item><title>Kubernetes Runtime Explained in Plain Language</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190613-k8s-runtime/</link><pubDate>Thu, 13 Jun 2019 11:28:26 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190613-k8s-runtime/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://mp.weixin.qq.com/s?__biz=Mzg5Mjc3MjIyMA==&amp;amp;mid=2247543594&amp;amp;idx=1&amp;amp;sn=9083cff79ca7f5fb9d6fee08d1144989&amp;amp;source=41&amp;amp;poc_token=HL8MZmmjq-HF5O8fHC711sGwSQ1O9OuO5hGLL_px" target="_blank" rel="noopener">Kubernetes Runtime Explained in Plain Language&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Do you understand the Nginx request processing flow?</title><link>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190307-nginx/</link><pubDate>Thu, 07 Mar 2019 14:05:55 +0800</pubDate><guid>https://9855cc0f.linzeyan.pages.dev/posts/2019/20190307-nginx/</guid><description>&lt;ul>
&lt;li>&lt;a href="https://mp.weixin.qq.com/s/otQIhuLABU3omOLtRfJnZQ" target="_blank" rel="noopener">Do you understand the Nginx request processing flow?&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="11-processing-phases">11 processing phases&lt;/h3>
&lt;ol>
&lt;li>NGX_HTTP_POST_READ_PHASE:&lt;/li>
&lt;/ol>
&lt;p>A phase after receiving the full HTTP headers. It is before URI rewrite. Very few modules register in this phase, and it is skipped by default.&lt;/p>
&lt;ol start="2">
&lt;li>NGX_HTTP_SERVER_REWRITE_PHASE:&lt;/li>
&lt;/ol>
&lt;p>The phase that modifies the URI before matching the location, used for redirects. This is where rewrite directives in the server block but outside location are executed. While reading request headers, nginx selects the virtual host by host and port.&lt;/p></description></item></channel></rss>