Jekyll2021-12-23T10:38:53-06:00https://sargue.net//Sargue’s blogPersistencia de notas sobre Java, Linux e informática en general.Sergi BailaNovedades de Java 112018-11-26T00:00:00-06:002018-11-26T00:00:00-06:00https://sargue.net/2018/11/26/novedades-de-java-11<p>El pasado septiembre fue publicada la <a href="http://openjdk.java.net/projects/jdk/11/">versión 11 de Java</a>. Esta es la segunda versión del nuevo mecanismo de publicación de versiones más frecuente con una cadencia de unos seis meses. Es, además, una versión importante ya que es la primera versión considerada LTS desde Java 8.</p>
<p>La lista de novedades no es especialmente relevante. Esto es de esperar dada el nuevo ritmo de versiones. Lo importante de esta versión son las implicaciones de cara al soporte. Veamos primero las principales novedades.</p>
<h2 id="principales-novedades">Principales novedades</h2>
<h3 id="eliminación-de-paquetes-java-ee-y-corba">Eliminación de paquetes Java EE y Corba</h3>
<p>Se han eliminado algunos paquetes incluidos en el JDK como <code class="language-plaintext highlighter-rouge">java.xml.bind</code>, <code class="language-plaintext highlighter-rouge">java.corba</code> o <code class="language-plaintext highlighter-rouge">java.activation</code>. En caso de necesitar esos paquetes tan sólo hay que incluirlos como dependencia externa.</p>
<h3 id="nuevo-cliente-http">Nuevo cliente HTTP</h3>
<p>El JDK incorpora un nuevo cliente HTTP con una API moderna y soporte para HTTP/2. Permite programación reactiva y el envío de peticiones asíncronas.</p>
<h3 id="sintaxis-de-variables-locales-en-parámetros-lambda">Sintaxis de variables locales en parámetros lambda</h3>
<p>Se puede utilizar la palabra clave <code class="language-plaintext highlighter-rouge">var</code> en los parámetros lambda. La utilidad de esta novedad radica en poder aplicar anotaciones a un parámetro sin necesidad de declararlo completamente como era necesario hasta ahora.</p>
<h3 id="shebang">Shebang</h3>
<p>La expresión <code class="language-plaintext highlighter-rouge">shebang</code> hace referencia a la cadena <code class="language-plaintext highlighter-rouge">#!</code> que se incluye al principio de un script (bajo Linux) y permite su ejecución directa por línea de comandos. Ahora se puede tener scripts Java de este tipo.</p>
<h3 id="tls-13">TLS 1.3</h3>
<p>Soporte para la nueva versión de <em>Transport Layer Security</em>.</p>
<h2 id="soporte-de-java-sigue-siendo-java-gratuito">Soporte de Java, ¿sigue siendo Java gratuito?</h2>
<p>Y ahora el tema importante respecto a esta nueva versión de Java.</p>
<p>¿Sigue siendo Java <em>gratuito</em>?</p>
<p>La respuesta corta es <a href="https://adoptopenjdk.net/">sí</a>.</p>
<p>La respuesta algo más elaborada es que el nuevo modelo de versiones y la publicación de la versión 11 nos dejan un paisaje diferente en lo relativo a las versiones de Java proporcionadas por Oracle. Hasta la versión 8 prácticamente se usaban las distribuciones de Java de Oracle que incluian actualizaciones de seguridad durante bastante tiempo. Eso ha cambiado y a partir de Enero de 2019 se acaban las actualizaciones gratuitas de Java 8. Las nuevas versiones con el ciclo de publicación semestral sólo tendrán soporte hasta la siguiente versión. Además la versión publicada por Oracle de Java sólo será gratuita para uso no comercial.</p>
<p>Nada de eso importa mucho para la mayoría de usuarios. La distrución de Oracle es exactamente igual que la de OpenJDK, sin cambios. Así que tan sólo hay que usar las versiones de OpenJDK. El servicio <a href="https://adoptopenjdk.net/">AdoptOpenJDK</a> proporciona las diferentes versiones de Java de manera totalmente libre. <strong>Es la página de referencia a partir de <em>ya</em> para descargar Java</strong>.</p>
<p>Para una explicación más detallada recomiendo la lectura de <a href="https://blog.joda.org/2018/08/java-is-still-available-at-zero-cost.html">Java is still available at zero-cost</a>.</p>
<h2 id="referencias">Referencias</h2>
<ul>
<li><a href="http://openjdk.java.net/projects/jdk/11/">http://openjdk.java.net/projects/jdk/11/</a></li>
<li><a href="https://blog.codefx.org/java/http-2-api-tutorial/">Java 11 HTTP/2 API Tutorial</a></li>
<li><a href="https://blog.codefx.org/java/java-11-gems/">Eleven Hidden Gems In Java 11</a></li>
<li><a href="https://picodotdev.github.io/blog-bitix/2018/09/novedades-y-nuevas-caracteristicas-de-java-11/">Novedades y nuevas características de Java 11</a></li>
</ul>Sergi BailaEl pasado septiembre fue publicada la versión 11 de Java. Esta es la segunda versión del nuevo mecanismo de publicación de versiones más frecuente con una cadencia de unos seis meses. Es, además, una versión importante ya que es la primera versión considerada LTS desde Java 8.Comprobar si un certificado está presente en el keystore2018-08-03T00:00:00-05:002018-08-03T00:00:00-05:00https://sargue.net/2018/08/03/comprobar-certificado-en-keystore<p>La máquina virtual de Java tiene su propio almacen de certificados de confianza: el fichero <em>cacerts</em>. Este fichero es un tipo específico de <em>KeyStore</em> Java en el que se almacenan certificados públicos en los que se confia, en general certificados raiz de autoridades de certificación.</p>
<p>Últimamente he tenido que comprobar varias veces si en el <em>keystore</em> de una instalación concreta existe o no un certificado debido a los diferentes eventos de seguridad como <a href="https://www.thesslstore.com/blog/symantec-re-issue-thousands-of-ssl-certificates-will-be-distrusted-tuesday/">el de de Symantec</a>.</p>
<p>Para facilitar el trabajo he creado <a href="https://gist.github.com/sargue/2c1f8fca3482bcd0de0ea4182f44cf9d">una pequeña rutina en Java</a> para hacer más facil la comprobación.</p>Sergi BailaLa máquina virtual de Java tiene su propio almacen de certificados de confianza: el fichero cacerts. Este fichero es un tipo específico de KeyStore Java en el que se almacenan certificados públicos en los que se confia, en general certificados raiz de autoridades de certificación.Novedades de Java 102018-03-24T00:00:00-05:002018-03-24T00:00:00-05:00https://sargue.net/2018/03/24/novedades-de-java-10<p>Acaba de ser publicada la <a href="http://openjdk.java.net/projects/jdk/10/">versión 10 de Java</a>. Esta es la primera versión del nuevo mecanismo de publicación de versiones más frecuente y con una cadencia, más o menos, fija. Así que las novedades, para ser un cambio de versión mayor, no son muchas. No es tampoco una de las importantes versiones LTS con soporte extendido, eso le tocará a la futura Java 11.</p>
<p>Sin más, algunas de las novedades de Java 10.</p>
<h2 id="inferencia-de-tipos-para-variables-locales">Inferencia de tipos para variables locales</h2>
<p>La novedad que, sin duda, más me gusta de esta nueva versión. Va a conseguir una claridad del código fuente notable.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"> <span class="kt">var</span> <span class="n">list</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o"><</span><span class="nc">String</span><span class="o">>();</span> <span class="c1">// infers ArrayList<String></span>
<span class="kt">var</span> <span class="n">stream</span> <span class="o">=</span> <span class="n">list</span><span class="o">.</span><span class="na">stream</span><span class="o">();</span> <span class="c1">// infers Stream<String></span></code></pre></figure>
<p>Por supuesto sólo se podrá usar en variables locales junto con un inicializador, índices en bucles for, etc. pero no en todas partes. Tampoco es su objetivo.</p>
<h2 id="cambios-internos">Cambios internos</h2>
<p>Java 10 introduce también algunos cambios internos como la paralelización del colector de basura G1 o la compartición de datos a nivel aplicación (<em>Application Class-Data Sharing</em>).</p>
<h1 id="referencias">Referencias</h1>
<ul>
<li><a href="http://openjdk.java.net/projects/jdk/10/">http://openjdk.java.net/projects/jdk/10/</a></li>
<li><a href="https://blog.takipi.com/how-java-10-will-change-the-way-you-code/">https://blog.takipi.com/how-java-10-will-change-the-way-you-code/</a></li>
</ul>Sergi BailaAcaba de ser publicada la versión 10 de Java. Esta es la primera versión del nuevo mecanismo de publicación de versiones más frecuente y con una cadencia, más o menos, fija. Así que las novedades, para ser un cambio de versión mayor, no son muchas. No es tampoco una de las importantes versiones LTS con soporte extendido, eso le tocará a la futura Java 11.Novedades de Java 92018-03-23T00:00:00-05:002018-03-23T00:00:00-05:00https://sargue.net/2018/03/23/novedades-de-java-9<p>Con la <a href="http://openjdk.java.net/projects/jdk/10/">reciente publicación de Java 10</a> iba a publicar un resum de las novedades y me he dado cuenta que no lo había hecho para Java 9. Ojo, esto no es un resumen exhaustivo. Como todo en este blog escribo más para mí que de manera general. Así que es la lista de temas que me interesa o que me parece que voy a utilizar más, a modo de referencia.</p>
<p>Así que ahí vamos, de manera muy resumida.</p>
<h2 id="literales-para-definir-colecciones">Literales para definir colecciones</h2>
<p>Para definir listas, conjuntos y mapas de un tamaño predefinido. Métodos <code class="language-plaintext highlighter-rouge">Set.of()</code>, <code class="language-plaintext highlighter-rouge">List.of()</code> y <code class="language-plaintext highlighter-rouge">Map.of()</code>.</p>
<h2 id="streams">Streams</h2>
<p>Nuevos métodos en <em>streams</em>, como por ejemplo <code class="language-plaintext highlighter-rouge">dropWhile</code> o <code class="language-plaintext highlighter-rouge">takeWhile</code>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"> <span class="nc">IntStream</span><span class="o">.</span><span class="na">range</span><span class="o">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">10</span><span class="o">).</span><span class="na">dropWhile</span><span class="o">(</span><span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o"><</span> <span class="mi">5</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">::</span><span class="n">println</span><span class="o">)</span>
<span class="nc">IntStream</span><span class="o">.</span><span class="na">iterate</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o"><</span> <span class="mi">3</span><span class="o">,</span> <span class="n">x</span> <span class="o">-></span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">::</span><span class="n">println</span><span class="o">)</span></code></pre></figure>
<h2 id="optionals">Optionals</h2>
<p>También ampliaciones en los <em>Optional</em> como el método <code class="language-plaintext highlighter-rouge">or()</code> o la posibilidad de convertirlo fácilmente en un <em>stream</em> con <code class="language-plaintext highlighter-rouge">stream()</code>.</p>
<h2 id="métodos-privados-en-interfaces">Métodos privados en interfaces</h2>
<p>Ampliando la funcionalidad de añadir métodos en los interfaces introducida en Java 8 ahora podemos crear métodos privados que nos permiten organizar mejor el código de los métodos públicos de la interfaz.</p>
<h2 id="variables-efectivamente-finales-en-try-with-resources">Variables efectivamente finales en <em>try-with-resources</em></h2>
<p>Hasta ahora teníamos que definir una nueva variable para utilizar el mecanismo de <em>try-with-resources</em>. Ahora podemos usar cualquier variable <em>efectivamente final</em> lo que significa que sólo se le hace una asignación.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"> <span class="c1">// Hasta Java 8</span>
<span class="k">try</span> <span class="o">(</span> <span class="nc">FileReader</span> <span class="n">fr</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileReader</span><span class="o">(</span><span class="s">"datos.txt"</span><span class="o">)</span> <span class="o">)</span> <span class="o">{</span>
<span class="c1">// código que utiliza fr</span>
<span class="o">}</span>
<span class="c1">// Desde Java 9</span>
<span class="nc">FileReader</span> <span class="n">fr</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileReader</span><span class="o">(</span><span class="s">"datos.txt"</span><span class="o">);</span>
<span class="k">try</span> <span class="o">(</span> <span class="n">fr</span> <span class="o">)</span> <span class="o">{</span>
<span class="c1">// código que utiliza fr</span>
<span class="o">}</span></code></pre></figure>
<h2 id="sistema-modular-project-jigsaw">Sistema modular (Project Jigsaw)</h2>
<p>El cambio principal. Es tan gordo que ni me meto con ello. Una capa más de encapsulación sobre el <em>class loader</em>. Ahora hay <strong>módulos</strong> que buscan ser librerías o piezas de librerias. Los módulos exportan paquetes y los importan, o requieren, lo cual les permite utilizarlos. En teoria mejora la encapsulación y separación. En la práctica parece que <a href="http://blog.joda.org/2018/03/jpms-negative-benefits.html">es un lío tremendo</a>.</p>
<h2 id="jshell">jshell</h2>
<p>Se trata de un REPL (de <em>read eval print loop</em>) o como su nombre indica un entorno de linea de comandos para ejecutar pequeños fragmentos de código de una manera sencilla y directa.</p>
<h2 id="property-resource-bundles-en-utf-8"><em>Property resource bundles</em> en UTF-8</h2>
<p>La codificación hasta ahora era, obligatoriamente, ISO-8859-1. Pasa a UTF-8 lo que es una excelente notícia.</p>
<h1 id="referencias">Referencias</h1>
<ul>
<li><a href="https://zeroturnaround.com/rebellabs/the-best-java-9-language-and-api-improvements/">https://zeroturnaround.com/rebellabs/the-best-java-9-language-and-api-improvements/</a></li>
<li><a href="http://www.baeldung.com/new-java-9">http://www.baeldung.com/new-java-9</a></li>
<li><a href="http://openjdk.java.net/projects/jdk9/">http://openjdk.java.net/projects/jdk9/</a></li>
</ul>Sergi BailaCon la reciente publicación de Java 10 iba a publicar un resum de las novedades y me he dado cuenta que no lo había hecho para Java 9. Ojo, esto no es un resumen exhaustivo. Como todo en este blog escribo más para mí que de manera general. Así que es la lista de temas que me interesa o que me parece que voy a utilizar más, a modo de referencia.Estimación de proyectos2018-03-20T00:00:00-05:002018-03-20T00:00:00-05:00https://sargue.net/2018/03/20/estimacion-de-proyectos<p>Así me siento en general al final de una conversación telefónica de 10 minutos con un cliente
tras exponerme un nuevo proyecto o funcionalidad.</p>
<p><img src="/assets/img/2018/Strip-Supermarche-650-final650-final.jpg" alt="comic" /></p>
<p>El original <a href="http://www.commitstrip.com/en/2018/02/05/it-project-estimates/">aquí</a> de una
tira cómica periódica muy recomendable.</p>Sergi BailaAsí me siento en general al final de una conversación telefónica de 10 minutos con un cliente tras exponerme un nuevo proyecto o funcionalidad.Monit2018-02-26T00:00:00-06:002018-02-26T00:00:00-06:00https://sargue.net/2018/02/26/monit<p>Muy tarde he descubierto <a href="https://mmonit.com/monit/">Monit</a>. Para el que no la conozca se trata
de una pequeña utilidad de sistema sin mayores dependencias ni grandes pretensiones pero
que realiza tareas de monitorización básica de una manera excelente.</p>
<p>Desde que la descubrí el año pasado es una constante en todos mis servidores.</p>
<h2 id="instalación">Instalación</h2>
<p>En Debian viene en el paquete <code class="language-plaintext highlighter-rouge">monit</code>.</p>
<h2 id="configuración">Configuración</h2>
<p>Revisar si queda configurado el <em>fail2ban</em> para <em>monit</em> y desactivarlo si es así: borrar
el <em>soft link</em> a <code class="language-plaintext highlighter-rouge">/etc/monit/conf-enabled</code>.</p>
<p>Revisar <code class="language-plaintext highlighter-rouge">/etc/aliases</code> y si se ha añadido una entrada para monit cambiarla por una dirección
de correo completa. Ejemplo: monit: webmaster@gestio.ovh. En general esto no suele hacer falta
pero lo tengo en mi lista de tareas ya que suelo utilizar <a href="/2018/02/23/configuracion-de-msmtp-con-mailgun/"><code class="language-plaintext highlighter-rouge">msmtp</code></a> en algunos servidores.</p>
<h3 id="ficheros-de-configuración">Ficheros de configuración</h3>
<p>Configuración base: dos ficheros bajo <code class="language-plaintext highlighter-rouge">/etc/monit/conf.d</code></p>
<h4 id="correu">correu</h4>
<figure class="highlight"><pre><code class="language-config" data-lang="config"><span class="n">set</span> <span class="n">alert</span> <span class="n">webmaster</span>@<span class="n">sargue</span>.<span class="n">net</span>
<span class="n">set</span> <span class="n">mail</span>-<span class="n">format</span> {
<span class="n">from</span>: <span class="n">Monit</span> <<span class="n">server</span>@<span class="n">mg</span>.<span class="n">sargue</span>.<span class="n">net</span>>
<span class="n">subject</span>: <span class="n">monit</span> <span class="n">alert</span> -- $<span class="n">EVENT</span> $<span class="n">SERVICE</span>
<span class="n">message</span>: $<span class="n">EVENT</span> <span class="n">Service</span> $<span class="n">SERVICE</span>
<span class="n">Date</span>: $<span class="n">DATE</span>
<span class="n">Action</span>: $<span class="n">ACTION</span>
<span class="n">Host</span>: $<span class="n">HOST</span>
<span class="n">Description</span>: $<span class="n">DESCRIPTION</span>
<span class="n">Your</span> <span class="n">faithful</span> <span class="n">employee</span>,
<span class="n">Monit</span>
}
<span class="n">set</span> <span class="n">mailserver</span> <span class="n">smtp</span>.<span class="n">mailgun</span>.<span class="n">org</span> <span class="n">port</span> <span class="m">587</span>
<span class="n">username</span> <span class="n">postmaster</span>@<span class="n">mg</span>.<span class="n">sargue</span>.<span class="n">net</span> <span class="n">password</span> <span class="n">xxxx</span>
<span class="n">using</span> <span class="n">SSL</span> <span class="n">with</span> <span class="n">timeout</span> <span class="m">30</span> <span class="n">seconds</span></code></pre></figure>
<h4 id="sistema">sistema</h4>
<figure class="highlight"><pre><code class="language-config" data-lang="config"><span class="n">check</span> <span class="n">system</span> $<span class="n">HOST</span>
<span class="n">if</span> <span class="n">loadavg</span> (<span class="m">1</span><span class="n">min</span>) > <span class="m">4</span> <span class="n">then</span> <span class="n">alert</span>
<span class="n">if</span> <span class="n">loadavg</span> (<span class="m">5</span><span class="n">min</span>) > <span class="m">2</span> <span class="n">then</span> <span class="n">alert</span>
<span class="n">if</span> <span class="n">cpu</span> <span class="n">usage</span> > <span class="m">95</span>% <span class="n">for</span> <span class="m">10</span> <span class="n">cycles</span> <span class="n">then</span> <span class="n">alert</span>
<span class="n">if</span> <span class="n">memory</span> <span class="n">usage</span> > <span class="m">75</span>% <span class="n">then</span> <span class="n">alert</span>
<span class="n">if</span> <span class="n">swap</span> <span class="n">usage</span> > <span class="m">25</span>% <span class="n">then</span> <span class="n">alert</span>
<span class="n">check</span> <span class="n">filesystem</span> <span class="n">root</span> <span class="n">path</span> /
<span class="n">if</span> <span class="n">space</span> <span class="n">usage</span> > <span class="m">75</span>% <span class="n">for</span> <span class="m">5</span> <span class="n">times</span> <span class="n">within</span> <span class="m">15</span> <span class="n">cycles</span> <span class="n">then</span> <span class="n">alert</span>
<span class="n">if</span> <span class="n">inode</span> <span class="n">usage</span> > <span class="m">50</span>% <span class="n">then</span> <span class="n">alert</span></code></pre></figure>Sergi BailaMuy tarde he descubierto Monit. Para el que no la conozca se trata de una pequeña utilidad de sistema sin mayores dependencias ni grandes pretensiones pero que realiza tareas de monitorización básica de una manera excelente.Configuración de msmtp con Mailgun2018-02-23T00:00:00-06:002018-02-23T00:00:00-06:00https://sargue.net/2018/02/23/configuracion-de-msmtp-con-mailgun<p>En el pasado he utilizado diversos servicios <a href="/tags/smtp">SMTP</a> y últimamente, en servidores VPS con pocos recursos, estoy optando por instalar <a href="http://msmtp.sourceforge.net/">msmtp</a>. Se trata de un pequeño servicio SMTP que simula a <code class="language-plaintext highlighter-rouge">sendmail</code> y funciona razonablemente bien sin utilizar ningún servicio residente.</p>
<h2 id="instalación">Instalación</h2>
<p>Existe paquete Debian así que sólo hay que instalar <code class="language-plaintext highlighter-rouge">msmtp-mta</code>.</p>
<h2 id="configuración-con-mailgun">Configuración con Mailgun</h2>
<p>Configuración en dos ficheros que hay que crear:</p>
<p>/etc/msmtprc</p>
<figure class="highlight"><pre><code class="language-config" data-lang="config"> <span class="n">defaults</span>
<span class="n">auth</span> <span class="n">on</span>
<span class="n">tls</span> <span class="n">on</span>
<span class="n">tls_trust_file</span> /<span class="n">etc</span>/<span class="n">ssl</span>/<span class="n">certs</span>/<span class="n">ca</span>-<span class="n">certificates</span>.<span class="n">crt</span>
<span class="n">syslog</span> <span class="n">on</span>
<span class="n">aliases</span> /<span class="n">etc</span>/<span class="n">aliases</span>
<span class="n">account</span> <span class="n">mailgun</span>
<span class="n">host</span> <span class="n">smtp</span>.<span class="n">mailgun</span>.<span class="n">org</span>
<span class="n">from</span> <span class="n">webmaster</span>@<span class="n">mg</span>.<span class="n">sargue</span>.<span class="n">net</span>
<span class="n">user</span> <span class="n">xxx</span>@<span class="n">mg</span>.<span class="n">sargue</span>.<span class="n">net</span>
<span class="n">password</span> <<span class="n">pass</span>>
<span class="n">auto_from</span> <span class="n">on</span>
<span class="n">maildomain</span> <span class="n">sargue</span>.<span class="n">net</span>
<span class="n">account</span> <span class="n">default</span> : <span class="n">mailgun</span></code></pre></figure>
<p>/etc/aliases</p>
<figure class="highlight"><pre><code class="language-config" data-lang="config"> <span class="n">default</span>: <span class="n">webmaster</span>@<span class="n">sargue</span>.<span class="n">net</span>
<span class="n">root</span>: <span class="n">webmaster</span>@<span class="n">sargue</span>.<span class="n">net</span></code></pre></figure>
<p>También hay que modificar las entradas existentes porque no es recursivo… no funciona <code class="language-plaintext highlighter-rouge">monit: root</code> por ejemplo.</p>
<p>También revisar otros correos configurados:</p>
<ul>
<li>cron: /etc/crontab (MAILTO)</li>
<li>logcheck: /etc/logcheck/logcheck.conf (SENDMAILTO)</li>
<li>cron-apt: /etc/cron-apt/config (MAILTO)</li>
</ul>
<p>Configurar en mail.rc (creo que instalada por paquete mailx)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> alias root root<webmaster@sargue.net>
</code></pre></div></div>Sergi BailaEn el pasado he utilizado diversos servicios SMTP y últimamente, en servidores VPS con pocos recursos, estoy optando por instalar msmtp. Se trata de un pequeño servicio SMTP que simula a sendmail y funciona razonablemente bien sin utilizar ningún servicio residente.“Creo en la evidencia”2017-04-07T08:06:19-05:002017-04-07T08:06:19-05:00https://sargue.net/2017/04/07/creo-en-la-evidencia<blockquote>
<p>«¿No crees en platillos voladores?», me preguntan. «¿No crees en la telepatía, en astronautas antiguos, en el triángulo de las Bermudas, en la vida después de la muerte?.» Les respondo que no. No, no, no, no y no de nuevo. Una persona, desesperada por la letanía de negación sin cesar, me preguntó entonces si creía en algo. «Sí», le dije, «Creo en la evidencia. Creo en la observación, las mediciones, el razonamiento y la confirmación por medio de observadores independientes. Creeré en cualquier cosa, sin importar que tan loca o ridícula, si hay evidencia que la soporte. Mientras más loca y más ridícula, eso sí, más firme y sólida tendrá que ser la evidencia.</p>
</blockquote>
<p><a href="http://www.goodreads.com/quotes/31413-don-t-you-believe-in-flying-saucers-they-ask-me-don-t">Isaac Asimov</a></p>
<p>Visto en <a href="http://www.microsiervos.com/archivo/libros/25-anos-sin-isaac-asimov.html">Microsiervos</a></p>Sergi Baila«¿No crees en platillos voladores?», me preguntan. «¿No crees en la telepatía, en astronautas antiguos, en el triángulo de las Bermudas, en la vida después de la muerte?.» Les respondo que no. No, no, no, no y no de nuevo. Una persona, desesperada por la letanía de negación sin cesar, me preguntó entonces si creía en algo. «Sí», le dije, «Creo en la evidencia. Creo en la observación, las mediciones, el razonamiento y la confirmación por medio de observadores independientes. Creeré en cualquier cosa, sin importar que tan loca o ridícula, si hay evidencia que la soporte. Mientras más loca y más ridícula, eso sí, más firme y sólida tendrá que ser la evidencia.Copias de seguridad con duplicity2016-07-26T10:07:38-05:002016-07-26T10:07:38-05:00https://sargue.net/2016/07/26/copias-de-seguridad-con-duplicity<p>Duplicity es una herramienta de copias de seguridad que combina la eficiencia de <em>rsync</em> para la transmisión y almacenamiento de datos con la seguridad de <em>gpg</em> para el cifrado de los mismos. Además mantiene un histórico eficiente de copias y dispone de múltiples <em>backends</em> donde almacenar los datos.</p>
<h3 id="tipos-de-backup">Tipos de <em>backup</em></h3>
<p>Una de las principales características de <em>duplicity</em> es su eficiencia. Para mantener copias históricas utiliza los mecanismos habituales de la copia completa y la incremental. La incremental envia no sólo los ficheros modificados sino que va más allá y, utilizando el algoritmo de <em>rsync</em>, envia sólo los cambios efectuados. Así se envia y almacena en destino sólo el mínimo necesario. Por supuesto los datos van también comprimidos para, si cabe, reducir más el tamaño.</p>
<h3 id="cifrado">Cifrado</h3>
<p>El cifrado es opcional pero tremendamente sencillo de utilizar. Puede configurarse con claves públicas y privadas con <em>gpg</em> o un cifrado simétrico más sencillo de utilizar. Yo utilizo este por comodidad, utilizando un <em>passphrase</em> complejo para cada configuración de <em>duplicity</em>.</p>
<h3 id="almacenes-backends">Almacenes (<em>backends</em>)</h3>
<p>Una vez indicados el tipo de backup, qué ficheros queremos, la encriptación y algunos datos más nos queda indicar donde guardar la copia de seguridad.</p>
<p>En este apartado, de nuevo, <em>duplicity</em> brilla por su flexibilidad. Permite un mecanismo de extensiones sencillo que ha propiciado la existencia de múltiples <em>backends</em> donde podemos dejar los ficheros. Yo he utilizado fácilmente SSH (quizás el más habitual), FTP y Amazon S3. La lista se va ampliando y se puede consultar en cada versión de <em>duplicity</em>.</p>
<p>Un ejemplo de <em>backends</em> a fecha de mayo de 2016:</p>
<ul>
<li>Azure</li>
<li>Backblaze B2</li>
<li>Rackspace Cloud Files</li>
<li>Copy cloud storage</li>
<li>Dropbox</li>
<li>FISH</li>
<li>FTP</li>
<li>Google Docs</li>
<li>Google Cloud Storage</li>
<li>HSI</li>
<li>hubiC</li>
<li>IMAP (!)</li>
<li>mega</li>
<li>onedrive</li>
<li>rsync</li>
<li>Amazon S3</li>
<li>SCP/SFTP</li>
<li>Switf (Openstack)</li>
<li>Tahoe-LAFS</li>
<li>WebDAV</li>
<li>pydrive</li>
<li>MediaFire</li>
</ul>
<h2 id="copia-doble-de-un-servidor-con-duplicity">Copia doble de un servidor con <em>duplicity</em></h2>
<p>Bien, una vez introducido <code class="language-plaintext highlighter-rouge">duplicity</code> vamos al tema interesante. Como siempre este artículo es un resumen personal a modo de <em>brain unload</em> para mi futura referencia. Hay que cogerlo como tal. Si a alguien le es de utilidad, genial.</p>
<p>He de gestionar bastantes servidores para clientes. Nada grande, normalmente son pequeños servidors VPS o dedicados pero sin grandes pretensiones. Servidores web, de aplicaciones, bases de datos… Para cada servidor me gusta mantener dos copias de seguridad en otros sendos destinos, que pueden ser otros servidores o servicios de alojamiento como Amazon S3 o Backblaze B2.</p>
<h3 id="script-de-backup">Script de backup</h3>
<p>Suponiendo que hago el backup de un servidor llamado <em>boverals</em> a dos servidores llamados <em>bkp1</em> y <em>bkp2</em> lo que tendria son dos scripts en <code class="language-plaintext highlighter-rouge">/root</code> con los apropiados y poco imaginativos nombres de <code class="language-plaintext highlighter-rouge">duplicity-bkp1</code> y <code class="language-plaintext highlighter-rouge">duplicity-bkp2</code>. Son idénticos excepto por el servidor destino.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/bash</span>
<span class="nb">export </span><span class="nv">PASSPHRASE</span><span class="o">=</span>xxxxxxxxxxxxxxxxxxxxxx
flock <span class="nt">-n</span> /var/run/duplicity-bkp1 <span class="se">\</span>
chronic duplicity <span class="se">\</span>
<span class="nt">--full-if-older-than</span> 1M <span class="se">\</span>
<span class="nt">--volsize</span> 250 <span class="se">\</span>
<span class="nt">--include</span> /etc <span class="se">\</span>
<span class="nt">--include</span> /opt <span class="se">\</span>
<span class="nt">--include</span> /root <span class="se">\</span>
<span class="nt">--include</span> /home <span class="se">\</span>
<span class="nt">--include</span> /var <span class="se">\</span>
<span class="nt">--exclude</span> <span class="se">\*\*</span> <span class="se">\</span>
/ rsync://bkp@bkp1.sargue.net//home/bkp/boverals
<span class="k">if</span> <span class="o">[</span> <span class="sb">`</span><span class="nb">date</span> +%d<span class="sb">`</span> <span class="o">!=</span> <span class="s2">"01"</span> <span class="o">]</span>
<span class="k">then
</span>flock <span class="nt">-n</span> /var/run/duplicity-bkp1 <span class="se">\</span>
duplicity collection-status <span class="se">\</span>
rsync://bkp@bkp1.sargue.net//home/bkp/boverals /
flock <span class="nt">-n</span> /var/run/duplicity-bkp1 <span class="se">\</span>
duplicity verify <span class="se">\</span>
<span class="nt">--compare-data</span> <span class="se">\</span>
rsync://bkp@bkp1.sargue.net//home/bkp/boverals /
<span class="k">fi
</span>flock <span class="nt">-n</span> /var/run/duplicity-bkp1 <span class="se">\</span>
chronic duplicity <span class="se">\</span>
remove-all-but-n-full 3 <span class="se">\</span>
<span class="nt">--force</span> <span class="se">\</span>
rsync://bkp@bkp1.sargue.net//home/bkp/boverals
<span class="nb">unset </span>PASSPHRASE</code></pre></figure>
<p>Como se puede ver utilizo un par de herramientas auxiliares aparte de duplicity: <code class="language-plaintext highlighter-rouge">flock</code> y <code class="language-plaintext highlighter-rouge">chronic</code>.</p>
<p>Con <a href="http://linux.die.net/man/1/flock"><code class="language-plaintext highlighter-rouge">flock</code></a> controlo el caso que una ejecución supere las 24 horas y se encabalgue. Imagino que es casi imposible que pueda pasar pero <em>casi</em> no es suficiente.</p>
<p>Uso <a href="https://joeyh.name/code/moreutils/"><code class="language-plaintext highlighter-rouge">chronic</code></a> para filtrar la ejecución con <code class="language-plaintext highlighter-rouge">cron</code> y que me avise
sólo si ocurre algún error en la ejecución del comando. Se traga la salida de un script a no ser que el resultado de la ejecución sea erróneo. Entonces suelta toda la salida. Al ejectuarse en cron significa que enviará un email.</p>
<p>La variable de entorno <code class="language-plaintext highlighter-rouge">PASSPHRASE</code> contiene la contraseña para la encriptación simétrica de las copias de seguridad. Cuando <em>duplicity</em> encuentra dicha variable de entorno la utiliza automáticamente.</p>
<h3 id="copia-en-destino-via-rsync-y-ssh">Copia en destino via rsync y SSH</h3>
<p>El script de ejemplo anterior utiliza dos servidores, el protocolo rsync y el transporte por SSH para almacenar la copia de seguridad. Es uno de los casos más habituales que utilizo, simplemente por motivos de costes. Puedo cruzar copias entre servidores, a ser posible alojados en diferentes centros de datos.</p>
<p>Para poder automatizar las copias lo único necesario en este caso es que el usuario con el que se ejecuta la copia de seguridad local (habitualmente <em>root</em>) pueda conectar sin contraseña por SSH al destino (usuario y servidor). Nada más facil que utilizar validación por certificados.</p>
<h2 id="restauración">Restauración</h2>
<p>La restauración es tremendamente sencilla:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">duplicity restore rsync://bkp@bkp1.sargue.net//home/bkp/boverals directorio</code></pre></figure>
<p>El comando construye la última versión de la copia utilizando todas las incrementales que sea necesario y deja el resultado en el directorio especificado. Si la copia está encriptada pedirá la contraseña por consola.</p>
<h2 id="cold-backup">Cold backup</h2>
<p>Todo estos mecanismos estan muy bien pero tienen un punto debil comun. Improbable, pero no imposible. Al estar todas las copias automatizadas es posible que un atacante que entre en un servidor borre no sólo datos en el propio servidor sino todas las copias de seguridad. Tengamos las que tengamos. En el caso de duplicity es tan sencillo como ejecutar un purgado.</p>
<p>La única solución es disponer de alguna copia de seguridad a la que no pueda borrarse desde los mismos servidores desde donde se realice la copia. Hay múltiples alternativas como copias a destinos de sólo escritura o que tengan una retención fijada. También una copia manual a un deposito seguro.</p>Sergi BailaDuplicity es una herramienta de copias de seguridad que combina la eficiencia de rsync para la transmisión y almacenamiento de datos con la seguridad de gpg para el cifrado de los mismos. Además mantiene un histórico eficiente de copias y dispone de múltiples backends donde almacenar los datos.Yubikey y KeePass22016-06-01T14:49:31-05:002016-06-01T14:49:31-05:00https://sargue.net/2016/06/01/yubikey-y-keepass2<p>Me he comprado una <a href="https://www.yubico.com/products/yubikey-hardware/yubikey4/">Yubikey 4</a> para trastear con ella y ver que tal se comporta como segundo factor de autenticación. De momento <a href="https://www.yubico.com/why-yubico/for-individuals">no está soportada en muchos lugares</a> pero he podido configurarla tanto en Google como en GitHub. Una de las características interesante de la Yubikey es que dispone de dos slots programables para diferentes mecanismos y algoritmos de autenticación. El primero es conveniente dejarlo en el mecanismo por defecto pero con el segundo podemos utilizar cualquier otro de los soportados.</p>
<p>Ya que utilizo <a href="http://keepass.info/">KeePass2</a> para guardar mis contraseñas he pensado en añadir un segundo factor además de la contraseña maestra que ahora mismo protege mis contraseñas. La propia gente de Yubikey <a href="https://www.yubico.com/why-yubico/for-individuals/password-managers/keepass/">recomienda una configuración</a> que funciona <strong>francamente mal</strong>. De hecho a mi no me ha funcionado y si lo hiciera seria realmente incómoda.</p>
<p>La que funciona muy bien es la que utiliza HMAC-SHA1. Aquí un <a href="http://brush701.github.io/keechallenge/">plugin</a> <a href="http://www.kahusecurity.com/2014/securing-keepass-with-a-second-factor/">que se configura en un momento</a> y con resultados estupendos.</p>Sergi BailaMe he comprado una Yubikey 4 para trastear con ella y ver que tal se comporta como segundo factor de autenticación. De momento no está soportada en muchos lugares pero he podido configurarla tanto en Google como en GitHub. Una de las características interesante de la Yubikey es que dispone de dos slots programables para diferentes mecanismos y algoritmos de autenticación. El primero es conveniente dejarlo en el mecanismo por defecto pero con el segundo podemos utilizar cualquier otro de los soportados.Fechas en formato nativo recibiendo JSON en AngularJS2016-05-17T05:58:28-05:002016-05-17T05:58:28-05:00https://sargue.net/2016/05/17/fechas-en-formato-nativo-recibiendo-json-en-angularjs<p>Una de las arquitecturas habituales del desarrollo de aplicaciones con AngularJS se basa en la interacción con el servidor mediante un API REST junto con un intercambio de datos con JSON. Si utilizamos fechas en nuestros objectos (bastante probable) nos encontraremos con una decisión.</p>
<p>El formato de intercambio JSON no tiene soporte para fechas como tipo nativo. Así que si queremos transmitir fechas debemos escoger qué formato utilizar. Si no tenemos un requisito específico para un formato es conveniente utilizar el <a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>.</p>
<p>Mediante esta codificación podemos recibir las fechas en el objeto JSON como un <code class="language-plaintext highlighter-rouge">String</code>. A partir de ahí podemos convertirla en una fecha utilizando múltiples mecanismos. En mi caso suelo utilizar la libreria <a href="http://momentjs.com/">moment.js</a> para trabajar con fechas a nivel Javascript. Convertir una fecha codificada con ISO 8601 es tan fácil como utilizar el propio constructor de <code class="language-plaintext highlighter-rouge">moment()</code>.</p>
<p>En mi último proyecto he ido un paso más allá para integrar un conversor en todas las respuestas HTTP de manera que revisa todos los strings de la respuesta y, los que parecen una fecha y se pueden convertir, los sustituye por una fecha en formato nativo de Javascript.</p>
<p>Este es el código que prepara este mecanismo.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="dl">"</span><span class="s2">use strict</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">regex</span> <span class="o">=</span> <span class="sr">/^</span><span class="se">\d{4}</span><span class="sr">-</span><span class="se">\d{2}</span><span class="sr">-</span><span class="se">\d{2}(?:</span><span class="sr">T</span><span class="se">\d{2}</span><span class="sr">:</span><span class="se">\d{2}(?:</span><span class="sr">:</span><span class="se">\d{2}(?:\.\d{3,9})?)?)?</span><span class="sr">$/</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">dateStringsToDates</span><span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">input</span> <span class="o">!==</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// Ignore things that aren't objects.</span>
<span class="k">return</span> <span class="nx">input</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">input</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">input</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="p">{</span> <span class="c1">// Ignore inherited properties</span>
<span class="k">continue</span><span class="p">;</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">value</span> <span class="o">=</span> <span class="nx">input</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
<span class="kd">var</span> <span class="nx">match</span><span class="p">;</span>
<span class="c1">// Check for string properties which look like dates.</span>
<span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">string</span><span class="dl">"</span> <span class="o">&&</span> <span class="p">(</span><span class="nx">match</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="nx">regex</span><span class="p">)))</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">m</span> <span class="o">=</span> <span class="nx">moment</span><span class="p">(</span><span class="nx">match</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">m</span><span class="p">.</span><span class="nx">isValid</span><span class="p">())</span> <span class="p">{</span>
<span class="nx">input</span><span class="p">[</span><span class="nx">key</span><span class="p">]</span> <span class="o">=</span> <span class="nx">m</span><span class="p">.</span><span class="nx">toDate</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">object</span><span class="dl">"</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Recurse into object</span>
<span class="nx">dateStringsToDates</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">input</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">angular</span>
<span class="p">.</span><span class="nx">module</span><span class="p">(</span><span class="dl">"</span><span class="s2">myApp</span><span class="dl">"</span><span class="p">,</span> <span class="p">[])</span>
<span class="p">.</span><span class="nx">factory</span><span class="p">(</span><span class="dl">'</span><span class="s1">DateStringsToDates</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">process</span><span class="p">:</span> <span class="nx">dateStringsToDates</span>
<span class="p">};</span>
<span class="p">})</span>
<span class="p">.</span><span class="nx">config</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">$httpProvider</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">$httpProvider</span><span class="p">.</span><span class="nx">defaults</span><span class="p">.</span><span class="nx">transformResponse</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">responseData</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">dateStringsToDates</span><span class="p">(</span><span class="nx">responseData</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}());</span></code></pre></figure>Sergi BailaUna de las arquitecturas habituales del desarrollo de aplicaciones con AngularJS se basa en la interacción con el servidor mediante un API REST junto con un intercambio de datos con JSON. Si utilizamos fechas en nuestros objectos (bastante probable) nos encontraremos con una decisión.Usando guice con java websockets (JSR-356)2016-05-10T06:26:28-05:002016-05-10T06:26:28-05:00https://sargue.net/2016/05/10/usando-guice-con-java-websockets-jsr-356<p>La especificación de websockets de Java (JSR-356) soporta, como es evidente, el uso de inyección de dependencias cuando se ejecuta en un entorno JavaEE mediante CDI.</p>
<p>El servidor <a href="http://tomcat.apache.org/">Tomcat</a> a partir de la versión 7 implementa soporte para esta tecnologia. Pero dado que no es un servidor JavaEE completo no incorpora soporte para CDI.</p>
<p>Si queremos usar una alternativa como <a href="https://github.com/google/guice">Guice</a> para inyección de dependencias tan sólo tenemos que ajustar el mecanismo de creación de las instancias de los <em>endpoints</em>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">WSConfigurator</span> <span class="kd">extends</span> <span class="nc">ServerEndpointConfig</span><span class="o">.</span><span class="na">Configurator</span> <span class="o">{</span>
<span class="nd">@Inject</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="nc">Injector</span> <span class="n">injector</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="no">T</span> <span class="nf">getEndpointInstance</span><span class="o">(</span><span class="nc">Class</span><span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="n">endpointClass</span><span class="o">)</span>
<span class="kd">throws</span> <span class="nc">InstantiationException</span>
<span class="o">{</span>
<span class="k">return</span> <span class="n">injector</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(</span><span class="n">endpointClass</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>Mediante este <code class="language-plaintext highlighter-rouge">WSConfigurator</code> todas las instancias de nuestros puntos de entrada de websockets se crearan inyectando las dependencias utilizando Guice.</p>
<p>Pero hay que acordarse de utilizarlo en cada definición de un servicio websocket. Un ejemplo de una clase que he utilizado en un proyecto reciente:</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@ServerEndpoint</span><span class="o">(</span>
<span class="n">value</span> <span class="o">=</span> <span class="s">"/ws/sync"</span><span class="o">,</span>
<span class="n">encoders</span> <span class="o">=</span> <span class="nc">JsonEncoder</span><span class="o">.</span><span class="na">class</span><span class="o">,</span>
<span class="n">decoders</span> <span class="o">=</span> <span class="nc">JsonDecoder</span><span class="o">.</span><span class="na">class</span><span class="o">,</span>
<span class="n">configurator</span> <span class="o">=</span> <span class="nc">WSConfigurator</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">WSSync</span> <span class="kd">extends</span> <span class="nc">AsyncWebSocketServer</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">WSSync</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="kd">private</span> <span class="nc">EventBus</span> <span class="n">eventBus</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">Sincronitzador</span> <span class="n">sincronitzador</span><span class="o">;</span>
<span class="nd">@Inject</span>
<span class="kd">public</span> <span class="nf">WSSync</span><span class="o">(</span><span class="nc">EventBus</span> <span class="n">eventBus</span><span class="o">,</span> <span class="nc">Sincronitzador</span> <span class="n">sincronitzador</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">eventBus</span> <span class="o">=</span> <span class="n">eventBus</span><span class="o">;</span>
<span class="k">this</span><span class="o">.</span><span class="na">sincronitzador</span> <span class="o">=</span> <span class="n">sincronitzador</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">...</span></code></pre></figure>
<p>Por último tenemos que cerrar el círculo inyectando el inyector en la clase <code class="language-plaintext highlighter-rouge">WSConfigurator</code>. Esta es la parte <em>sucia</em> del invento pero es un patrón que suele aparecer cuando hay que añadir soporte de Guice en una libreria de terceros.</p>
<p>En mi caso utilizo un módulo específico para los websockets.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">com.google.inject.AbstractModule</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">WebSocketModule</span> <span class="kd">extends</span> <span class="nc">AbstractModule</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">configure</span><span class="o">()</span> <span class="o">{</span>
<span class="n">requestStaticInjection</span><span class="o">(</span><span class="nc">WSConfigurator</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">requestStaticInjection</span><span class="o">(</span><span class="nc">JsonEncoder</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>Sergi BailaLa especificación de websockets de Java (JSR-356) soporta, como es evidente, el uso de inyección de dependencias cuando se ejecuta en un entorno JavaEE mediante CDI.Usando Gson con JAX-RS2016-05-02T05:49:26-05:002016-05-02T05:49:26-05:00https://sargue.net/2016/05/02/usando-gson-como-conversor-en-jersey<p>La codificación más habitual de los servicios web RESTful es JSON. La implementación de referencia del estandar JAX-RS, <a href="https://jersey.java.net">Jersey</a>, viene con facilidades para utilizar diversas librerias JSON como conversores y codificadores de las entradas y salidas del API REST.</p>
<p>Sin embargo yo quería usar una que no está incluida entra las habituales de Jersey. Por suerte JAX-RS proporciona unos mecanismos de extensión sencillos.</p>
<p>Esta es una clase de ejemplo, un proveedor, para utilizar <a href="https://github.com/google/gson">Gson</a> en Jersey.</p>
<p>Como se puede ver en el método <code class="language-plaintext highlighter-rouge">getGson</code> es donde configuramos la instancia de Gson así que ahí podriamos incluir los diferentes adaptadores especiales de objetos, como he hecho yo con un par de clases de la nueva libreria <code class="language-plaintext highlighter-rouge">java.time</code> de Java 8.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="cm">/**
* Proveïdor Jersey per fer servir Gson com a parser JSON.
*/</span>
<span class="nd">@Provider</span>
<span class="nd">@Produces</span><span class="o">(</span><span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span>
<span class="nd">@Consumes</span><span class="o">(</span><span class="nc">MediaType</span><span class="o">.</span><span class="na">APPLICATION_JSON</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">JerseyServerGson</span>
<span class="kd">implements</span> <span class="nc">MessageBodyWriter</span><span class="o"><</span><span class="nc">Object</span><span class="o">>,</span> <span class="nc">MessageBodyReader</span><span class="o"><</span><span class="nc">Object</span><span class="o">></span>
<span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isReadable</span><span class="o">(</span><span class="nc">Class</span><span class="o"><?></span> <span class="n">type</span><span class="o">,</span>
<span class="nc">Type</span> <span class="n">genericType</span><span class="o">,</span>
<span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span>
<span class="nc">MediaType</span> <span class="n">mediaType</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">Object</span> <span class="nf">readFrom</span><span class="o">(</span><span class="nc">Class</span><span class="o"><</span><span class="nc">Object</span><span class="o">></span> <span class="n">type</span><span class="o">,</span>
<span class="nc">Type</span> <span class="n">genericType</span><span class="o">,</span>
<span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span>
<span class="nc">MediaType</span> <span class="n">mediaType</span><span class="o">,</span>
<span class="nc">MultivaluedMap</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">String</span><span class="o">></span> <span class="n">httpHeaders</span><span class="o">,</span>
<span class="nc">InputStream</span> <span class="n">entityStream</span><span class="o">)</span>
<span class="kd">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">WebApplicationException</span>
<span class="o">{</span>
<span class="k">try</span> <span class="o">(</span> <span class="nc">InputStreamReader</span> <span class="n">input</span> <span class="o">=</span>
<span class="k">new</span> <span class="nf">InputStreamReader</span><span class="o">(</span><span class="n">entityStream</span><span class="o">,</span> <span class="s">"UTF-8"</span><span class="o">)</span> <span class="o">)</span> <span class="o">{</span>
<span class="nc">Gson</span> <span class="n">gson</span> <span class="o">=</span> <span class="n">getGson</span><span class="o">();</span>
<span class="k">return</span> <span class="n">gson</span><span class="o">.</span><span class="na">fromJson</span><span class="o">(</span><span class="n">input</span><span class="o">,</span> <span class="n">genericType</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="nd">@NotNull</span>
<span class="kd">private</span> <span class="nc">Gson</span> <span class="nf">getGson</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nf">GsonBuilder</span><span class="o">()</span>
<span class="o">.</span><span class="na">registerTypeAdapter</span><span class="o">(</span><span class="nc">LocalDateTime</span><span class="o">.</span><span class="na">class</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">AdapterLocalDateTime</span><span class="o">().</span><span class="na">nullSafe</span><span class="o">())</span>
<span class="o">.</span><span class="na">registerTypeAdapter</span><span class="o">(</span><span class="nc">LocalDate</span><span class="o">.</span><span class="na">class</span><span class="o">,</span>
<span class="k">new</span> <span class="nf">AdapterLocalDate</span><span class="o">().</span><span class="na">nullSafe</span><span class="o">())</span>
<span class="o">.</span><span class="na">setPrettyPrinting</span><span class="o">()</span>
<span class="o">.</span><span class="na">serializeNulls</span><span class="o">()</span>
<span class="o">.</span><span class="na">create</span><span class="o">();</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isWriteable</span><span class="o">(</span><span class="nc">Class</span><span class="o"><?></span> <span class="n">type</span><span class="o">,</span>
<span class="nc">Type</span> <span class="n">genericType</span><span class="o">,</span>
<span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span>
<span class="nc">MediaType</span> <span class="n">mediaType</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">long</span> <span class="nf">getSize</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">,</span>
<span class="nc">Class</span><span class="o"><?></span> <span class="n">type</span><span class="o">,</span>
<span class="nc">Type</span> <span class="n">genericType</span><span class="o">,</span>
<span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span>
<span class="nc">MediaType</span> <span class="n">mediaType</span><span class="o">)</span>
<span class="o">{</span>
<span class="c1">// Deprecated and ignored in Jersey 2</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">writeTo</span><span class="o">(</span><span class="nc">Object</span> <span class="n">o</span><span class="o">,</span>
<span class="nc">Class</span><span class="o"><?></span> <span class="n">type</span><span class="o">,</span>
<span class="nc">Type</span> <span class="n">genericType</span><span class="o">,</span>
<span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">,</span>
<span class="nc">MediaType</span> <span class="n">mediaType</span><span class="o">,</span>
<span class="nc">MultivaluedMap</span><span class="o"><</span><span class="nc">String</span><span class="o">,</span> <span class="nc">Object</span><span class="o">></span> <span class="n">httpHeaders</span><span class="o">,</span>
<span class="nc">OutputStream</span> <span class="n">entityStream</span><span class="o">)</span>
<span class="kd">throws</span> <span class="nc">IOException</span><span class="o">,</span> <span class="nc">WebApplicationException</span>
<span class="o">{</span>
<span class="k">try</span> <span class="o">(</span> <span class="nc">OutputStreamWriter</span> <span class="n">writer</span> <span class="o">=</span>
<span class="k">new</span> <span class="nf">OutputStreamWriter</span><span class="o">(</span><span class="n">entityStream</span><span class="o">,</span> <span class="s">"UTF-8"</span><span class="o">)</span> <span class="o">)</span> <span class="o">{</span>
<span class="n">getGson</span><span class="o">().</span><span class="na">toJson</span><span class="o">(</span><span class="n">o</span><span class="o">,</span> <span class="n">genericType</span><span class="o">,</span> <span class="n">writer</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>Sergi BailaLa codificación más habitual de los servicios web RESTful es JSON. La implementación de referencia del estandar JAX-RS, Jersey, viene con facilidades para utilizar diversas librerias JSON como conversores y codificadores de las entradas y salidas del API REST.Usando Guice con el cliente REST de Jersey2016-04-25T07:31:29-05:002016-04-25T07:31:29-05:00https://sargue.net/2016/04/25/usando-guice-con-el-cliente-rest-de-jersey<p>En un artículo anterior <a href="https://sargue.net/2016/04/11/compatibilidad-de-jersey-y-guice/">expliqué como integrar Guice con JAX-RS / Jersey como servidor</a>. En esta entrada mostraré como utilizar Guice con el cliente REST de Jersey.</p>
<p>La idea de base es la misma: utilizar el <a href="https://hk2.java.net/guice-bridge/">guice-bridge</a> proporcionado por HK2. El mecanismo exacto difiere ligeramente.</p>
<p>Primero definimos una clase que realiza el enlace entre las dos librerias de inyección de dependencia.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kn">import</span> <span class="nn">com.google.inject.Injector</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.glassfish.hk2.api.ServiceLocator</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.jvnet.hk2.guice.bridge.api.GuiceBridge</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.Logger</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.slf4j.LoggerFactory</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.inject.Inject</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.ws.rs.core.Feature</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">javax.ws.rs.core.FeatureContext</span><span class="o">;</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">JerseyClientGuiceInjector</span> <span class="kd">implements</span> <span class="nc">Feature</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span>
<span class="nc">JerseyClientGuiceInjector</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nd">@com</span><span class="o">.</span><span class="na">google</span><span class="o">.</span><span class="na">inject</span><span class="o">.</span><span class="na">Inject</span> <span class="kd">private</span> <span class="kd">static</span> <span class="nc">Injector</span> <span class="n">injector</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">ServiceLocator</span> <span class="n">serviceLocator</span><span class="o">;</span>
<span class="nd">@Inject</span>
<span class="kd">public</span> <span class="nf">JerseyClientGuiceInjector</span><span class="o">(</span><span class="nc">ServiceLocator</span> <span class="n">serviceLocator</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">serviceLocator</span> <span class="o">=</span> <span class="n">serviceLocator</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">configure</span><span class="o">(</span><span class="nc">FeatureContext</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Configurant Guice en client Jersey."</span><span class="o">);</span>
<span class="nc">GuiceBridge</span><span class="o">.</span><span class="na">getGuiceBridge</span><span class="o">().</span><span class="na">initializeGuiceBridge</span><span class="o">(</span><span class="n">serviceLocator</span><span class="o">);</span>
<span class="nc">GuiceIntoHK2Bridge</span> <span class="n">guiceBridge</span> <span class="o">=</span>
<span class="n">serviceLocator</span><span class="o">.</span><span class="na">getService</span><span class="o">(</span><span class="nc">GuiceIntoHK2Bridge</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="k">if</span> <span class="o">(</span><span class="n">injector</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"Guice Injector not found"</span><span class="o">);</span>
<span class="n">guiceBridge</span><span class="o">.</span><span class="na">bridgeGuiceInjector</span><span class="o">(</span><span class="n">injector</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">trace</span><span class="o">(</span><span class="s">"Configurat Guice en client Jersey."</span><span class="o">);</span>
<span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>La clase es sencilla pero tiene su dificultad comprender como funciona. Si nos fijamos en el constructor vemos que está anotado con un <code class="language-plaintext highlighter-rouge">@Inject</code> y su único parámetro es un <code class="language-plaintext highlighter-rouge">ServiceLocator</code>. El <code class="language-plaintext highlighter-rouge">ServiceLocator</code> es el equivalente al <code class="language-plaintext highlighter-rouge">Injector</code> de Guice. Esta clase la creará el cliente de Jersey y por tanto las dependencias las inyectará HK2 y no Guice.</p>
<p>Por otro lado tenemos el inyector de Guice definido como un atributo <strong>estático</strong> de la clase. Eso es así para poder romper el círculo ya que no podemos crear una clase que reciba ambos inyectores (el de Guice y el de HK2). El de Guice lo inyectamos de manera estática. Yo lo hago en el <code class="language-plaintext highlighter-rouge">AppModule</code>, mi módulo Guice principal de la aplicación.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="n">requestStaticInjection</span><span class="o">(</span><span class="nc">JerseyClientGuiceInjector</span><span class="o">.</span><span class="na">class</span><span class="o">);</span></code></pre></figure>
<p>Una vez tenemos ambos inyectores ya podemos crear el puente entre ambos como se puede ver en el método <code class="language-plaintext highlighter-rouge">configure</code>.</p>
<p>Una vez lista la clase <code class="language-plaintext highlighter-rouge">JerseyClientGuiceInjector</code> ya podemos utilizarla en nuestros clientes REST.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nc">ClientBuilder</span><span class="o">.</span><span class="na">newClient</span><span class="o">()</span>
<span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="nc">JerseyClientGuiceInjector</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="nc">JerseyClientGson</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">register</span><span class="o">(</span><span class="nc">ApiRequestFilter</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">target</span><span class="o">(</span><span class="n">serveiConfig</span><span class="o">.</span><span class="na">string</span><span class="o">(</span><span class="s">"servidor.api.url"</span><span class="o">))</span>
<span class="o">.</span><span class="na">path</span><span class="o">(</span><span class="s">"articles"</span><span class="o">)</span>
<span class="o">.</span><span class="na">request</span><span class="o">()</span>
<span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">entitat</span><span class="o">,</span> <span class="nc">ApiResult</span><span class="o">.</span><span class="na">class</span><span class="o">);</span></code></pre></figure>Sergi BailaEn un artículo anterior expliqué como integrar Guice con JAX-RS / Jersey como servidor. En esta entrada mostraré como utilizar Guice con el cliente REST de Jersey.Integración en JAX-RS de la nueva libreria de tiempo de Java 82016-04-18T06:09:56-05:002016-04-18T06:09:56-05:00https://sargue.net/2016/04/18/integracion-en-jersey-de-la-nueva-libreria-de-tiempo-de-java-8<p>Con la introducción de las nuevas clases de <code class="language-plaintext highlighter-rouge">java.time</code> en Java 8 podemos encontrarnos con la conveniencia de querer utilizarlas como parámetros en algunos métodos REST de nuestra API JAX-RS.</p>
<p>A continuación muestro un ejemplo real de una aplicación en la que utilizo dos tipos de <code class="language-plaintext highlighter-rouge">java.time</code> como parámetros de puntos REST. Concretamente las clases <code class="language-plaintext highlighter-rouge">LocalDate</code> y <code class="language-plaintext highlighter-rouge">MonthDay</code>.</p>
<p>Para añadir el soporte tan sólo tenemos que registrar un proveedor JAX-RS que implemente la interfaz <code class="language-plaintext highlighter-rouge">ParamConverterProvider</code>.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@Provider</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ParamConverters</span> <span class="kd">implements</span> <span class="nc">ParamConverterProvider</span> <span class="o">{</span>
<span class="nd">@SuppressWarnings</span><span class="o">(</span><span class="s">"unchecked"</span><span class="o">)</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="nc">ParamConverter</span><span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="nf">getConverter</span><span class="o">(</span><span class="nc">Class</span><span class="o"><</span><span class="no">T</span><span class="o">></span> <span class="n">rawType</span><span class="o">,</span>
<span class="nc">Type</span> <span class="n">genericType</span><span class="o">,</span>
<span class="nc">Annotation</span><span class="o">[]</span> <span class="n">annotations</span><span class="o">)</span>
<span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">rawType</span> <span class="o">==</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="k">return</span> <span class="o">(</span><span class="nc">ParamConverter</span><span class="o"><</span><span class="no">T</span><span class="o">>)</span> <span class="k">new</span> <span class="nc">ParamConverter</span><span class="o"><</span><span class="nc">LocalDate</span><span class="o">>()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">LocalDate</span> <span class="nf">fromString</span><span class="o">(</span><span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">TimeUtils</span><span class="o">.</span><span class="na">localDateFromJSON</span><span class="o">(</span><span class="n">value</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">toString</span><span class="o">(</span><span class="nc">LocalDate</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">};</span>
<span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">rawType</span> <span class="o">==</span> <span class="nc">MonthDay</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="k">return</span> <span class="o">(</span><span class="nc">ParamConverter</span><span class="o"><</span><span class="no">T</span><span class="o">>)</span> <span class="k">new</span> <span class="nc">ParamConverter</span><span class="o"><</span><span class="nc">MonthDay</span><span class="o">>()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">MonthDay</span> <span class="nf">fromString</span><span class="o">(</span><span class="nc">String</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">TimeUtils</span><span class="o">.</span><span class="na">parseMonthDay</span><span class="o">(</span><span class="n">value</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">toString</span><span class="o">(</span><span class="nc">MonthDay</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">};</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>La clase <code class="language-plaintext highlighter-rouge">TimeUtils</code> es una clase propia que utilizo para encapsular algunos métodos extra relaciones con <code class="language-plaintext highlighter-rouge">java.time</code>. Las partes interesantes son estas:</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">class</span> <span class="nc">TimeUtils</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">TimeUtils</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="nc">LocalDate</span> <span class="nf">localDateFromJSON</span><span class="o">(</span><span class="nc">String</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
<span class="k">if</span> <span class="o">(</span><span class="n">data</span><span class="o">.</span><span class="na">endsWith</span><span class="o">(</span><span class="s">"Z"</span><span class="o">))</span> <span class="o">{</span>
<span class="c1">// les dates serialitzades via JSON ho fan amb aquest format</span>
<span class="c1">// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString</span>
<span class="k">return</span> <span class="nc">Instant</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">data</span><span class="o">)</span>
<span class="o">.</span><span class="na">atZone</span><span class="o">(</span><span class="nc">ZoneId</span><span class="o">.</span><span class="na">systemDefault</span><span class="o">())</span>
<span class="o">.</span><span class="na">toLocalDate</span><span class="o">();</span>
<span class="o">}</span> <span class="k">else</span>
<span class="k">return</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">data</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="nc">MonthDay</span> <span class="nf">parseMonthDay</span><span class="o">(</span><span class="nc">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="kt">int</span><span class="o">[]</span> <span class="n">ddmm</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">stream</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="s">"/"</span><span class="o">))</span>
<span class="o">.</span><span class="na">mapToInt</span><span class="o">(</span><span class="nl">Integer:</span><span class="o">:</span><span class="n">parseInt</span><span class="o">)</span>
<span class="o">.</span><span class="na">toArray</span><span class="o">();</span>
<span class="k">return</span> <span class="nc">MonthDay</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">ddmm</span><span class="o">[</span><span class="mi">1</span><span class="o">],</span> <span class="n">ddmm</span><span class="o">[</span><span class="mi">0</span><span class="o">]);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"No he pogut interpretar {} com MonthDay."</span><span class="o">,</span> <span class="n">s</span><span class="o">);</span>
<span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>De la anterior clase la parte más <em>notable</em> es un pequeño hackeo para soportar correctamente las fechas en Javascript tal como se indica en el comentario. Dichas fechas se codifican siempre con el mismo formato relativo a UTC independientemente de la zona horaria local. Así que si queremos convertirlo a un <code class="language-plaintext highlighter-rouge">LocalDate</code> debemos suponer alguna zona horaria. En el caso de esta aplicación no habia soporte para zonas horarias y todo funcionaba en la misma, clientes y servidor, así que de ahí el uso de la zona de sistema.</p>Sergi BailaCon la introducción de las nuevas clases de java.time en Java 8 podemos encontrarnos con la conveniencia de querer utilizarlas como parámetros en algunos métodos REST de nuestra API JAX-RS.Inicialización de Guice en una aplicación web2016-04-11T09:35:00-05:002016-04-11T09:35:00-05:00https://sargue.net/2016/04/11/inicializacion-de-guice-en-una-aplicacion-web<p>Si queremos utilizar <a href="https://github.com/google/guice">Guice</a> en una aplicación web tenemos varias maneras de inicializar el entorno. El objetivo final es más o menos el mismo: configurar <a href="https://github.com/google/guice/wiki/Servlets">el filtro de Guice</a> necesario para la integración del elemento esencial, el <code class="language-plaintext highlighter-rouge">ServletModule</code>.</p>
<p>La <a href="https://github.com/google/guice/wiki/ServletModule">propia documentación</a> del <code class="language-plaintext highlighter-rouge">ServletModule</code> nos proporciona algunas alternativas y más o menos proporciona un camino recomendado con el uso del <code class="language-plaintext highlighter-rouge">GuiceServletContextListener</code>.</p>
<p>El problema que he encontrado yo con ese <em>listener</em> es la manera de adaptarlo fácilmente al uso de un segundo <em>listener</em>. Porque necesariamente vamos a tener código de inicialización de nuestra aplicación. Una manera sería usar los dos, otra usar sólo el de Guice. Yo he preferido controlar exactamente cuando inicializo Guice así que sólo hay un <em>listener</em> registrado en el sistema, el mio, y de ahí llamo al de Guice.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="cm">/**
* Bootstrap general de l'aplicació.
*/</span>
<span class="nd">@WebListener</span>
<span class="nd">@Singleton</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">AppContextListener</span> <span class="kd">implements</span> <span class="nc">ServletContextListener</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span>
<span class="nc">AppContextListener</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nd">@Inject</span> <span class="kd">private</span> <span class="nc">Provider</span><span class="o"><</span><span class="nc">DataSource</span><span class="o">></span> <span class="n">dataSourceProvider</span><span class="o">;</span>
<span class="nd">@Inject</span> <span class="kd">private</span> <span class="nc">ServeiConfig</span> <span class="n">serveiConfig</span><span class="o">;</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">contextInitialized</span><span class="o">(</span><span class="nc">ServletContextEvent</span> <span class="n">sce</span><span class="o">)</span> <span class="o">{</span>
<span class="k">try</span> <span class="o">{</span>
<span class="nc">ServletContext</span> <span class="n">context</span> <span class="o">=</span> <span class="n">sce</span><span class="o">.</span><span class="na">getServletContext</span><span class="o">();</span>
<span class="n">configureLogback</span><span class="o">(</span><span class="n">context</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Inicialitzant aplicació."</span><span class="o">);</span>
<span class="n">configuraGuice</span><span class="o">(</span><span class="n">sce</span><span class="o">);</span>
<span class="n">configuraBaseDeDades</span><span class="o">();</span>
<span class="n">context</span><span class="o">.</span><span class="na">setAttribute</span><span class="o">(</span><span class="s">"serveiConfig"</span><span class="o">,</span> <span class="n">serveiConfig</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"APLICACIÓ INICIADA."</span><span class="o">);</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">RuntimeException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"Error inicialitzant aplicació"</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
<span class="k">throw</span> <span class="n">e</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
<span class="nd">@Override</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">contextDestroyed</span><span class="o">(</span><span class="nc">ServletContextEvent</span> <span class="n">sce</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Aturant l'aplicació."</span><span class="o">);</span>
<span class="k">new</span> <span class="nf">GuiceBootstrap</span><span class="o">().</span><span class="na">contextDestroyed</span><span class="o">(</span><span class="n">sce</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"Aturada aplicació."</span><span class="o">);</span>
<span class="o">((</span><span class="nc">LoggerContext</span><span class="o">)</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getILoggerFactory</span><span class="o">()).</span><span class="na">stop</span><span class="o">();</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">configureLogback</span><span class="o">(</span><span class="nc">ServletContext</span> <span class="n">ctx</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// ...</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">configuraGuice</span><span class="o">(</span><span class="nc">ServletContextEvent</span> <span class="n">sce</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// primer configuro el propi contextlistener de Guice que defineixs els mòduls</span>
<span class="k">new</span> <span class="nf">GuiceBootstrap</span><span class="o">().</span><span class="na">contextInitialized</span><span class="o">(</span><span class="n">sce</span><span class="o">);</span>
<span class="c1">// com a cas especial carrego l'injector i m'auto-injecto</span>
<span class="nc">ServletContext</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">sce</span><span class="o">.</span><span class="na">getServletContext</span><span class="o">();</span>
<span class="nc">Injector</span> <span class="n">injector</span> <span class="o">=</span>
<span class="o">(</span><span class="nc">Injector</span><span class="o">)</span> <span class="n">ctx</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="nc">Injector</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">injector</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"Guice Injector not found"</span><span class="o">);</span>
<span class="n">injector</span><span class="o">.</span><span class="na">injectMembers</span><span class="o">(</span><span class="k">this</span><span class="o">);</span>
<span class="o">}</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">configuraBaseDeDades</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">// ...</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>
<p>Y la clase <code class="language-plaintext highlighter-rouge">GuiceBootstrap</code> es la encargada de inicializar Guice con todos los módulos que utilicemos en nuestra aplicación.</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="cm">/**
* Inicialitza Guice i Guice-Servlets.
* <p>
* Tot i ser un {@code ServletContextListener} no el defineixo com a tal
* ja que l'ordre és important i necessito que s'executi abans que el
* listener de l'aplicació.
*/</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">GuiceBootstrap</span> <span class="kd">extends</span> <span class="nc">GuiceServletContextListener</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="nc">Injector</span> <span class="nf">getInjector</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">Guice</span><span class="o">.</span><span class="na">createInjector</span><span class="o">(</span>
<span class="k">new</span> <span class="nf">AppModule</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">WebSocketModule</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">DatabaseModule</span><span class="o">(),</span>
<span class="k">new</span> <span class="nf">ServletModule</span><span class="o">()</span> <span class="o">{</span>
<span class="nd">@Override</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">configureServlets</span><span class="o">()</span> <span class="o">{</span>
<span class="n">filter</span><span class="o">(</span><span class="s">"/*"</span><span class="o">).</span><span class="na">through</span><span class="o">(</span><span class="nc">LoginFilter</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="n">serve</span><span class="o">(</span><span class="s">"/login"</span><span class="o">).</span><span class="na">with</span><span class="o">(</span><span class="nc">LoginServlet</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="c1">// els servlets migrats del server s'autoinjecten a l'init </span>
<span class="c1">// de BaseServlet</span>
<span class="o">}</span>
<span class="o">});</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>Sergi BailaSi queremos utilizar Guice en una aplicación web tenemos varias maneras de inicializar el entorno. El objetivo final es más o menos el mismo: configurar el filtro de Guice necesario para la integración del elemento esencial, el ServletModule.Compatibilidad de Jersey 2 y Guice2016-04-11T07:43:09-05:002016-04-11T07:43:09-05:00https://sargue.net/2016/04/11/compatibilidad-de-jersey-y-guice<p>La implementación de referencia de JAX-RS, <a href="https://jersey.java.net/">Jersey</a>, utiliza internamente una libreria de inyección de dependencias llamada <a href="https://hk2.java.net">HK2</a>.</p>
<p><a href="https://hk2.java.net">HK2</a> forma parte del ecosistema de Oracle / Glassfish igual que Jersey así que, en parte, tiene sentido. Sin embargo todo ese ecosistema no cuenta entre sus mejores cualidades con la interoperabilidad.</p>
<p>Uno de los problemas que he encontrado es la necesidad de trabajar junto con <a href="https://github.com/google/guice">Guice</a>, la libreria de inyección de dependencia de Google, que es la que suelo utilizar en mis proyectos.</p>
<p>Por suerte la gente de HK2 proporcionan un puente, <a href="https://hk2.java.net/guice-bridge/">guice-bridge</a>, para trabajar junto con Guice: una pequeña libreria que permite la inyección en ambos sentidos por separado o de manera simultanea.</p>
<p>En mi caso concreto suelo necesitar sólo un sentido: inyectar objetos creados con Guice en recursos Jersey (y por tanto creados por HK2). Para ello tan sólo he de añadir un fragmento de código en el inicializador del API REST (la clase que hace el <em>bootstrap</em> de Jersey).</p>
<figure class="highlight"><pre><code class="language-java" data-lang="java"><span class="nd">@ApplicationPath</span><span class="o">(</span><span class="s">"api"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">ApiRest</span> <span class="kd">extends</span> <span class="nc">ResourceConfig</span> <span class="o">{</span>
<span class="kd">private</span> <span class="kd">static</span> <span class="kd">final</span> <span class="nc">Logger</span> <span class="n">log</span> <span class="o">=</span> <span class="nc">LoggerFactory</span><span class="o">.</span><span class="na">getLogger</span><span class="o">(</span><span class="nc">ApiRest</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nd">@Inject</span>
<span class="kd">public</span> <span class="nf">ApiRest</span><span class="o">(</span><span class="nc">ServiceLocator</span> <span class="n">serviceLocator</span><span class="o">,</span> <span class="nc">ServletContext</span> <span class="n">servletContext</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"Inicialitzant Jersey."</span><span class="o">);</span>
<span class="n">packages</span><span class="o">(</span><span class="s">"net.sargue.app.api.providers"</span><span class="o">);</span>
<span class="n">packages</span><span class="o">(</span><span class="s">"net.sargue.app.api"</span><span class="o">);</span>
<span class="n">packages</span><span class="o">(</span><span class="s">"org.glassfish.jersey.examples.multipart"</span><span class="o">);</span>
<span class="n">register</span><span class="o">(</span><span class="nc">MultiPartFeature</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nc">GuiceBridge</span><span class="o">.</span><span class="na">getGuiceBridge</span><span class="o">().</span><span class="na">initializeGuiceBridge</span><span class="o">(</span><span class="n">serviceLocator</span><span class="o">);</span>
<span class="nc">GuiceIntoHK2Bridge</span> <span class="n">guiceBridge</span> <span class="o">=</span> <span class="n">serviceLocator</span><span class="o">.</span><span class="na">getService</span><span class="o">(</span><span class="nc">GuiceIntoHK2Bridge</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="nc">Injector</span> <span class="n">injector</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Injector</span><span class="o">)</span> <span class="n">servletContext</span><span class="o">.</span><span class="na">getAttribute</span><span class="o">(</span><span class="nc">Injector</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getName</span><span class="o">());</span>
<span class="k">if</span> <span class="o">(</span><span class="n">injector</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="s">"Guice Injector not found"</span><span class="o">);</span>
<span class="n">guiceBridge</span><span class="o">.</span><span class="na">bridgeGuiceInjector</span><span class="o">(</span><span class="n">injector</span><span class="o">);</span>
<span class="n">register</span><span class="o">(</span><span class="nc">RolesAllowedDynamicFeature</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span></code></pre></figure>Sergi BailaLa implementación de referencia de JAX-RS, Jersey, utiliza internamente una libreria de inyección de dependencias llamada HK2.Checklist de instalación de servidor Debian2016-03-24T11:36:07-05:002016-03-24T11:36:07-05:00https://sargue.net/2016/03/24/checklist-de-instalacion-de-servidor-debian<p>Una lista de los pasos que hago tras la instalación base de un servidor Linux, sea en local (raramente) o remoto (habitualmente). Esta lista es una generalización: no todos los pasos se aplican a todos los servidores y hay servidores que requieren otros pasos. Pero me sirve como guia de trabajo.</p>
<p>Aunque se trata de un post de 2016 lo sigo revisando y actualizando de vez en cuando. <strong>Última actualización: 26/3/2019</strong></p>
<p>Lamento el poco detalle, es una lista de uso interno pero pese a ello he pensado que puede ser util en cierta medida y por eso lo publico.</p>
<h3 id="checklist-de-instalación">Checklist de instalación</h3>
<ul>
<li>revisión fecha/hora/timezone: <code class="language-plaintext highlighter-rouge">dpkg-reconfigure tzdata</code></li>
<li>actualización inicial de paquetes: <code class="language-plaintext highlighter-rouge">apt update</code>, <code class="language-plaintext highlighter-rouge">apt upgrade</code></li>
<li>instalación de vim: <code class="language-plaintext highlighter-rouge">apt install vim</code></li>
<li>configuración vim: <code class="language-plaintext highlighter-rouge">vim /etc/vim/vimrc</code></li>
<li>ajuste del nombre del equipo: <code class="language-plaintext highlighter-rouge">vim /etc/hostname</code></li>
<li>configuración apt: <code class="language-plaintext highlighter-rouge">vim /etc/apt/sources.list</code></li>
<li>revisión del kernel instalado e instalación del kernel del paquete oficial de Debian</li>
<li>instalación generador de entropia: <code class="language-plaintext highlighter-rouge">apt install haveged</code></li>
<li>securización SSH con <a href="https://github.com/arthepsy/ssh-audit">ssh-audit</a></li>
<li>desactivación de validación con usuario y contraseña para SSH, sólo certificados</li>
<li>instalación fail2ban: <code class="language-plaintext highlighter-rouge">apt install fail2ban</code></li>
<li>instalación firewall: <code class="language-plaintext highlighter-rouge">apt install iptables-persistent</code></li>
<li>configuración firewall (ver anexo)</li>
<li>instalación msmtp-mta: <code class="language-plaintext highlighter-rouge">apt install msmtp-mta</code></li>
<li>instalación de logcheck, revisar fichero de configuración principal</li>
<li>instalación de cron-apt (ver anexo)</li>
<li>instalación y configuración de actualizaciones de seguridad automáticas (ver anexo)</li>
</ul>
<h3 id="anexo-cortafuegos">Anexo: cortafuegos</h3>
<p>Utilizo <code class="language-plaintext highlighter-rouge">netfilter-persistence</code> para la gestión de la configuración del cortafuegos. Ya que también utilizo <code class="language-plaintext highlighter-rouge">fail2ban</code> para guardar los cambios hago una pequeña variación para eliminar las reglas de <code class="language-plaintext highlighter-rouge">fail2ban</code> que son dinàmicas.</p>
<figure class="highlight"><pre><code class="language-shell_session" data-lang="shell_session"><span class="go">sudo iptables-save | grep -v fail2ban | sudo tee /etc/iptables/rules.v4
sudo ip6tables-save | grep -v fail2ban | sudo tee /etc/iptables/rules.v4</span></code></pre></figure>
<p>La configuración inicial de base es la siguiente:</p>
<p><code class="language-plaintext highlighter-rouge">/etc/iptables/rules.v4</code></p>
<figure class="highlight"><pre><code class="language-text" data-lang="text">*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:connection-tracking - [0:0]
:whitelist - [0:0]
-A INPUT -i lo -m comment --comment "permeto trafic loopback" -j ACCEPT
-A INPUT -j connection-tracking
-A INPUT -j whitelist
-A INPUT -p tcp -m tcp --dport 22 -m comment --comment "servei SSH" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -m comment --comment "servei HTTP" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -m comment --comment "servei HTTPS" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 113 -m comment --comment "Reject incoming AUTH/ident requests with RST" -j REJECT --reject-with tcp-reset
-A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 5/sec -m comment --comment "limitador de ping per DoS" -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -m comment --comment "limitador de ping per DoS" -j DROP
-A connection-tracking -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A connection-tracking -m conntrack --ctstate INVALID -j DROP
-A connection-tracking -m conntrack --ctstate NEW -j RETURN
-A connection-tracking -j LOG --log-prefix "CONNSTATE BARF: " --log-level 2
-A connection-tracking -j DROP
-A whitelist -s 1.2.3.0/24 -j ACCEPT
-A whitelist -s 5.6.7.8/32 -j ACCEPT
COMMIT</code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">/etc/iptables/rules.v6</code></p>
<figure class="highlight"><pre><code class="language-text" data-lang="text">*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:connection-tracking - [0:0]
-A INPUT -i lo -m comment --comment "permeto trafic loopback" -j ACCEPT
-A INPUT -p ipv6-icmp -m comment --comment "permeto trafic ICMPv6" -j ACCEPT
-A INPUT -j connection-tracking
-A INPUT -p tcp -m tcp --dport 80 -m comment --comment "servei HTTP" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -m comment --comment "servei HTTPS" -j ACCEPT
-A INPUT -p tcp -m tcp --dport 113 -m comment --comment "Reject incoming AUTH/ident requests with RST" -j REJECT --reject-with tcp-reset
-A connection-tracking -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A connection-tracking -m conntrack --ctstate INVALID -j DROP
-A connection-tracking -m conntrack --ctstate NEW -j RETURN
-A connection-tracking -j LOG --log-prefix "CONNSTATE BARF: " --log-level 2
-A connection-tracking -j DROP
COMMIT</code></pre></figure>
<h3 id="anexo-cron-apt">Anexo: <code class="language-plaintext highlighter-rouge">cron-apt</code></h3>
<p>Añadir algunas opciones al fichero de configuración principal: <code class="language-plaintext highlighter-rouge">/etc/cron-apt/config</code></p>
<figure class="highlight"><pre><code class="language-config" data-lang="config"><span class="n">MAILTO</span>=<span class="s2">"sargue@gmail.com"</span>
<span class="n">MAILON</span>=<span class="s2">"upgrade"</span>
<span class="n">SYSLOGON</span>=<span class="s2">"error"</span></code></pre></figure>
<h3 id="anexo-actualizaciones-automáticas">Anexo: actualizaciones automáticas</h3>
<p>Opcionalmente configurar el servicio de <a href="https://wiki.debian.org/UnattendedUpgrades">actualizaciones de seguridad automáticas</a> de Debian.</p>
<p>Algunas de las opciones de configuración interesantes.</p>
<figure class="highlight"><pre><code class="language-config" data-lang="config">// <span class="n">Send</span> <span class="n">email</span> <span class="n">to</span> <span class="n">this</span> <span class="n">address</span> <span class="n">for</span> <span class="n">problems</span> <span class="n">or</span> <span class="n">packages</span> <span class="n">upgrades</span>
// <span class="n">If</span> <span class="n">empty</span> <span class="n">or</span> <span class="n">unset</span> <span class="n">then</span> <span class="n">no</span> <span class="n">email</span> <span class="n">is</span> <span class="n">sent</span>, <span class="n">make</span> <span class="n">sure</span> <span class="n">that</span> <span class="n">you</span>
// <span class="n">have</span> <span class="n">a</span> <span class="n">working</span> <span class="n">mail</span> <span class="n">setup</span> <span class="n">on</span> <span class="n">your</span> <span class="n">system</span>. <span class="n">A</span> <span class="n">package</span> <span class="n">that</span> <span class="n">provides</span>
// <span class="s1">'mailx'</span> <span class="n">must</span> <span class="n">be</span> <span class="n">installed</span>. <span class="n">E</span>.<span class="n">g</span>. <span class="s2">"user@example.com"</span>
<span class="n">Unattended</span>-<span class="n">Upgrade</span>::<span class="n">Mail</span> <span class="s2">"webmaster@sargue.net"</span>;
<span class="n">Unattended</span>-<span class="n">Upgrade</span>::<span class="n">Sender</span> <span class="s2">"vps42@mg.sargue.net"</span>;
// <span class="n">Do</span> <span class="n">automatic</span> <span class="n">removal</span> <span class="n">of</span> <span class="n">new</span> <span class="n">unused</span> <span class="n">dependencies</span> <span class="n">after</span> <span class="n">the</span> <span class="n">upgrade</span>
// (<span class="n">equivalent</span> <span class="n">to</span> <span class="n">apt</span>-<span class="n">get</span> <span class="n">autoremove</span>)
<span class="n">Unattended</span>-<span class="n">Upgrade</span>::<span class="n">Remove</span>-<span class="n">Unused</span>-<span class="n">Dependencies</span> <span class="s2">"true"</span>;
// <span class="n">Automatically</span> <span class="n">reboot</span> *<span class="n">WITHOUT</span> <span class="n">CONFIRMATION</span>* <span class="n">if</span>
// <span class="n">the</span> <span class="n">file</span> /<span class="n">var</span>/<span class="n">run</span>/<span class="n">reboot</span>-<span class="n">required</span> <span class="n">is</span> <span class="n">found</span> <span class="n">after</span> <span class="n">the</span> <span class="n">upgrade</span>
<span class="n">Unattended</span>-<span class="n">Upgrade</span>::<span class="n">Automatic</span>-<span class="n">Reboot</span> <span class="s2">"true"</span>;
// <span class="n">Automatically</span> <span class="n">reboot</span> <span class="n">even</span> <span class="n">if</span> <span class="n">there</span> <span class="n">are</span> <span class="n">users</span> <span class="n">currently</span> <span class="n">logged</span> <span class="n">in</span>.
<span class="n">Unattended</span>-<span class="n">Upgrade</span>::<span class="n">Automatic</span>-<span class="n">Reboot</span>-<span class="n">WithUsers</span> <span class="s2">"true"</span>;</code></pre></figure>Sergi BailaUna lista de los pasos que hago tras la instalación base de un servidor Linux, sea en local (raramente) o remoto (habitualmente). Esta lista es una generalización: no todos los pasos se aplican a todos los servidores y hay servidores que requieren otros pasos. Pero me sirve como guia de trabajo.Configuración de una réplica MariaDB sobre Debian2016-03-14T15:10:23-05:002016-03-14T15:10:23-05:00https://sargue.net/2016/03/14/configuracion-de-una-replica-mariadb-sobre-debian<p>En esta entrada documento como configurar una réplica de una base de datos MariaDB que se ejecuta sobre Debian tanto el servidor maestro como la réplica.</p>
<p>Para los ejemplos supondré que los servidores se llaman <code class="language-plaintext highlighter-rouge">vps1</code> el correspondiente al maestro y <code class="language-plaintext highlighter-rouge">vps2</code> el servidor de respaldo y réplica.</p>
<p>Asumo que en ambos servidores se instala el servicio de bases de datos MariaDB con su instalación a partir de los paquetes oficiales. En el caso del servidor de réplica <code class="language-plaintext highlighter-rouge">vps2</code> significa que esta configuración es la más sencilla si sólo hacemos la réplica de un servidor (<code class="language-plaintext highlighter-rouge">vps1</code>) y no necesitamos un servicio MariaDB en el propio servidor.</p>
<p>En caso contrario, y una solución que utilizo en otros casos, es ejecutar un binario con la misma versión que el servidor bajo un usuario nuevo en el servidor de réplica. De esa manera se puede tener un servidor de réplicas que sirvan a diferentes servidores maestros.</p>
<h3 id="procedimiento">Procedimiento</h3>
<h4 id="servidor-maestro-vps1">Servidor maestro <code class="language-plaintext highlighter-rouge">vps1</code></h4>
<p>Preparamos el servidor <code class="language-plaintext highlighter-rouge">vps1</code> para la replicación activando el log binario y creando un usuario que utilizaremos para el esclavo de replicación.</p>
<p>Nuevo fichero <code class="language-plaintext highlighter-rouge">/etc/mysql/conf.d/binlog.cnf</code>:</p>
<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="nn">[mysqld]</span>
<span class="err">log-bin</span>
<span class="py">server_id</span><span class="p">=</span><span class="s">1</span></code></pre></figure>
<p>Creación del usuario de replicación en <code class="language-plaintext highlighter-rouge">vps1</code>:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="k">CREATE</span> <span class="k">USER</span> <span class="s1">'slave-vps2'</span><span class="o">@</span><span class="s1">'vps2'</span> <span class="n">IDENTIFIED</span> <span class="k">BY</span> <span class="s1">'slavepass'</span><span class="p">;</span></code></pre></figure>
<p>Reiniciamos el servicio en <code class="language-plaintext highlighter-rouge">vps1</code> para que se produzca la activación del log binario de manera efectiva.</p>
<p>Ahora ya podemos exportar todos los datos existentes junto a las coordenadas de replicación.</p>
<p>Ejecutar en <code class="language-plaintext highlighter-rouge">vps1</code>:</p>
<figure class="highlight"><pre><code class="language-shell_session" data-lang="shell_session"><span class="gp">mysqldump --gtid --master-data --all-databases -u root -p ></span><span class="w"> </span>master.sql</code></pre></figure>
<h4 id="servidor-esclavo-vps2">Servidor esclavo <code class="language-plaintext highlighter-rouge">vps2</code></h4>
<p>Pasamos a trabajar en el servidor <code class="language-plaintext highlighter-rouge">vps2</code>. Lo primero es instalar el servicio MariaDB y ajustar la configuración para trabajar como esclavo.</p>
<p>Instalación de MariaDB en <code class="language-plaintext highlighter-rouge">vps2</code>:</p>
<figure class="highlight"><pre><code class="language-shell_session" data-lang="shell_session"><span class="go">aptitude install mariadb-server</span></code></pre></figure>
<p>Comentar <code class="language-plaintext highlighter-rouge">bind-address</code> en <code class="language-plaintext highlighter-rouge">my.cnf</code> para que escuche en todas las interfaces de red.</p>
<p>Añadir la configuración específica como esclavo en un nuevo fichero <code class="language-plaintext highlighter-rouge">/etc/mysql/conf.d/esclavo.cnf</code>:</p>
<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="nn">[mysqld]</span>
<span class="py">server_id</span><span class="p">=</span><span class="s">2</span>
<span class="py">report_host</span><span class="p">=</span><span class="s">192.168.x.x</span>
<span class="py">report_port</span><span class="p">=</span><span class="s">3306</span></code></pre></figure>
<p>La parte de <code class="language-plaintext highlighter-rouge">report_host</code> y <code class="language-plaintext highlighter-rouge">report-port</code> es opcional pero yo la utilizo para poder luego ejectuar una comprobación remotamente de la consistencia de la réplica utilizando la aplicación <code class="language-plaintext highlighter-rouge">pt-table-checksum</code>.</p>
<h5 id="actualización-de-la-contraseña-del-usuario-de-sistema-operativo">Actualización de la contraseña del usuario de sistema operativo</h5>
<p>Un breve comentario sobre un detalle importante a tener en cuenta acerca de este procedimiento. El sistema operativo Debian al instalar un paquete de base de datos genera y guarda un usuario específico utilizado por el sistema operativo para diversas tareas, como simplemente, por ejemplo, parar el servicio de manera ordenada.</p>
<p>Dicho usuario se almacena en el fichero <code class="language-plaintext highlighter-rouge">/etc/mysql/debian.cnf</code> en cada servidor. Siguiendo los pasos de este procedimiento estamos creando una réplica completa de <code class="language-plaintext highlighter-rouge">vps1</code> a <code class="language-plaintext highlighter-rouge">vps2</code> incluyendo todos los usuarios de la base de datos. Eso significa que al cargar los datos en <code class="language-plaintext highlighter-rouge">vps2</code> estamos cargando los usuarios de <code class="language-plaintext highlighter-rouge">vps1</code> incluyendo este usuario de sistema operativo.</p>
<p>La recomendación, pues, es copiar la contraseña de este usuario especial de <code class="language-plaintext highlighter-rouge">vps1</code> a <code class="language-plaintext highlighter-rouge">vps2</code> o realizar una replica excluyendo la base de datos <code class="language-plaintext highlighter-rouge">mysql</code>.</p>
<h5 id="carga-de-datos-e-inicio-de-la-réplica">Carga de datos e inicio de la réplica</h5>
<p>Cargamos los datos iniciales:</p>
<figure class="highlight"><pre><code class="language-shell_session" data-lang="shell_session"><span class="gp">vps1$</span><span class="w"> </span>scp master.sql vps2:</code></pre></figure>
<figure class="highlight"><pre><code class="language-shell_session" data-lang="shell_session"><span class="gp">vps2$</span><span class="w"> </span>mysql <span class="nt">-u</span> root <span class="nt">-p</span> < master.sql</code></pre></figure>
<p>Iniciamos la réplica:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><span class="n">CHANGE</span> <span class="n">MASTER</span> <span class="k">TO</span>
<span class="n">MASTER_HOST</span><span class="o">=</span><span class="s1">'192.168.xxxx'</span><span class="p">,</span>
<span class="n">MASTER_USER</span><span class="o">=</span><span class="s1">'slave-vps2'</span><span class="p">,</span>
<span class="n">MASTER_PASSWORD</span><span class="o">=</span><span class="s1">'slavepass'</span><span class="p">,</span>
<span class="n">MASTER_PORT</span><span class="o">=</span><span class="mi">3306</span><span class="p">,</span>
<span class="n">MASTER_CONNECT_RETRY</span><span class="o">=</span><span class="mi">10</span><span class="p">;</span>
<span class="k">START</span> <span class="n">SLAVE</span><span class="p">;</span>
<span class="k">SHOW</span> <span class="n">SLAVE</span> <span class="n">STATUS</span><span class="err">\</span><span class="k">G</span></code></pre></figure>Sergi BailaEn esta entrada documento como configurar una réplica de una base de datos MariaDB que se ejecuta sobre Debian tanto el servidor maestro como la réplica.Configuración de envio de correo en Debian con SSMTP2016-03-11T17:17:05-06:002016-03-11T17:17:05-06:00https://sargue.net/2016/03/11/configuracion-de-envio-de-correo-en-debian-con-ssmtp<p>Hoy en dia la mayoria de servidores no gestionan su propio correo electrónico pero sí necesitan enviar correos electrónicos para notificaciones, avisos o cualquier otro menester.</p>
<p>Históricamente he venido instalando el paquete Exim (el estándar Debian) configurado sólo para envio via un <em>smarthost</em> (un servidor encargado de aceptar y entregar el correo).</p>
<p>Sin embargo esa solución desperdicia bastantes recursos ya que no necesitamos, ni de lejos, nada tan complejo como un MTA completo. Ahí entra SSMTP. Se trata de una pequeña aplicación que hace las funciones de envio de correo de un MTA emulando las llamadas al venerable <em>sendmail</em> por linea de comandos.</p>
<h2 id="instalación">Instalación</h2>
<p>Debian provee un <a href="https://packages.debian.org/jessie/ssmtp">paquete estándar</a> con lo que la instalación es trivial.</p>
<h2 id="configuración">Configuración</h2>
<p>La configuración tampoco es complicada. Necesitamos un servidor SMTP que nos permita reenviar correo, un usuari y una contraseña. Puede ser el de cualquier cuenta de correo incluyendo GMail y servicios similares. Sin embargo yo habitualmente utilizo el servicio SMTP de <a href="http://www.mailgun.com/">Mailgun</a> ya que dispongo de cuenta para utilizarlo al desarrollar.</p>
<p>En todo caso la configuración no difiere así que yo voy a utilizar los siguientes datos de ejemplo:</p>
<ul>
<li>Servidor: stmp.ejemplo.com</li>
<li>Usuario: marty@ejemplo.com</li>
<li>Contraseña: DeLorean1955</li>
</ul>
<p>Voy a suponer que el usuario y la dirección de correo de la cuenta coinciden ya que es lo más habitual. Sino habria que ajustar algunos campos.</p>
<p>La configuración de SSMTP se realiza en dos ficheros, ambos en el directorio <code class="language-plaintext highlighter-rouge">/etc/ssmtp</code>:</p>
<p><strong>revaliases</strong></p>
<figure class="highlight"><pre><code class="language-config" data-lang="config"><span class="c"># sSMTP aliases
#
# Format: local_account:outgoing_address:mailhub
#
# Example: root:your_login@your.domain:mailhub.your.domain[:port]
# where [:port] is an optional port number that defaults to 25.
</span><span class="n">root</span>:<span class="n">marty</span>@<span class="n">ejemplo</span>.<span class="n">com</span>:<span class="n">stmp</span>.<span class="n">ejemplo</span>.<span class="n">com</span></code></pre></figure>
<p><strong>ssmtp.conf</strong></p>
<figure class="highlight"><pre><code class="language-config" data-lang="config"><span class="c">#
# Config file for sSMTP sendmail
#
# The person who gets all mail for userids < 1000
# Make this empty to disable rewriting.
</span><span class="n">root</span>=<span class="n">marty</span>@<span class="n">ejemplo</span>.<span class="n">com</span>
<span class="c"># The place where the mail goes. The actual machine name is required no
# MX records are consulted. Commonly mailhosts are named mail.domain.com
</span><span class="n">mailhub</span>=<span class="n">stmp</span>.<span class="n">ejemplo</span>.<span class="n">com</span>
<span class="c"># Where will the mail seem to come from?
#rewriteDomain=
</span>
<span class="c"># The full hostname
</span><span class="n">hostname</span>=<span class="n">mi</span>.<span class="n">servidor</span>.<span class="n">com</span>
<span class="c"># Are users allowed to set their own From: address?
# YES - Allow the user to specify their own From: address
# NO - Use the system generated From: address
</span><span class="n">FromLineOverride</span>=<span class="n">YES</span>
<span class="n">AuthUser</span>=<span class="n">marty</span>@<span class="n">ejemplo</span>.<span class="n">com</span>
<span class="n">AuthPass</span>=<span class="n">DeLorean1995</span>
<span class="n">UseTLS</span>=<span class="n">YES</span>
<span class="n">UseSTARTTLS</span>=<span class="n">YES</span></code></pre></figure>
<h2 id="securización">Securización</h2>
<p>Por ultimo una nota sobre la seguridad. Como se puede ver la contraseña está en texto plano en un fichero que todo el mundo del sistema puede leer. Si probamos a cambiar el permiso del fichero <code class="language-plaintext highlighter-rouge">ssmtp.conf</code> enseguida vemos que los usuarios que no son <code class="language-plaintext highlighter-rouge">root</code> no pueden enviar correos.</p>
<p>La solución és utilizar un usuario específico para el servicio y la funcionalidad el <code class="language-plaintext highlighter-rouge">setuid</code>. La idea la saqué de <a href="http://askubuntu.com/questions/225094/my-password-is-in-a-configuration-file-how-do-i-prevent-read-access-to-other-us">askubuntu.com</a> aunque también está <a href="https://wiki.archlinux.org/index.php/SSMTP">la misma idea en la wiki de ArchLinux</a>.</p>
<p>El resumen de comandos:</p>
<figure class="highlight"><pre><code class="language-shell_session" data-lang="shell_session"><span class="go">sudo useradd -g nogroup -M -s /bin/false -c "sSMTP pseudo-user" ssmtp
cd /etc/ssmtp/
sudo chown ssmtp .
sudo chmod 4750 .
sudo chown ssmtp ssmtp.conf
sudo chmod 640 ssmtp.conf
sudo chown ssmtp:nogroup /usr/sbin/ssmtp
sudo chmod 4555 /usr/sbin/ssmtp</span></code></pre></figure>Sergi BailaHoy en dia la mayoria de servidores no gestionan su propio correo electrónico pero sí necesitan enviar correos electrónicos para notificaciones, avisos o cualquier otro menester.