Jekyll2024-01-05T16:06:16+00:00http://l4sh.github.io/feed.xmlLuis’ personal site!Just a personal site for me to post stuff I find interesting.
Luis SalazarSet up a dockerized wordpress instance behind nginx2019-01-10T00:00:00+00:002019-01-10T00:00:00+00:00http://l4sh.github.io/set-up-a-dockerized-wordpress-instance-behind-nginx<p>This is a quick recipe for installing a containerized WordPress
environment behind Nginx.</p>
<p>The configuration files as well as any revision can be found in the
<a href="https://github.com/l4sh/dockerized-wordpress">Github repo</a>.</p>
<p>Nginx will act as a reverse proxy serving the content through HTTPS to
the clients and forwarding the requests to the Docker container
running WordPress.</p>
<p>A graphical representation of this configuration would be</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> HTTPS HTTP
Port 443 Port 8000
[Browser] ----------> [Nginx] -----------> [Wordpress Container]
</code></pre></div></div>
<p>The project structure is the following</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>.
├── docker-compose.yml
├── nginx.conf
└── wp-content
</code></pre></div></div>
<p>If you did not download the files from the
<a href="https://github.com/l4sh/dockerized-wordpress">repo</a> create them, as
well as the <code class="language-plaintext highlighter-rouge">wp-content</code> folder.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>touch nginx.conf docker-compose.yml
mkdir wp-content
</code></pre></div></div>
<h2 id="nginx-configuration">Nginx configuration</h2>
<p>For the nginx part lets start with the <a href="https://gist.github.com/plentz/6737338">secure configuration
maintained by plentz</a>.</p>
<p>I already have a wildcard certificate in place for my domain. If you
need one and are managing your DNS records with Cloudflare <a href="https://bjornjohansen.no/wildcard-certificate-letsencrypt-cloudflare">this
guide</a>
might be of interest.</p>
<p>The site information to be used for this configuration is the following.</p>
<table>
<tbody>
<tr>
<td>Site domain</td>
<td>wordpress.example.com</td>
</tr>
<tr>
<td>WP Container Port</td>
<td>8000</td>
</tr>
<tr>
<td>Upstream name</td>
<td>wordpress</td>
</tr>
<tr>
<td>SSL Certificate path</td>
<td>/etc/letsencrypt/live/example.com/fullchain.pem</td>
</tr>
<tr>
<td>SSL Key path</td>
<td>/etc/letsencrypt/live/example.com/privkey.pem</td>
</tr>
<tr>
<td>SSL DHParam</td>
<td>/etc/letsencrypt/live/example.com/dhparam.pem</td>
</tr>
</tbody>
</table>
<p>Resulting in this nginx configuration file. I’ve added a <code class="language-plaintext highlighter-rouge"><-- CHANGEME</code>
indicator to highlight the lines that should be changed.</p>
<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># nginx.conf</span>
<span class="c1"># Configuration adapted from https://gist.github.com/plentz/6737338</span>
<span class="c1"># don't send the nginx version number in error pages and Server header</span>
<span class="k">server_tokens</span> <span class="no">off</span><span class="p">;</span>
<span class="c1"># config to don't allow the browser to render the page inside an frame or iframe</span>
<span class="c1"># and avoid clickjacking http://en.wikipedia.org/wiki/Clickjacking</span>
<span class="c1"># if you need to allow [i]frames, you can use SAMEORIGIN or even set an uri</span>
<span class="c1"># with ALLOW-FROM uri</span>
<span class="c1"># https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options</span>
<span class="k">add_header</span> <span class="s">X-Frame-Options</span> <span class="s">SAMEORIGIN</span><span class="p">;</span>
<span class="c1"># when serving user-supplied content, include a X-Content-Type-Options: nosniff</span>
<span class="c1"># header along with the Content-Type: header,</span>
<span class="c1"># to disable content-type sniffing on some browsers.</span>
<span class="c1"># https://www.owasp.org/index.php/List_of_useful_HTTP_headers</span>
<span class="c1"># currently suppoorted in IE > 8</span>
<span class="c1"># http://blogs.msdn.com/b/ie/archive/2008/09/02/ie8-security-part-vi-beta-2-update.aspx</span>
<span class="c1"># http://msdn.microsoft.com/en-us/library/ie/gg622941(v=vs.85).aspx</span>
<span class="c1"># 'soon' on Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=471020</span>
<span class="k">add_header</span> <span class="s">X-Content-Type-Options</span> <span class="s">nosniff</span><span class="p">;</span>
<span class="c1"># This header enables the Cross-site scripting (XSS) filter built into most</span>
<span class="c1"># recent web browsers.</span>
<span class="c1"># It's usually enabled by default anyway, so the role of this header is to</span>
<span class="c1"># re-enable the filter for</span>
<span class="c1"># this particular website if it was disabled by the user.</span>
<span class="c1"># https://www.owasp.org/index.php/List_of_useful_HTTP_headers</span>
<span class="k">add_header</span> <span class="s">X-XSS-Protection</span> <span class="s">"1</span><span class="p">;</span> <span class="k">mode=block"</span><span class="p">;</span>
<span class="c1"># with Content Security Policy (CSP) enabled(and a browser that supports it</span>
<span class="c1"># (http://caniuse.com/#feat=contentsecuritypolicy),</span>
<span class="c1"># you can tell the browser that it can only download content from the domains</span>
<span class="c1"># you explicitly allow</span>
<span class="c1"># http://www.html5rocks.com/en/tutorials/security/content-security-policy/</span>
<span class="c1"># https://www.owasp.org/index.php/Content_Security_Policy</span>
<span class="c1"># I need to change our application code so we can increase security by disabling</span>
<span class="c1"># 'unsafe-inline' 'unsafe-eval'</span>
<span class="c1"># directives for css and js (if you have inline css or js, you will need to keep</span>
<span class="c1"># it too).</span>
<span class="c1"># more: http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful</span>
<span class="k">add_header</span> <span class="s">Content-Security-Policy</span> <span class="s">"default-src</span> <span class="s">'self'</span><span class="p">;</span> <span class="k">script-src</span> <span class="s">'self'</span> <span class="s">'unsafe-inline'</span> <span class="s">'unsafe-eval'</span> <span class="s">https://ssl.google-analytics.com</span> <span class="s">https://assets.zendesk.com</span> <span class="s">https://connect.facebook.net</span><span class="p">;</span> <span class="k">img-src</span> <span class="s">'self'</span> <span class="s">https://ssl.google-analytics.com</span> <span class="s">https://s-static.ak.facebook.com</span> <span class="s">https://assets.zendesk.com</span><span class="p">;</span> <span class="k">style-src</span> <span class="s">'self'</span> <span class="s">'unsafe-inline'</span> <span class="s">https://fonts.googleapis.com</span> <span class="s">https://assets.zendesk.com</span><span class="p">;</span> <span class="k">font-src</span> <span class="s">'self'</span> <span class="s">https://themes.googleusercontent.com</span><span class="p">;</span> <span class="k">frame-src</span> <span class="s">https://assets.zendesk.com</span> <span class="s">https://www.facebook.com</span> <span class="s">https://s-static.ak.facebook.com</span> <span class="s">https://tautt.zendesk.com</span><span class="p">;</span> <span class="k">object-src</span> <span class="s">'none'"</span><span class="p">;</span>
<span class="k">upstream</span> <span class="s">wordpress</span> <span class="p">{</span>
<span class="kn">server</span> <span class="nf">127.0.0.1</span><span class="p">:</span><span class="mi">8000</span> <span class="s">fail_timeout=0</span><span class="p">;</span> <span class="c1"># <-- CHANGEME</span>
<span class="p">}</span>
<span class="c1"># Redirect traffic to SSL</span>
<span class="k">server</span> <span class="p">{</span>
<span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
<span class="kn">listen</span> <span class="s">[::]:80</span><span class="p">;</span>
<span class="kn">server_name</span> <span class="s">wordpress.example.com</span><span class="p">;</span> <span class="c1"># <-- CHANGEME</span>
<span class="kn">return</span> <span class="mi">301</span> <span class="s">https://</span><span class="nv">$host$request_uri</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">server</span> <span class="p">{</span>
<span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span> <span class="s">http2</span><span class="p">;</span>
<span class="kn">listen</span> <span class="s">[::]:443</span> <span class="s">ssl</span> <span class="s">http2</span><span class="p">;</span>
<span class="kn">server_name</span> <span class="s">wordpress.example.com</span><span class="p">;</span> <span class="c1"># <-- CHANGEME</span>
<span class="kn">ssl_certificate</span> <span class="n">/etc/letsencrypt/live/example.com/fullchain.pem</span><span class="p">;</span> <span class="c1"># <-- CHANGEME</span>
<span class="kn">ssl_certificate_key</span> <span class="n">/etc/letsencrypt/live/example.com/privkey.pem</span><span class="p">;</span> <span class="c1"># <-- CHANGEME</span>
<span class="c1"># enable session resumption to improve https performance</span>
<span class="c1"># http://vincent.bernat.im/en/blog/2011-ssl-session-reuse-rfc5077.html</span>
<span class="kn">ssl_session_cache</span> <span class="s">shared:SSL:50m</span><span class="p">;</span>
<span class="kn">ssl_session_timeout</span> <span class="s">1d</span><span class="p">;</span>
<span class="kn">ssl_session_tickets</span> <span class="no">off</span><span class="p">;</span>
<span class="c1"># Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits</span>
<span class="kn">ssl_dhparam</span> <span class="n">/etc/letsencrypt/live/example.com/dhparam.pem</span><span class="p">;</span> <span class="c1"># <-- CHANGEME</span>
<span class="c1"># enables server-side protection from BEAST attacks</span>
<span class="c1"># http://blog.ivanristic.com/2013/09/is-beast-still-a-threat.html</span>
<span class="kn">ssl_prefer_server_ciphers</span> <span class="no">on</span><span class="p">;</span>
<span class="c1"># disable SSLv3(enabled by default since nginx 0.8.19) since it's less secure then TLS http://en.wikipedia.org/wiki/Secure_Sockets_Layer#SSL_3.0</span>
<span class="kn">ssl_protocols</span> <span class="s">TLSv1</span> <span class="s">TLSv1.1</span> <span class="s">TLSv1.2</span><span class="p">;</span>
<span class="c1"># ciphers chosen for forward secrecy and compatibility</span>
<span class="c1"># http://blog.ivanristic.com/2013/08/configuring-apache-nginx-and-openssl-for-forward-secrecy.html</span>
<span class="kn">ssl_ciphers</span> <span class="s">ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS</span><span class="p">;</span>
<span class="c1"># enable ocsp stapling (mechanism by which a site can convey certificate</span>
<span class="c1"># revocation information to visitors in a privacy-preserving, scalable manner)</span>
<span class="c1"># http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox/</span>
<span class="kn">resolver</span> <span class="mi">8</span><span class="s">.8.8.8</span> <span class="mi">8</span><span class="s">.8.4.4</span><span class="p">;</span>
<span class="kn">ssl_stapling</span> <span class="no">on</span><span class="p">;</span>
<span class="kn">ssl_stapling_verify</span> <span class="no">on</span><span class="p">;</span>
<span class="c1"># ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;</span>
<span class="c1"># config to enable HSTS(HTTP Strict Transport Security)</span>
<span class="c1"># https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security</span>
<span class="c1"># to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping</span>
<span class="c1"># also https://hstspreload.org/</span>
<span class="kn">add_header</span> <span class="s">Strict-Transport-Security</span> <span class="s">"max-age=31536000</span><span class="p">;</span> <span class="kn">includeSubdomains</span><span class="p">;</span> <span class="kn">preload"</span><span class="p">;</span>
<span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
<span class="kn">proxy_pass</span> <span class="s">http://wordpress</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">;</span>
<span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="s">https</span><span class="p">;</span>
<span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span> <span class="nv">$remote_addr</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>After finishing any modifications create a link to this file from the
respective nginx folder. E.g.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo ln</span> <span class="nt">-s</span> /srv/www/dockerized-wordpress/nginx.conf /etc/nginx/sites-enabled/wordpress
</code></pre></div></div>
<p>Check that the file works and reload the nginx configuration.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo nginx -t
sudo systemctl reload nginx
</code></pre></div></div>
<h2 id="docker-configuration">Docker configuration</h2>
<p>For the docker configuration I’ll be running the <a href="https://hub.docker.com/_/wordpress/">official
WordPress image</a> and a compose
file very similar to <a href="https://docs.docker.com/compose/wordpress/">the one found in the Docker documentation.
</a></p>
<p><code class="language-plaintext highlighter-rouge">wp-content</code> will be mounted separately and points to a folder inside
within the project. This will allow us to apply version control and
back up any modification with more ease.</p>
<p>In my case the environment is for development and experimentation, so
I’ll be setting up a multisite with some developer friendly
options. These settings are commented out for now. In the last section
you can see how to create a multisite. If you’re looking on how to
create a multisite read that section before running the containers.</p>
<p><em>NOTE: It’s important to know that any change in
<code class="language-plaintext highlighter-rouge">WORDPRESS_CONFIG_EXTRA</code> won’t be added to <code class="language-plaintext highlighter-rouge">wp-config.php</code> after the
container volume is created. <a href="https://github.com/docker-library/wordpress/issues/333">Check this issue
[docker-library/wordpress/issues/333]</a>.
A way to work around this is to remove the <code class="language-plaintext highlighter-rouge">wp-config.php</code> file after
any update to <code class="language-plaintext highlighter-rouge">WORDPRESS_CONFIG_EXTRA</code> or remove the volume and allow
it to recreate it.</em></p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># docker-compose.yml</span>
<span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.3'</span>
<span class="na">services</span><span class="pi">:</span>
<span class="na">db</span><span class="pi">:</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">mysql:5.7</span>
<span class="na">volumes</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">db_data:/var/lib/mysql</span>
<span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
<span class="na">environment</span><span class="pi">:</span>
<span class="na">MYSQL_ROOT_PASSWORD</span><span class="pi">:</span> <span class="s">wproot</span>
<span class="na">MYSQL_DATABASE</span><span class="pi">:</span> <span class="s">wordpress</span>
<span class="na">MYSQL_USER</span><span class="pi">:</span> <span class="s">wordpress</span>
<span class="na">MYSQL_PASSWORD</span><span class="pi">:</span> <span class="s">wordpress</span>
<span class="na">wordpress</span><span class="pi">:</span>
<span class="na">depends_on</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">db</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">wordpress:latest</span>
<span class="na">ports</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s2">"</span><span class="s">8000:80"</span>
<span class="na">volumes</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">wp_data:/var/www/html</span>
<span class="pi">-</span> <span class="s">./wp-content:/var/www/html/wp-content</span>
<span class="na">restart</span><span class="pi">:</span> <span class="s">unless-stopped</span>
<span class="na">environment</span><span class="pi">:</span>
<span class="na">WORDPRESS_DB_HOST</span><span class="pi">:</span> <span class="s">db:3306</span>
<span class="na">WORDPRESS_DB_USER</span><span class="pi">:</span> <span class="s">wordpress</span>
<span class="na">WORDPRESS_DB_PASSWORD</span><span class="pi">:</span> <span class="s">wordpress</span>
<span class="na">WORDPRESS_CONFIG_EXTRA</span><span class="pi">:</span> <span class="pi">|</span>
<span class="s">/* Site URL */</span>
<span class="s">define('WP_HOME', 'https://wordpress.example.com'); # <-- CHANGEME</span>
<span class="s">define('WP_SITEURL', 'https://wordpress.example.com'); # <-- CHANGEME</span>
<span class="s">/* Developer friendly settings */</span>
<span class="s"># define('SCRIPT_DEBUG', true);</span>
<span class="s"># define('CONCATENATE_SCRIPTS', false);</span>
<span class="s"># define('WP_DEBUG', true);</span>
<span class="s"># define('WP_DEBUG_LOG', true);</span>
<span class="s"># define('SAVEQUERIES', true);</span>
<span class="s">/* Multisite */</span>
<span class="s"># define('WP_ALLOW_MULTISITE', true );</span>
<span class="s"># define('MULTISITE', true);</span>
<span class="s"># define('SUBDOMAIN_INSTALL', false);</span>
<span class="s"># define('DOMAIN_CURRENT_SITE', 'wordpress.example.com'); # <-- CHANGEME</span>
<span class="s"># define('PATH_CURRENT_SITE', '/');</span>
<span class="s"># define('SITE_ID_CURRENT_SITE', 1);</span>
<span class="s"># define('BLOG_ID_CURRENT_SITE', 1);</span>
<span class="na">volumes</span><span class="pi">:</span>
<span class="na">db_data</span><span class="pi">:</span> <span class="pi">{}</span>
<span class="na">wp_data</span><span class="pi">:</span> <span class="pi">{}</span>
</code></pre></div></div>
<p>Once the configuration is done run</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up
</code></pre></div></div>
<p>If everything went ok you should be able to go to your site URL (in
this case https://wordpress.example.com) and see the classic WordPress
5 minute install wizard (which now is more like 1 minute since some of it
has been preconfigured).</p>
<p>Only one more thing to do. Add your user to the <code class="language-plaintext highlighter-rouge">www-data</code> group and
change the owner of the <code class="language-plaintext highlighter-rouge">wp-content</code> folder to
<code class="language-plaintext highlighter-rouge">www-data:www-data</code>. Otherwise it will ask for information on where to
store any plugin or theme when attempting to install.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chown -Rf www-data:www-data wp-content
</code></pre></div></div>
<p>The site should be ready to start development now.</p>
<h2 id="wordpress-multisite">Wordpress Multisite</h2>
<p>The process for creating a multisite is a little different, and a
little annoying to be honest. If you know a quicker and better way
please let me know.</p>
<p>These instructions are for subdirectories multisites. I’ve not yet
tried out a subdomains configuration this way.</p>
<p>We need to follow the <a href="https://codex.wordpress.org/Create_A_Network">Create a Network
guide</a> to some extent,
but since we’re to lazy to just go into the container to manually
delete the <code class="language-plaintext highlighter-rouge">wp-config.php</code> file we’ll just remove and recreate the
volume after updating the configuration. The database and any custom
addition should be left intact (that is if they were done in the
<code class="language-plaintext highlighter-rouge">wp-content</code> folder).</p>
<p>The docker way would be to first run the WordPress installation with
the <code class="language-plaintext highlighter-rouge">WP_ALLOW_MULTISITE</code> option enabled. That section of the
<code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file should look like this.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
WORDPRESS_CONFIG_EXTRA: |
...
/* Multisite */
define('WP_ALLOW_MULTISITE', true );
# define('MULTISITE', true);
# define('SUBDOMAIN_INSTALL', false);
# define('DOMAIN_CURRENT_SITE', 'wordpress.example.com'); # <-- CHANGEME
# define('PATH_CURRENT_SITE', '/');
# define('SITE_ID_CURRENT_SITE', 1);
# define('BLOG_ID_CURRENT_SITE', 1);
...
</code></pre></div></div>
<p>Run the containers</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up
</code></pre></div></div>
<p>Go to the site URL and perform the installation.</p>
<p>After installing go to <a href="https://codex.wordpress.org/Tools_Network_Screen">Administration > Tools > Network
Setup</a> and enter the
required information. Make sure to select Subdirectories.</p>
<p>After the process is finished and WordPress now displays which
settings to add to the <code class="language-plaintext highlighter-rouge">wp-config.php</code> file update the
<code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file to uncomment the rest of the multisite
settings.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ...
WORDPRESS_CONFIG_EXTRA: |
...
/* Multisite */
define('WP_ALLOW_MULTISITE', true );
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', false);
define('DOMAIN_CURRENT_SITE', 'wordpress.example.com'); # <-- CHANGEME
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);
...
</code></pre></div></div>
<p>Since it won’t add the updated configuration we’ll just take down the
service, delete the <code class="language-plaintext highlighter-rouge">wp_data</code> volume and bring it back up.</p>
<p>The name of the volume can be different, make sure to check first. But
essentially the process should be.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose down
docker volume rm wp_data
docker-compose up
</code></pre></div></div>
<p>Now you should have a WordPress multisite</p>l4shThis is a quick recipe for installing a containerized WordPress environment behind Nginx.Some pointers on installing mod_wsgi on Windows2018-10-08T00:00:00+00:002018-10-08T00:00:00+00:00http://l4sh.github.io/some-pointers-on-installing-mod_wsgi-on-windows<p>Recently I needed to install mod_wsgi in a Windows box and this proved to be a little bit tricky. Granted this was a different base installation from the one recommended by the mod_wsgi developer and I’m not really a Windows user, but I thought this might help someone with the same issues.</p>
<p>If you can choose the Apache httpd distribution to install, just use the one from Apache Lounge (<a href="https://apachelounge.com">https://apachelounge.com</a>). Is the one recommended in the mod_wsgi readme and the easiest one to build the package for. In my case I needed to install mod_wsgi onto an already running instance of Apache httpd.</p>
<p>The installation reflected in this post is a 32 bit one. Use the appropriate packages accordingly. Don’t mix different architechture packages.</p>
<h2 id="install-python">Install python</h2>
<p>Download the python installer from <a href="https://www.python.org/downloads/windows/">https://www.python.org/downloads/windows/</a> and execute it. Make sure that it’s being installed for all users (it will install to <code class="language-plaintext highlighter-rouge">C:\Program Files\Python</code>), you might need to use the “Customize installation” option.</p>
<p>Once the process is done open a prompt and try running the following commands.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">python -V
pip -V
</span></code></pre></div></div>
<p>It should print both the version of the python interpreter and the python installer.</p>
<h2 id="install-the-visual-c-compiler">Install the Visual C++ compiler</h2>
<p>Download and install “Build tools for Visual Studio” from <a href="https://visualstudio.microsoft.com/downloads/">https://visualstudio.microsoft.com/downloads/</a></p>
<p>Note that the “Visual C++ redistributable package” installs just the runtime components. For this we need the build tools package.</p>
<p>For more information check <a href="https://wiki.python.org/moin/WindowsCompilers">https://wiki.python.org/moin/WindowsCompilers</a></p>
<h2 id="upgrade-setuptools">Upgrade setuptools</h2>
<p>Open a command prompt and run:</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">pip install --upgrade setuptools
</span></code></pre></div></div>
<h2 id="install-mod_wsgi">Install mod_wsgi</h2>
<p>If the Apache httpd instance is installed in a path other than <code class="language-plaintext highlighter-rouge">C:\Apache\Apache24</code> (Apache Lounge’s default installation path) the <code class="language-plaintext highlighter-rouge">MOD_WSGI_APACHE_ROOTDIR</code> environment variable will need to be set. In this case I had to set it to the following path <code class="language-plaintext highlighter-rouge">C:\Program Files (x86)\Apache Software Foundation\Apache</code></p>
<p>From within a command prompt run:</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">pip install mod_wsgi
</span></code></pre></div></div>
<p>If everything is alright it should just display a message announcing that the package has been installed.</p>
<p>In my case it threw an error complaining about missing files in the <code class="language-plaintext highlighter-rouge">include</code> directory. This directory contains the headers needed for compiling the <code class="language-plaintext highlighter-rouge">mod_wsgi</code> module. This Apache httpd installation lacked some files and contained a few extra ones when compared with an Apache Lounge release installed in a test box. This issue was solved by copying the files from the Apache Lounge release and running again the <code class="language-plaintext highlighter-rouge">mod_wsgi</code> install command. If you need to do this, make sure that the major and minor versions of Apache httpd match.</p>
<h2 id="configure-mod_wsgi">Configure mod_wsgi</h2>
<p>After installing the <code class="language-plaintext highlighter-rouge">mod_wsgi</code> package some configuration is needed.</p>
<p>In order to obtain the proper paths for we need to run the following command:</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mod_wsgi-express module-config
</span></code></pre></div></div>
<p>It should return an output like:</p>
<div class="language-apache highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">LoadFile</span> "c:/program <span class="ss">files</span> (x86)/python37-32/python37.dll"
<span class="nc">LoadModule</span> wsgi_module "c:/program <span class="ss">files</span> (x86)/python37-32/lib/site-packages/mod_wsgi/server/mod_wsgi.cp37-win32.pyd"
WSGIPythonHome "c:/program <span class="ss">files</span> (x86)/python37-32"
</code></pre></div></div>
<p>This output will then need to be copied in the <code class="language-plaintext highlighter-rouge">conf/httpd.conf</code> file or other relevant configuration file in the Apache httpd installation folder.</p>
<p>Upon restarting Apache httpd the server control window should now display a new item in the status bar that reads “mod_wsgi”.</p>
<p>This should be enough to run a WSGI app with the module.</p>
<h2 id="testing-it-all">Testing it all</h2>
<p>Create a new python file (<code class="language-plaintext highlighter-rouge">C:\WSGIAppDir\wsgi_app.py</code>) with the following content.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">wsgi_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
<span class="n">output</span> <span class="o">=</span> <span class="sa">b</span><span class="s">'It Works!'</span>
<span class="n">status</span> <span class="o">=</span> <span class="s">'200 OK'</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">[(</span><span class="s">'Content-type'</span><span class="p">,</span> <span class="s">'text/plain'</span><span class="p">),</span>
<span class="p">(</span><span class="s">'Content-Length'</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">output</span><span class="p">)))]</span>
<span class="n">start_response</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
<span class="k">return</span> <span class="n">output</span>
</code></pre></div></div>
<p>Add a new section to the apache configuration, either in a new vhost configuration file or at the end of <code class="language-plaintext highlighter-rouge">httpd.conf</code> with the following content:</p>
<div class="language-apache highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="nl">IfModule</span><span class="sr"> wsgi_module</span><span class="p">>
</span> WSGIScriptAlias /test-wsgi/ "C:/WSGIAppDir/wsgi_app.py"
<span class="p"><</span><span class="nl">Directory</span><span class="sr"> "C:/WSGIAppDir"</span><span class="p">>
</span> <span class="nc">AllowOverride</span> <span class="ss">None</span>
<span class="nc">Options</span> <span class="ss">None</span>
<span class="nc">Order</span> deny,allow
<span class="nc">Allow</span> <span class="ss">from</span> <span class="ss">all</span>
<span class="p"></</span><span class="nl">Directory</span><span class="p">>
</</span><span class="nl">IfModule</span><span class="p">>
</span></code></pre></div></div>
<p>Notice the forward slashes in the directories instead of the MS Windows traditional backward slashes.</p>l4shRecently I needed to install mod_wsgi in a Windows box and this proved to be a little bit tricky. Granted this was a different base installation from the one recommended by the mod_wsgi developer and I’m not really a Windows user, but I thought this might help someone with the same issues.Signing a CSR with a custom CA using openSSL2016-04-13T00:00:00+00:002016-04-13T00:00:00+00:00http://l4sh.github.io/signing-a-csr-with-a-custom-ca-using-openssl<p>Sometimes we need to self-sign an SSL certificate and of course the internet is
filled with guides on how to do it. But what about when we need to sign an
already issued Certificate Signing Request with our own Certificate Authority?</p>
<p>Most of what I’ve found on this topic requires a few steps to get this done and
I must say that one of the most detailed guides I have found is at
https://jamielinux.com/docs/openssl-certificate-authority/index-full.html.
However this post does not intend to be a fully detailed guide, but a quick
and dirty way to to create a CA and sign a CSR without much hassle.</p>
<p>Be aware that if you’re using these for a public website the certificate
generated through this method will prompt an error on browsers since we’re
not a recognized CA. If you’d like a free valid certificate you could use
Let’s Encrypt or StartSSL.</p>
<p>If you’re going to be issuing a lot of certificates it’s best if you assemble
the structure explained in Jamie Nguyen’s article in order to have more control.</p>
<p>The process
The first thing to do is create a work directory and cd into it.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">mkdir custom_CA
</span><span class="gp">cd $</span>_
</code></pre></div></div>
<p>Then we generate a new CA key file inside this directory.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">openssl genrsa -out rootCA.key 2048
</span></code></pre></div></div>
<p>Now we need a certificate to sign the request. This will ask some information such as country, state, hostname, etc. Keep in mind that this is the CA’s information and not the site’s or entity that requests the certificate information.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
</span></code></pre></div></div>
<p>Then we’ll copy the CSR file into the folder we’re working on. In this case the file is named example_com.csr and currently resides in my home folder.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">cp ~/example_com.csr custom_CA/
</span></code></pre></div></div>
<p>Sign the CSR generating a new certificate.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">openssl x509 -req -in example_com.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out example_com.crt -days 365 -sha256
</span></code></pre></div></div>
<p>As a final step we verify that the generated certificate matches the signing request. For this we can use the modulus option in openssl and generate an MD5 hash and visually compare. The output of both commands should be identical.</p>
<div class="language-terminal highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">openssl req -noout -modulus -in example_com.csr | openssl md5
openssl x509 -noout -modulus -in example_com.crt | openssl md5
</span></code></pre></div></div>l4shSometimes we need to self-sign an SSL certificate and of course the internet is filled with guides on how to do it. But what about when we need to sign an already issued Certificate Signing Request with our own Certificate Authority?Rooteando el Huawei CM980 (Evolucion II)2016-02-07T00:00:00+00:002016-02-07T00:00:00+00:00http://l4sh.github.io/rooteando-el-huawei-cm980-evolucion-ii<p>Este es un post algo viejito. Lo cargo por acá en caso que alguien necesite documentación sobre como realizar el procedimiento.</p>
<p>Luego de un tiempo pariendo con el pésimo servicio que ofrece movilnet para GSM (deberían cambiar el slogan a: movilnet… contigo siempre… excepto en GSM) tenía dos opciones: cambiar de operadora o comprar un teléfono CDMA en la misma operadora. Al final opté por la segunda adquiriendo hace unos dias un Huawei CM980 Evolución II y no me arrepiento. Por primera vez en meses pude hacer una llamada desde mi teléfono que conectara sin tener que remarcar varias veces.</p>
<p>El teléfono es bastante rápido, almenos en comparación con su predecesor (UM840 Evolución), aunque después de un rato jugando con el sientes que falta algo, el preciado root. Hasta el miercoles no se había publicado información sobre como rootear este teléfono, cuando sitios como androidmovida.com y Android Venezuela publicaron la información con los pasos para hacerlo. El método fue descubierto aparentemente por un usuario de taringa llamado SHUNREY16: <a href="http://www.taringa.net/posts/celulares/15840908/root-para-huawei-cm980-en-Venezuela.html">http://www.taringa.net/posts/celulares/15840908/root-para-huawei-cm980-en-Venezuela.html</a></p>
<p>Si no sabes que es rootear un dispositivo o para que sirve lee el siguiente parrafo, si llegaste buscando como hacerlo completamente desde linux lee más abajo.</p>
<p>Rootear o rooting es el proceso de adquirir privilegios de administrador en un sistema tipo unix, en este caso un dispositivo android. En cuanto a por qué es tan importante para algunos de nosotros el root: principalmente es por el control, poder eliminar las aplicaciones que los fabricantes y operadoras te obligan a tener en el dispositivo que compraste por algun contrato o alianza que tengan con X empresa, poder bloquear anuncios publicitarios a nivel de sistema (bloqueando los hosts que sirven dichos anuncios), restringir el acceso de varias aplicaciones al nivel que creas conveniente y no al que el desarrollador cree conveniente, tomar capturas de pantalla del teléfono, agregar mas potencia a la shell del telefono, overclocking, underclocking y prácticamente cualquier operación que como administrador desees realizar en el telefono; si lo compraste debería ser realmente tuyo y controlado por ti.</p>
<p>Hasta acá todo muy bien, con la excepción que la información publicada esta enfocada a Windows, por lo que decidí publicar los pasos para hacerlo 100% desde GNU/Linux. Una de las principales diferencias entre los metodos publicados es la instalación de los controladores para el teléfono; como sabemos la compatibilidad de hardware es controlada por el kernel por lo que no es necesario instalar nada adicional para detectar el dispositivo (almenos no en Fedora 17 con linux 3.6.2)</p>
<p>Se necesitan los archivos <code class="language-plaintext highlighter-rouge">Recovery_Sonic_u8650_v6.0.1.0_r1</code> y <code class="language-plaintext highlighter-rouge">Superuser-3.0.7-efgh-signed</code> los cuales corresponden al recovery para el Huawei U8650 y Superuser.</p>
<p>Los pasos a seguir:
Copiar el archivo <code class="language-plaintext highlighter-rouge">Superuser-3.0.7-efgh-signed.zip</code> en la tarjeta SD (sin descomprimirlo).</p>
<p>En el telefono ir a <em>Configuración » Aplicaciones » Desarrollo</em> y activar Depuración USB.</p>
<p>Apagar el telefono (noté que en algunas ocasiones es necesario sacar e introducir de nuevo la batería para que pueda realizarse el siguiente paso).</p>
<p>Presionar y mantener presionados las teclas <code class="language-plaintext highlighter-rouge">VOL DOWN</code> + <code class="language-plaintext highlighter-rouge">PWR ON</code> para colocarlo en modo fastboot (debería quedarse en el logo de movilnet)</p>
<p>Descomprimir el archivo <code class="language-plaintext highlighter-rouge">Recovery_Sonic_u8650_v6.0.1.0_r1.zip</code>. Ir a la carpeta que se creó y dar permisos de ejecucion al script install-recovery-linux.sh y a la herramienta fastboot que se encuentra en la carpeta tools.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chmod +x install-recovery-linux.sh
chmod +x tools/fastboot-linux
</code></pre></div></div>
<p>Para comprobar que el teléfono es detectado por fastboot se puede correr y solicitar que liste los dispositivos (es necesario tener privilegios elevados en la maquina donde se ejecuta).</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo tools/fastboot-linux devices
</code></pre></div></div>
<p>En mi caso mostró <code class="language-plaintext highlighter-rouge">???????????? fastboot</code> lo que quiere decir que encontró un dispositivo, como es el único conectado asumiremos que es el CM980. Ahora toca correr el <code class="language-plaintext highlighter-rouge">install-recovery-linux.sh</code>.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sudo ./install-recovery-linux.sh
</code></pre></div></div>
<p>Lo cual debería dar esta salida</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>erasing ‘recovery’… OKAY
sending ‘recovery’ (4258 KB)… OKAY
writing ‘recovery’… OKAY
rebooting…
</code></pre></div></div>
<p>Luego de esto el teléfono se apaga, para confirmar que el recovery fue instalado es necesario iniciar el telefono en modo recovery presionando y manteniendo las teclas VOL UP + PWR ON. Debería mostrar algo como:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- reboot system now
- install zip fom sdcard
- wipe data/factory reset
- wipe cache partition
- backup and restore
- mounts and storage
- advanced
</code></pre></div></div>
<p>Para desplazarse por los menús se usan los botones <code class="language-plaintext highlighter-rouge">VOL UP</code>, <code class="language-plaintext highlighter-rouge">VOL DOWN</code> y <code class="language-plaintext highlighter-rouge">PWR ON</code> para seleccionar.</p>
<p>Seleccionamos <em>install zip from sdcard » choose zip from sdcard</em> y buscamos el archivo zip con el superuser que colocamos al principio (<code class="language-plaintext highlighter-rouge">Superuser-3.0.7-efgh-signed.zip</code>)</p>
<p>Luego de esto se muestra una pantalla preguntando si se desea realizar la acción con una advertencia en mayúsculas indicando que esto no puede ser desecho, y varias opciones de selección que dicen No. Nos desplazamos hasta <code class="language-plaintext highlighter-rouge">yes - - install Superuser-3.0.7-efgh-signed.zip</code>, seleccionamos y esperamos. Al terminar mostrará <code class="language-plaintext highlighter-rouge">Install complete. Enjoy!</code></p>
<p>Con esto debería estar rooteado el teléfono, si quieres comprobarlo podrias intentar correr <code class="language-plaintext highlighter-rouge">su</code> en Terminal Emulator y ver que sucede. Si el proceso se completó de forma exitosa debe mostrarse la notificación de Superuser indicando que la aplicacion Terminal Emulator solicita acceso de super usuario, al concederlo el caracter de la shell debería cambiar de <code class="language-plaintext highlighter-rouge">$</code> a <code class="language-plaintext highlighter-rouge">#</code>.</p>l4shEste es un post algo viejito. Lo cargo por acá en caso que alguien necesite documentación sobre como realizar el procedimiento.