<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>RobertOps 💻</title><description>No description</description><link>https://www.robertops.com/</link><language>es</language><item><title>Instalación de kubernetes utilizando Ansible</title><link>https://www.robertops.com/posts/2024-10-24_k8s_ansible/</link><guid isPermaLink="true">https://www.robertops.com/posts/2024-10-24_k8s_ansible/</guid><pubDate>Wed, 30 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;:::note
Los archivos del proyecto se encuentran en &lt;a href=&quot;https://github.com/robertorodriguez98/k3s-ansible&quot;&gt;este repositorio&lt;/a&gt;
:::&lt;/p&gt;
&lt;h2&gt;Preparación del escenario&lt;/h2&gt;
&lt;p&gt;Se van a crear 3 máquinas virtuales, 1 actuará de plano de control, y las otras 2 serán nodos. Para crear el escenario se va a usar &lt;strong&gt;Vagrant&lt;/strong&gt; con &lt;strong&gt;virtualbox&lt;/strong&gt; como proveedor, utilizando el siguiente &lt;code&gt;Vagrantfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&quot;2&quot;) do |config|
    config.vm.box = &quot;debian/bookworm64&quot;
    config.vm.box_check_update = false
    config.vm.synced_folder &apos;.&apos;, &apos;/vagrant&apos;, disabled: true
    config.vm.provider &quot;virtualbox&quot; do |v|
      v.memory = 1024
      v.cpus = 2
    end
    config.vm.define &quot;plano-control&quot; do |pcontrol|
      pcontrol.vm.hostname = &quot;plano-control&quot;
      pcontrol.vm.network &quot;private_network&quot;,
        ip: &quot;192.168.56.10&quot;
    end
    config.vm.define &quot;nodo1&quot; do |nodo1|
      nodo1.vm.hostname = &quot;nodo1&quot;
      nodo1.vm.network &quot;private_network&quot;,
        ip: &quot;192.168.56.20&quot;
    end
    config.vm.define &quot;nodo2&quot; do |nodo2|
      nodo2.vm.hostname = &quot;nodo2&quot;
      nodo2.vm.network &quot;private_network&quot;,
        ip: &quot;192.168.56.30&quot;
    end
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Instalación k8s&lt;/h2&gt;
&lt;p&gt;Para instalar kubernetes se va a utilizar &lt;a href=&quot;https://k3s.io/&quot;&gt;k3s&lt;/a&gt;, que se instala siguiendo estos pasos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;en el &lt;strong&gt;plano de control&lt;/strong&gt;:&lt;pre&gt;&lt;code&gt;curl -sfL https://get.k3s.io | sh -
&lt;/code&gt;&lt;/pre&gt;
después se obtiene el token en &lt;code&gt;/var/lib/rancher/k3s/server/node-token&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;en los nodos:&lt;pre&gt;&lt;code&gt;curl -sfL https://get.k3s.io | K3S_URL=https://[ip plano control]:6443 K3S_TOKEN=[token] sh -
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ansible&lt;/h3&gt;
&lt;p&gt;sin embargo, nos interesa automatizar dicho proceso ya que haciendo esto permite escalar el número de nodos fácilmente. para ello, se va a usar &lt;a href=&quot;https://www.ansible.com/&quot;&gt;ansible&lt;/a&gt;. Se han configurado los siguientes ficheros.&lt;/p&gt;
&lt;h3&gt;Inventario&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;all:
  children:
    planos_control:
      hosts:
        p_nodo: 
          ansible_ssh_host: 192.168.56.10
          ansible_ssh_user: vagrant
          ansible_ssh_private_key_file: ../.vagrant/machines/plano-control/virtualbox/private_key
    trabajadores:
      hosts:
        nodo1: 
          ansible_ssh_host: 192.168.56.20
          ansible_ssh_user: vagrant
          ansible_ssh_private_key_file: ../.vagrant/machines/nodo1/virtualbox/private_key
        nodo2: 
          ansible_ssh_host: 192.168.56.30
          ansible_ssh_user: vagrant
          ansible_ssh_private_key_file: ../.vagrant/machines/nodo2/virtualbox/private_key

  vars:
    token_k3s: &quot;&quot;
    token_k3s_base64: &quot;&quot;
    ip_pcontrol: &quot;192.168.56.10&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Playbook&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- hosts: all
  become: true
  tasks:
    - name: Actualizamos el sistema
      apt: update_cache=yes upgrade=yes
    - name: nos aseguramos de que curl esté instalado
      apt:
        pkg: 
          - curl

- hosts: planos_control
  become: true
  tasks:
    # Para que no haya problemas de certificados al usar kubectl, añadimos la IP del plano de control durante la instalación de k3s.
    - name: instalamos k3s
      shell: &quot;curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC=&apos;--tls-san {{ ip_pcontrol }}&apos; sh -&quot;
    - name: sacar token
      ansible.builtin.slurp:
        src: &quot;/var/lib/rancher/k3s/server/node-token&quot;
      register: token_k3s_base64
    - name: descodificar token
      ansible.builtin.set_fact:
        token_k3s: &quot;{{ token_k3s_base64.content | ansible.builtin.b64decode | replace(&apos;\n&apos;, &apos;&apos; ) }}&quot;
    - debug: msg=&quot;el token es {{token_k3s}}&quot;

- hosts: trabajadores
  become: true
  tasks:
    - name: instalamos k3s con el token
      shell: &quot;curl -sfL https://get.k3s.io | K3S_URL=https://{{ ip_pcontrol }}:6443 K3S_TOKEN={{ hostvars[&apos;p_nodo&apos;].token_k3s }} sh -&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras ejecutar el playbook, k3s se habrá instalado correctamente. Podemos comprobarlo con &lt;code&gt;kubectl&lt;/code&gt;, primero copiamos la configuración de kubernetes del plano de control a nuestro kubeconfig :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export KUBECONFIG=~/.kube/config
mkdir ~/.kube 2&amp;gt; /dev/null
chmod 600 &quot;$KUBECONFIG&quot;
ssh -i .vagrant/machines/plano-control/virtualbox/private_key vagrant@192.168.56.10 &apos;sudo k3s kubectl config view --raw&apos; &amp;gt; ~/.kube/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y en el fichero modificamos la ip de localhost por la de la máquina:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sed -i &apos;s/127\.0\.0\.1/192\.168\.56\.10/g&apos; $KUBECONFIG
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente podemos usar kubectl.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;get_nodes.png&quot; alt=&quot;kubectl get nodes&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Instalación Gitea</title><link>https://www.robertops.com/posts/2023-06-14_instalacion-gitea/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-06-14_instalacion-gitea/</guid><pubDate>Wed, 14 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;Gitea&lt;/h1&gt;
&lt;p&gt;Gitea es una solución ligera de alojamiento de código gestionada por la comunidad y escrita en Go, tiene las principales características que tiene github, incluyendo actions. Ahora veremos la instalación de gitea en la VPS.&lt;/p&gt;
&lt;h2&gt;Preparación&lt;/h2&gt;
&lt;p&gt;En mi caso voy a instalarlo  utilizando &lt;code&gt;docker-compose&lt;/code&gt;, para ello, y teniendo en cuenta que uso debian, es necesario ejecutar los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt install -y docker.io docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;Para instalarlo, voy a optar por la opción con una base de datos PostgreSQL. Para ello, en un directorio vacío creamos el siguiente fichero &lt;code&gt;docker-compose.yml&lt;/code&gt; (cambiando las credenciales, claro):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &quot;3&quot;

networks:
  gitea:
    external: false

services:
  server:
    image: gitea/gitea:nightly
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
    networks:
      - gitea
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - &quot;3200:3000&quot;
      - &quot;222:22&quot;
    depends_on:
      - db

  db:
    image: postgres:14
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=gitea
      - POSTGRES_DB=gitea
    networks:
      - gitea
    volumes:
      - ./postgres:/var/lib/postgresql/data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y lo desplegamos con&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con eso ya se habrían creado los contenedores. Además, para poder entrar en gitea desde fuera de la VPS usando el dominio, vamos a añadir el siguiente proxy inverso al servidor nginx:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    if ($host ~ ^[^.]+\.admichin\.es$) {
        return 301 https://$host$request_uri;
    } # managed by Certbot
        listen 80;
        listen [::]:80;
        server_name gitea.admichin.es;
        return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl    on;
    ssl_certificate /etc/letsencrypt/live/admichin.es-0001/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/admichin.es-0001/privkey.pem; # managed by Certbot

    index index.html index.php index.htm index.nginx-debian.html;
    server_name gitea.admichin.es;

    location / {
            proxy_pass http://localhost:3200;
            include proxy_params;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con esto hecho, si accedemos a &lt;a href=&quot;https://gitea.admichin.es&quot;&gt;gitea.admichin.es&lt;/a&gt; por primera vez, se abrirá la página de configuración:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/es0cMvq.png&quot; alt=&quot;configuracion1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En esta configuración es importante poner el dominio (más abajo, no donde sale en la foto ya que esa es la configuración de la base de datos). Así como establecer la configuración de ssh y el nombre del sitio. Tras finalizar la configuración, o en la misma ya que es una opción, el primer usuario que se cree será el administrador. Con esto ya tendríamos gitea instalado y funcionando.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dAKzydC.png&quot; alt=&quot;principal&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Actions&lt;/h1&gt;
&lt;p&gt;Gitea, desde la versión 1.19 permite añadir actions, que son similares a las GitHub actions. Aunque actualicemos a dicha versión, no aparecerán, ya que tenemos que añadir lo siguiente al final del fichero de configuración, que se encuentra en &lt;code&gt;data/gitea/conf/app.ini&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[actions]
ENABLED=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez activado, aparece la siguiente opción en el apartado de administración:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OeZEsB5.png&quot; alt=&quot;actions&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para el paso que irá a continuación, vamos a necesitar el  token de registro. Para obtenerlo se accede al apartado de Runners y al botón de &lt;code&gt;Create a new Runner&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/bpHH3qp.png&quot; alt=&quot;token&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Runners&lt;/h2&gt;
&lt;p&gt;Un runner es una máquina que ejecuta las tareas de un workflow de actions. En mi caso voy a utilizar un contenedor como runner dentro de la VPS también, pero hay varios métodos:&lt;/p&gt;
&lt;h3&gt;En local&lt;/h3&gt;
&lt;p&gt;Descargamos el binario de &lt;a href=&quot;https://gitea.com/gitea/act_runner&quot;&gt;https://gitea.com/gitea/act_runner&lt;/a&gt; adecuado para nuestro sistema y ejecutamos los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./act_runner register --no-interactive --instance &amp;lt;instance&amp;gt; --token &amp;lt;token&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donde la instancia es la dirección o IP en la que esté alojada Gitea, y el token es el token que hemos obtenido previamente. Tras esto, ejecutamos el runner:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./act_runner daemon
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;En contenedor&lt;/h3&gt;
&lt;p&gt;Para ejecutarlo con docker voy a usar docker-compose. para ello, creo un nuevo directorio con el siguiente fichero docker-compose.yml:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &quot;3&quot;

services:
  runner:
    image: gitea/act_runner
    restart: always
    volumes:
      - ./data/act_runner:/data
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - GITEA_INSTANCE_URL=instance
      - GITEA_RUNNER_REGISTRATION_TOKEN=token
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al igual que en el caso anterior la instancia es la dirección o IP en la que esté alojada Gitea, y el token es el token que hemos obtenido previamente.&lt;/p&gt;
&lt;p&gt;Una vez en ejecución, los runners aparecen de la siguiente manera:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/iN7zF86.png&quot; alt=&quot;runners&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Activar actions&lt;/h2&gt;
&lt;p&gt;Aunque esté configurado, las actions están desactivadas por defecto en los repositorios. Para activarlas hay que acceder a la configuración, y en el apartado de ajustes avanzados activarlas:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/C9gBaBP.png&quot; alt=&quot;activar&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Podemos ver que se ha activado porque aparece el botón de actions en el repositorio, y podemos añadirlas como se añadirían en GitHub:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DHLtkJY.png&quot; alt=&quot;boton&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;Enlaces de interés&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.gitea.com/next/installation/install-with-docker&quot;&gt;Gitea Installation with Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.gitea.io/2022/12/feature-preview-gitea-actions/&quot;&gt;Feature Preview: Gitea Actions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.gitea.io/2023/03/hacking-on-gitea-actions/&quot;&gt;Hacking on Gitea Actions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Despliegue de EKS usando CrossPlane + ArgoCD</title><link>https://www.robertops.com/posts/2023-06-01_despliegue-crossplane-eks/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-06-01_despliegue-crossplane-eks/</guid><pubDate>Thu, 01 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Descripción&lt;/h2&gt;
&lt;p&gt;El objetivo del proyecto es, utilizando Crossplane, desplegar un clúster de eks y luego sobre éste desplegar una aplicación web estática utilizando para ambos casos como apoyo ArgoCD.&lt;/p&gt;
&lt;p&gt;A pesar de existir una alternativa de IaC como terraform, se ha decidido usar Crossplane ya que permite utilizar los comandos nativos de kubernetes para gestionar todos sus recursos, al ser una extensión del mismo.&lt;/p&gt;
&lt;p&gt;Todos los manifiestos y archivos de configuración se encuentran en el siguiente &lt;a href=&quot;https://github.com/robertorodriguez98/proyecto-integrado&quot;&gt;repositorio&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Pincha aquí para ir directamente a la configuración del &lt;a href=&quot;#escenario-realizado&quot;&gt;&lt;strong&gt;escenario realizado&lt;/strong&gt;&lt;/a&gt; o aquí para ir directamente a las &lt;a href=&quot;#demostraciones&quot;&gt;&lt;strong&gt;demos&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;También en la página de los proyectos integrados se pueden encontrar la documentación en formato pdf, la presentación, y el vídeo de demostración:&lt;/p&gt;
&lt;p&gt;https://dit.gonzalonazareno.org/gestiona/proyectos/&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Objetivos que se quieren conseguir&lt;/h2&gt;
&lt;p&gt;Los objetivos que se quieren conseguir son los siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lograr una mayor &lt;strong&gt;comprensión&lt;/strong&gt; de lo que es &lt;strong&gt;Crossplane&lt;/strong&gt; y sus posibles aplicaciones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatización&lt;/strong&gt;: La definición de la infraestructura como código y la automatización del proceso de implementación y gestión de la infraestructura permite a los equipos de desarrollo y operaciones centrarse en tareas más importantes y reducir los errores humanos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Escalabilidad&lt;/strong&gt;: Otra ventaja de Kubernetes es su capacidad de escalar automáticamente, tanto hacia arriba como hacia abajo, en función de las necesidades del sistema. Al utilizar Crossplane para gestionar la infraestructura de AWS, se pueden aprovechar las capacidades de escalabilidad de AWS y de Kubernetes para garantizar que el sistema siempre tenga los recursos necesarios para funcionar correctamente.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Portabilidad&lt;/strong&gt;: Al utilizar Crossplane, se puede asegurar que la infraestructura es portátil y se puede mover fácilmente a otras nubes o proveedores de infraestructura en el futuro. Esto permite a las organizaciones evitar el bloqueo de proveedores y aprovechar las ventajas de diferentes proveedores de nube según sea necesario.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Costes&lt;/strong&gt;: La utilización de una infraestructura basada en la nube puede ser costosa, por lo que es importante asegurarse de que los recursos se utilizan de manera eficiente y se minimizan los costos innecesarios. Al utilizar Crossplane para gestionar la infraestructura de AWS, se puede optimizar el uso de los recursos y evitar pagar por recursos no utilizados.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;¿Por qué Crossplane?&lt;/h3&gt;
&lt;p&gt;La &lt;strong&gt;principal ventaja&lt;/strong&gt; de Crossplane es que, aún existiendo frameworks que permitan desplegar la misma infraestructura, Crossplane traslada esas interacciones a &lt;strong&gt;kubernetes&lt;/strong&gt;, utilizando sus comandos propios y sus métodos.  Esto quiere decir que, por ejemplo, habiendo desplegado un clúster en AWS, se crean automáticamente los siguientes objetos de kubernetes enlazados a la api de AWS:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/A8vDXMj.png&quot; alt=&quot;objetos de kubernetes creados&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Además, utilizando kubectl podemos obtener aún más información acerca de estos objetos, dentro del propio clúster local:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/hlc1Rae.png&quot; alt=&quot;cluster local&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En la imagen se puede obtener toda la información referente a el objeto indicado, en este caso un nodegroup, y podemos ver, por ejemplo, el rol de nodo que tiene asignado, la región en la que se encuentra, las subredes disponibles…&lt;/p&gt;
&lt;p&gt;Aparte de eso, las ventajas que tiene frente el principal competidor, terraform, son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ficheros más entendibles (YAML)&lt;/strong&gt;: Crossplane utiliza archivos YAML para definir la infraestructura, lo que los hace más fáciles de entender y leer para los desarrolladores que pueden estar más familiarizados con el formato YAML. Terraform, por otro lado, utiliza su propio lenguaje de configuración llamado HCL (HashiCorp Configuration Language), que puede tener una curva de aprendizaje para aquellos que no están familiarizados con él.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Permite desplegar indistintamente en los proveedores cloud&lt;/strong&gt;: Crossplane se enfoca en la gestión de la infraestructura multi-nube, lo que significa que permite a los usuarios definir y gestionar recursos en múltiples proveedores de nube utilizando la misma sintaxis de Kubernetes. Terraform también soporta múltiples proveedores de nube, pero requiere la definición de cada recurso utilizando un proveedor de nube específico.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metodología GitOps&lt;/strong&gt;: Crossplane está diseñado para trabajar en una metodología GitOps, lo que significa que todas las definiciones de recursos se almacenan en un repositorio Git y los cambios se aplican automáticamente al clúster de Kubernetes utilizando un proceso de integración y entrega continua (CI/CD). Terraform no está diseñado específicamente para trabajar con GitOps, aunque puede integrarse con sistemas de control de versiones como Git para almacenar y gestionar el código de infraestructura.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Fundamentos teóricos y conceptos&lt;/h2&gt;
&lt;h3&gt;Kubernetes&lt;/h3&gt;
&lt;p&gt;Kubernetes o k8s para acortar, es una plataforma de sistema distribuido de código libre para la automatización del despliegue, ajuste de escala y manejo de aplicaciones. Una de las principales ventajas de Kubernetes es que ofrece una plataforma común para el desarrollo y la producción de aplicaciones. Esto significa que los equipos de desarrollo pueden crear aplicaciones en sus equipos y luego trasladarlas sin problemas a un entorno de producción utilizando las mismas herramientas y procesos. Kubernetes proporciona una capa de abstracción entre la infraestructura subyacente y las aplicaciones que se ejecutan en ella, lo que facilita la portabilidad de las aplicaciones entre diferentes plataformas y proveedores de nube. Además, Kubernetes ofrece características de autoreparación, lo que significa que las aplicaciones pueden recuperarse automáticamente de fallos en tiempo de ejecución, sin necesidad de intervención humana.&lt;/p&gt;
&lt;h4&gt;Plano de control&lt;/h4&gt;
&lt;p&gt;El plano de control de Kubernetes es el conjunto de componentes que se encargan de gestionar el estado del clúster y de coordinar todas las operaciones en el mismo. Estos componentes son responsables de tomar decisiones sobre la orquestación y el escalado de los contenedores y aplicaciones en el clúster, y de garantizar que el estado deseado del clúster se mantenga en todo momento.&lt;/p&gt;
&lt;h3&gt;GitOps&lt;/h3&gt;
&lt;p&gt;La metodología GitOps es un enfoque para la entrega continua de aplicaciones en la nube que utiliza Git como fuente de verdad para la configuración y la implementación de la infraestructura y las aplicaciones. En la metodología GitOps, todas las definiciones de la infraestructura y las aplicaciones se almacenan en un repositorio Git centralizado. Los cambios en el repositorio Git son automáticamente detectados por una herramienta de despliegue, que se encarga de implementar los cambios en la infraestructura y las aplicaciones.&lt;/p&gt;
&lt;p&gt;La metodología GitOps se basa en los principios de la automatización, la colaboración y la transparencia. En primer lugar, la metodología GitOps&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatización&lt;/strong&gt; de la gestión de la infraestructura y las aplicaciones, lo que permite la implementación continua de cambios en un entorno controlado y seguro.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Colaboración&lt;/strong&gt; entre los miembros del equipo, ya que todos los cambios se realizan en el repositorio Git centralizado, lo que permite a los miembros del equipo trabajar en conjunto de manera más eficiente.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transparencia&lt;/strong&gt;, ya que todos los cambios y versiones se registran en el repositorio Git centralizado, lo que permite a los miembros del equipo revisar y rastrear el historial de cambios.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;ArgoCD&lt;/h3&gt;
&lt;p&gt;Argo CD es una herramienta de entrega continua (Continuous Delivery) y de operaciones de infraestructura (Infrastructure Operations) que se ejecuta en Kubernetes. Permite la automatización y el control del proceso de implementación y despliegue de aplicaciones en un clúster de Kubernetes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/893wkC8.png&quot; alt=&quot;ArgoCD&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una de las características más destacadas de Argo CD es su capacidad para automatizar la gestión de versiones y el despliegue de aplicaciones. Permite la definición de flujos de trabajo (workflows) para la implementación de cambios, lo que garantiza que los cambios se realicen de manera controlada y segura. Argo CD también incluye características de seguridad, como el control de acceso basado en roles y la autenticación de usuarios.&lt;/p&gt;
&lt;h3&gt;Infraestructura como código&lt;/h3&gt;
&lt;p&gt;Es una práctica que consiste en definir la infraestructura de una aplicación o sistema de forma programática utilizando un lenguaje de programación o una sintaxis específica. En lugar de configurar manualmente servidores, redes y otros recursos de infraestructura, los desarrolladores y los equipos de operaciones pueden definir la infraestructura como código y utilizar herramientas de automatización para gestionar y desplegar la infraestructura en un entorno reproducible y escalable.&lt;/p&gt;
&lt;h3&gt;Crossplane&lt;/h3&gt;
&lt;p&gt;Crossplane es una extensión de kubernetes que transforma kubernetes en un &lt;strong&gt;plano de control Universal&lt;/strong&gt;. Permite a los usuarios utilizar cualquier API como si fuera nativa de Kubernetes. Esto se logra mediante el uso de los recursos personalizados de Kubernetes (CRD), que permiten a los desarrolladores definir y extender los recursos de Kubernetes con sus propias definiciones. Puede crear y gestionar recursos que no son nativos de Kubernetes, como bases de datos, servicios de almacenamiento en la nube, cuentas de usuario y cualquier otra cosa que una API pueda ofrecer.&lt;/p&gt;
&lt;p&gt;Además, Crossplane es compatible con &lt;strong&gt;múltiples proveedores de nube&lt;/strong&gt;, lo que significa que los usuarios pueden utilizar cualquier servicio de cualquier proveedor de nube, incluidos Amazon Web Services, Google Cloud Platform y Microsoft Azure, entre otros. Esto permite a los usuarios crear &lt;strong&gt;aplicaciones que utilizan servicios de varios proveedores de nube&lt;/strong&gt; sin tener que preocuparse por la complejidad de gestionar múltiples APIs de nube diferentes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fJhMKBU.png&quot; alt=&quot;Crossplane&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Proveedor&lt;/h4&gt;
&lt;p&gt;Un proveedor de Crossplane le permite aprovisionar una &lt;strong&gt;infraestructura en un servicio externo&lt;/strong&gt;. se utiliza, entre otras cosas, para la autenticación, la realización de llamadas a API externas y utilizando APIs de kubernetes.&lt;/p&gt;
&lt;p&gt;Para utilizar un proveedor además, es necesaria una configuración. Ésta se realiza por medio del uso de &lt;strong&gt;providerConfig&lt;/strong&gt;. Normalmente se utilizan para autenticarse con la API con la que se está comunicando, En este proyecto, se utilizan tanto con las credenciales de &lt;strong&gt;AWS&lt;/strong&gt; como con el &lt;strong&gt;clúster de EKS&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Escenario realizado&lt;/h2&gt;
&lt;p&gt;El escenario consiste en lo siguiente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clúster local de kubernetes, en el que se han instalado las siguientes aplicaciones:
&lt;ul&gt;
&lt;li&gt;ArgoCD&lt;/li&gt;
&lt;li&gt;Crossplane, con los proveedores de AWS y Kubernetes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Clúster de EKS creado y gestionado por Crossplane, en el que hay 3 nodos.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/5KqPm1R.png&quot; alt=&quot;Escenario&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Instalaciones&lt;/h3&gt;
&lt;p&gt;Para la realización del proyecto, se necesita instalar los siguientes recursos:&lt;/p&gt;
&lt;h4&gt;Clúster local de kubernetes&lt;/h4&gt;
&lt;p&gt;Para desplegar un clúster local de k8s, se usará &lt;strong&gt;kind&lt;/strong&gt;, ya que el proveedor de aws de crossplane consume muchos recursos, y kind es más liviano. Para su instalación se seguirá la documentación oficial: &lt;a href=&quot;https://kind.sigs.k8s.io/docs/user/quick-start/&quot;&gt;Instalación kind&lt;/a&gt;. Para gestionar el clúster también es necesario kubectl, y la documentación oficial de instalación es la siguiente: &lt;a href=&quot;https://kubernetes.io/es/docs/tasks/tools/install-kubectl/&quot;&gt;instalación kubectl&lt;/a&gt;. Es muy importante partir de un &lt;strong&gt;clúster nuevo&lt;/strong&gt; y sin configuraciones previas.&lt;/p&gt;
&lt;h4&gt;ArgoCD&lt;/h4&gt;
&lt;p&gt;Para instalar ArgoCD se ejecutan los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para acceder a la página de administración, necesito la contraseña inicial. Se obtiene con el siguiente comando; el usuario es admin:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath=&quot;{.data.password}&quot; | base64 -d;echo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y para acceder a la página, se realiza el siguiente port-forward:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl port-forward svc/argocd-server -n argocd 8080:80 --address=0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso ya está configurado ArgoCD.&lt;/p&gt;
&lt;h4&gt;Crossplane&lt;/h4&gt;
&lt;p&gt;Para instalar crossplane utilizamos helm&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;helm repo add \
crossplane-stable https://charts.crossplane.io/stable
helm repo update

helm install crossplane \
crossplane-stable/crossplane \
--namespace crossplane-system \
--create-namespace
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración&lt;/h3&gt;
&lt;h4&gt;AWS&lt;/h4&gt;
&lt;p&gt;Es necesaria una cuenta con los permisos de AWS para crear y gestionar los siguientes recursos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;instancias EC2&lt;/li&gt;
&lt;li&gt;clúster de EKS&lt;/li&gt;
&lt;li&gt;redes VPC&lt;/li&gt;
&lt;li&gt;Tokens de acceso a la propia cuenta
Una vez se disponga de dicha cuenta, creamos el token en el siguiente apartado:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;credenciales de seguridad &amp;gt; crear clave de acceso &amp;gt; seleccionamos el cuadro y 	crear clave de acceso &amp;gt; descargar archivo.csv&lt;/p&gt;
&lt;h4&gt;Proveedores de Crossplane&lt;/h4&gt;
&lt;h5&gt;AWS&lt;/h5&gt;
&lt;p&gt;Ahora instalamos la última versión del provider de AWS de crossplane&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/crossplane-contrib/provider-aws:v0.39.0
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso, creamos el fichero &lt;strong&gt;aws-credentials.txt&lt;/strong&gt; con las claves generadas en AWS con el siguiente formato:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[default]
aws_access_key_id = &amp;lt;aws_access_key&amp;gt;
aws_secret_access_key = &amp;lt;aws_secret_key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos un &lt;strong&gt;secret&lt;/strong&gt; de kubernetes con las credenciales&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl create secret \
generic aws-creds \
-n crossplane-system \
--from-file=creds=./aws-credentials.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente, se crea un &lt;strong&gt;providerconfig&lt;/strong&gt; con el secret que hemos creado&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: aws.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds
      key: creds
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;kubernetes&lt;/h5&gt;
&lt;p&gt;Instalamos el proveedor con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat &amp;lt;&amp;lt;EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: crossplane-provider-kubernetes
spec:
  package: crossplane/provider-kubernetes:main
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Recursos gestionados&lt;/h4&gt;
&lt;p&gt;Para desplegar un clúster de aws de forma sencilla, se van a utilizar dos recursos gestionados (managed resources):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;eks.yaml&lt;/strong&gt;, de tipo composition que contiene parámetros para la creación del clúster, como lo son las redes que se crearán, en qué servidores, qué tipo de máquinas pueden crearse, etc..&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;definition.yaml&lt;/strong&gt;, de tipo compositeResourceDefinition, contiene los parámetros que se le van a pasar con el manifiesto final, así como los recursos  a los que corresponden en composition. También contiene valores por defecto.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ambos ficheros se encuentran en el repositorio del proyecto en la carpeta crossplane:
https://github.com/robertorodriguez98/proyecto-integrado/tree/main/crossplane&lt;/p&gt;
&lt;p&gt;Para añadirlos, se utiliza kubectl:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl apply -f eks.yaml &amp;amp;&amp;amp; kubectl apply -f definition.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Ngrok&lt;/h4&gt;
&lt;p&gt;Para que ArgoCD esté atento a los commits de github y siga así la metodología GitOps, al tenerlo instalado en un clúster local, es necesario instalar ngrok, exponiendo el puerto 8080:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc &amp;gt;/dev/null &amp;amp;&amp;amp; echo &quot;deb https://ngrok-agent.s3.amazonaws.com buster main&quot; | sudo tee /etc/apt/sources.list.d/ngrok.list &amp;amp;&amp;amp; sudo apt update &amp;amp;&amp;amp; sudo apt install ngrok
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Añadimos el token:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ngrok config add-authtoken [TOKEN]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y exponemos el puerto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ngrok http https://localhost:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Webhook&lt;/h4&gt;
&lt;p&gt;Para que GitHub notifique a ArgoCD en el evento de un push al repositorio, tenemos que añadirlo en la configuración del mismo. Para hacerlo, en el repositorio accedemos al apartado &lt;strong&gt;Settings&lt;/strong&gt;; en la barra lateral seleccionamos &lt;strong&gt;Webhooks&lt;/strong&gt; y &lt;strong&gt;Add webhook&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Dentro de la página, tenemos que rellenar los siguientes campos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Payload URL&lt;/strong&gt;: la url generada por Ngrok, con la cadena /api/webhook añadida al final.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Content type&lt;/strong&gt;: application/json.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event&lt;/strong&gt;: Cuando se produce un&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Preparación previa a la demo&lt;/h4&gt;
&lt;p&gt;Para preparar la demo, iniciaremos la aplicación de argocd &lt;strong&gt;app.yaml&lt;/strong&gt; que se encuentra en el repositorio, ésta despliega el siguiente manifiesto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apiVersion: prodready.cluster/v1alpha1
kind: ClusterClaim
metadata:
  name: proyecto-eks
  labels:
    cluster-owner: robertorm
spec:
  id: proyecto-eks
  compositionSelector:
    matchLabels:
      provider: aws
      cluster: eks
  parameters:
    nodeSize: medium
    minNodeCount: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Donde podemos modificar los siguientes parámetros:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;name&lt;/strong&gt;: Nombre del clúster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;nodesize&lt;/strong&gt;: tipo de máquina que se van a utilizar en los nodos. En el fichero eks.yaml se establecen los siguientes tamaños:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;small: t2:micro&lt;/li&gt;
&lt;li&gt;medium: t3.micro&lt;/li&gt;
&lt;li&gt;large: t3.medium&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Los dos primeros tamaños, entran dentro de la capa gratuita de AWS.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;minNodeCount&lt;/strong&gt;: mínimo de nodos que tienen que crearse en el clúster.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tras aproximadamente &lt;strong&gt;15 minutos&lt;/strong&gt;, el clúster estará creado junto a sus nodos. Si observamos el panel de ArgoCD de la aplicación, podemos ver todos los recursos de AWS que se han desplegado:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/CaTYVUb.png&quot; alt=&quot;ArgoCD&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Demostraciones&lt;/h2&gt;
&lt;h3&gt;Modificación del número de nodos&lt;/h3&gt;
&lt;p&gt;El escenario final de la demo es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/5KqPm1R.png&quot; alt=&quot;Escenario&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y el flujo de trabajo en el que consiste la primera demo, con el clúster ya desplegado es el siguiente:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Se realiza un commit a &lt;strong&gt;github&lt;/strong&gt; con un cambio en el manifiesto &lt;strong&gt;aws-eks.yaml&lt;/strong&gt;, concretamente se cambia el número de nodos que va a tener el clúster de EKS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ArgoCD&lt;/strong&gt; se da cuenta de que se han realizado cambios en el repositorio, y, aplicando la metodología &lt;strong&gt;gitops&lt;/strong&gt;, hace que en los recursos que gestiona se vean reflejados dichos cambios. Compruebo que en el panel de la aplicación se ve reflejado el cambio en el recurso &lt;strong&gt;nodegroup&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crossplane&lt;/strong&gt;, haciendo uso del provider de AWS, se comunica con la API referente a EKS, e indica que el número de nodos ha cambiado.&lt;/li&gt;
&lt;li&gt;Finalmente, en &lt;strong&gt;AWS&lt;/strong&gt; se hacen efectivos los cambios, por lo que podemos comprobarlo accediendo a la consola de &lt;strong&gt;EKS&lt;/strong&gt; y viendo el nuevo nodo.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/5Up9qo2.png&quot; alt=&quot;Demo1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tras ello, para demostrar el funcionamiento de Crossplane, y como este asegura que se siga el marco de trabajo &lt;strong&gt;GitOps&lt;/strong&gt;, se añade un nodo desde la consola de AWS, mostrando como Crossplane lo detecta y vuelve a dejarlo como está definido en los recursos.&lt;/p&gt;
&lt;h3&gt;Despliegue en el clúster&lt;/h3&gt;
&lt;p&gt;Una vez con el clúster, se va a desplegar una aplicación sobre él, utilizando también crossplane. El escenario es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ZqjeuTb.png&quot; alt=&quot;Escenario2&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Configuración del proveedor de kubernetes&lt;/h4&gt;
&lt;p&gt;Para configurar el proveedor de kubernetes para que tenga acceso al clúster que acabamos de crear, se ejecutan los siguientes comandos; Primero obtenemos el &lt;strong&gt;kubeconfig&lt;/strong&gt; y lo guardamos en un fichero:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl --namespace crossplane-system \
    get secret proyecto-eks-cluster \
    --output jsonpath=&quot;{.data.kubeconfig}&quot; \
    | base64 -d &amp;gt;kubeconfig.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enviamos el contenido del fichero a la variable KUBECONFIG:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;KUBECONFIG=$(&amp;lt;kubeconfig.yaml)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Usando la variable, creamos un secret que pueda usar el proveedor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl -n crossplane-system create secret generic cluster-config --from-literal=kubeconfig=&quot;${KUBECONFIG}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, añadimos la configuración del proveedor usando el secret (el fichero está en la raiz del repositorio). El archivo es: &lt;a href=&quot;https://github.com/robertorodriguez98/proyecto-integrado/blob/main/config-kubernetes.yaml&quot;&gt;config-kubernetes.yaml&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl apply -f config-kubernetes.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Script&lt;/h4&gt;
&lt;p&gt;Para facilitar la ejecución de la demo, se ha creado el script &lt;a href=&quot;https://github.com/robertorodriguez98/proyecto-integrado/blob/main/scripts/configurar-k8s.sh&quot;&gt;configurar-k8s.sh&lt;/a&gt; que aúna los pasos anteriores.&lt;/p&gt;
&lt;h4&gt;Aplicación de ArgoCD&lt;/h4&gt;
&lt;p&gt;Una vez realizados los pasos anteriores, solo queda desplegar la aplicación. Para ello se va a volver a utilizar ArgoCD; en este caso se va a utilizar la aplicación &lt;a href=&quot;https://github.com/robertorodriguez98/proyecto-integrado/blob/main/despliegue-remoto.yaml&quot;&gt;despliegue-remoto.yaml&lt;/a&gt;. Una vez desplegado, se ve así en ArgoCD&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/8RSAr3s.png&quot; alt=&quot;ArgoCD2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;para obtener la dirección en la que está la aplicación desplegada (gracias al loadbalancer) se utiliza el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl describe object loadbalancer-aplicacion-remoto
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O si queremos la dirección directamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;kubectl describe object loadbalancer-aplicacion-remoto | egrep &quot;Hostname&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En la captura el comando es &quot;k&quot; ya que tengo un alias para el comando kubectl.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/hnPYe2c.png&quot; alt=&quot;hostname&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finalmente, si accedemos a la dirección podemos visualizar la aplicación desplegada:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ak5QzBW.png&quot; alt=&quot;app&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Dificultades encontradas&lt;/h2&gt;
&lt;h3&gt;Despliegue sobre AWS&lt;/h3&gt;
&lt;p&gt;Las principales dificultades que se han encontrado a la hora de desplegar sobre AWS son las siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Falta de documentación/ejemplos&lt;/strong&gt;: Al tratarse de una tecnología relativamente nueva y con una comunidad todavía en crecimiento, no ha sido fácil encontrar ejemplos que aplicaran la api de &lt;strong&gt;AWS&lt;/strong&gt; para desplegar específicamente sobre &lt;strong&gt;EKS&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Múltiples proveedores&lt;/strong&gt;: A la hora de elegir el proveedor en el marketplace, resulta que para Amazon Web Services existen &lt;strong&gt;dos diferentes&lt;/strong&gt;, y en la documentación, dependiendo de la versión que se esté consultando, usan uno u otro, teniendo éstos &lt;strong&gt;diferentes métodos y llamadas&lt;/strong&gt;.Se ha utilizado finalmente el siguiente, debido a que hay más documentación sobre él: https://marketplace.upbound.io/providers/crossplane-contrib/provider-aws/v0.39.0&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coste&lt;/strong&gt;: El uso de EKS no se encuentra dentro de la &lt;strong&gt;capa gratuita de AWS&lt;/strong&gt;, por lo que desplegar un clúster conlleva un gasto económico (aunque no muy alto).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Despliegue sobre kubernetes&lt;/h3&gt;
&lt;p&gt;Desplegando sobre kubernetes he tenido otras problemáticas. Son las siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Falta de documentación/ejemplos&lt;/strong&gt;: Al ser una extensión de &lt;strong&gt;Crossplane&lt;/strong&gt;, los problemas relacionados con la falta de comunidad también se aplican al proveedor de kubernetes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pocas explicaciones&lt;/strong&gt;: Existe un repositorio oficial con ejemplos, pero para que estos funcionen sobre un clúster en &lt;strong&gt;AWS&lt;/strong&gt; hay que realizar &lt;strong&gt;pasos extra&lt;/strong&gt; que no explican.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caducidad&lt;/strong&gt;: Las credenciales que se utilizan para crear el &lt;strong&gt;secret&lt;/strong&gt; de kubernetes que permite la conexión, caducan a los &lt;strong&gt;30 minutos&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Con la realización del proyecto hemos podido comprender mejor la &lt;strong&gt;infraestructura como código&lt;/strong&gt;, así como la implementación de ésta por medio de una nueva tecnología que es &lt;strong&gt;Crossplane&lt;/strong&gt;, aplicando este conocimiento a desplegar un clúster de EKS en Amazon Web Services y una aplicación dentro  del mismo, haciendo uso de un &lt;strong&gt;entorno con múltiples proveedores&lt;/strong&gt;, aprovechando las funcionalidades que este ofrece. Al ser Crossplane una herramienta tan versátil y potente, hay muchas cosas que se pueden hacer que no se han llegado a tocar en el proyecto.&lt;/p&gt;
&lt;p&gt;También, y, a consecuencia del tema del proyecto, hemos aprendido acerca de la metodología &lt;strong&gt;GitOps&lt;/strong&gt; y cómo aplicarla utilizando &lt;strong&gt;ArgoCD&lt;/strong&gt;, que sirve a la perfección para este propósito gracias al funcionamiento de sus aplicaciones basadas en repositorios de GitHub.&lt;/p&gt;
&lt;p&gt;Finalmente, se ha profundizado en el funcionamiento de &lt;strong&gt;kubernetes&lt;/strong&gt;, aprendiendo acerca del plano de control  y de las diferentes APIs que gestionan los recursos.&lt;/p&gt;
&lt;p&gt;En conclusión, Crossplane es una herramienta muy &lt;strong&gt;polifacética y útil&lt;/strong&gt;, siendo su principal virtud, &lt;strong&gt;trasladar la infraestructura como código al terreno de kubernetes&lt;/strong&gt;, haciendo así que sea especialmente atractiva, y, tras un poco de aprendizaje, una herramienta a tener en cuenta de cara a &lt;strong&gt;desplegar infraestructura&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Bibliografía&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Crossplane on Amazon EKS (canal Containers from the Couch): https://www.youtube.com/live/aWRWKnniqeM?feature=share&lt;/li&gt;
&lt;li&gt;Documentación de Crossplane: https://docs.crossplane.io/v1.12/&lt;/li&gt;
&lt;li&gt;Proveedores de Crossplane: https://docs.crossplane.io/latest/concepts/providers/&lt;/li&gt;
&lt;li&gt;Documentación de la api de los diferentes proveedores:
&lt;ul&gt;
&lt;li&gt;AWS: https://doc.crds.dev/github.com/crossplane/provider-aws&lt;/li&gt;
&lt;li&gt;Helm: https://doc.crds.dev/github.com/crossplane-contrib/provider-helm&lt;/li&gt;
&lt;li&gt;K8s: https://doc.crds.dev/github.com/crossplane-contrib/provider-kubernetes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;AWS Quickstart: https://docs.crossplane.io/v1.12/getting-started/provider-aws/&lt;/li&gt;
&lt;li&gt;Production ready EKS Cluster with Crossplane: https://www.kloia.com/blog/production-ready-eks-cluster-with-crossplane&lt;/li&gt;
&lt;li&gt;GitOps model for provisioning and bootstrapping Amazon EKS clusters using Crossplane and Flux: https://aws.amazon.com/es/blogs/containers/gitops-model-for-provisioning-and-bootstrapping-amazon-eks-clusters-using-crossplane-and-flux/&lt;/li&gt;
&lt;li&gt;Ejemplos del proveedor de kubernetes: https://github.com/crossplane-contrib/provider-kubernetes&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Sistema de copias de seguridad</title><link>https://www.robertops.com/posts/2023-03-07_copias_seguridad/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-03-07_copias_seguridad/</guid><pubDate>Tue, 07 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Enunciado&lt;/h2&gt;
&lt;p&gt;Implementar un sistema de copias de seguridad para las instancias del cloud, teniendo en cuenta las siguientes características:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Selecciona una aplicación para realizar el proceso: bacula, amanda, shell script con tar, rsync, dar, afio, etc.&lt;/li&gt;
&lt;li&gt;Utiliza una de las instancias como servidor de copias de seguridad, añadiéndole un volumen y almacenando localmente las copias de seguridad que consideres adecuadas en él.&lt;/li&gt;
&lt;li&gt;El proceso debe realizarse de forma completamente automática&lt;/li&gt;
&lt;li&gt;Selecciona qué información es necesaria guardar (listado de paquetes, ficheros de configuración, documentos, datos, etc.)&lt;/li&gt;
&lt;li&gt;Realiza semanalmente una copia completa&lt;/li&gt;
&lt;li&gt;Realiza diariamente una copia incremental o diferencial (decidir cual es más adecuada)&lt;/li&gt;
&lt;li&gt;Implementa una planificación del almacenamiento de copias de seguridad para una ejecución prevista de varios años, detallando qué copias completas se almacenarán de forma permanente y cuales se irán borrando&lt;/li&gt;
&lt;li&gt;Selecciona un directorio de datos &quot;críticos&quot; que deberá almacenarse cifrado en la copia de seguridad, bien encargándote de hacer la copia manualmente o incluyendo la contraseña de cifrado en el sistema&lt;/li&gt;
&lt;li&gt;Incluye en la copia los datos de las nuevas aplicaciones que se vayan instalando durante el resto del curso&lt;/li&gt;
&lt;li&gt;Utiliza una ubicación secundaria para almacenar las copias de seguridad. Solicita acceso o la instalación de las aplicaciones que sean precisas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La corrección consistirá tanto en la restauración puntual de un fichero en cualquier fecha como la restauración completa de una de las instancias la última semana de curso.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;El escenario es el siguiente_&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://fp.josedomingo.org/sri2223/4_iaas/img/os.drawio.png&quot; alt=&quot;escenario&quot; /&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Máquina&lt;/th&gt;
&lt;th&gt;IPs&lt;/th&gt;
&lt;th&gt;tipo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;alfa&lt;/td&gt;
&lt;td&gt;172.22.200.218, 172.16.0.1, 192.168.0.1&lt;/td&gt;
&lt;td&gt;Debian&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bravo&lt;/td&gt;
&lt;td&gt;172.16.0.200&lt;/td&gt;
&lt;td&gt;Rocky Linux&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;charlie&lt;/td&gt;
&lt;td&gt;192.168.0.2&lt;/td&gt;
&lt;td&gt;Contenedor ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;192.168.0.3&lt;/td&gt;
&lt;td&gt;Contenedor ubuntu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Preparaciones previas&lt;/h3&gt;
&lt;p&gt;Y he decidido hacer las copias de seguridad desde alfa, ya que tiene conexión directa con las otras instancias. Para ello, voy a utilizar &lt;strong&gt;bacula&lt;/strong&gt;, junto con su interfaz web &lt;strong&gt;baculum&lt;/strong&gt;, tras ver las características de las distintas herramientas.&lt;/p&gt;
&lt;p&gt;Antes de la instalación, compruebo que en alfa en total se han usado en disco 4.5 GB y en bravo 1.9 GB. Como charlie y delta son contenedores, el espacio ya se ha contado en alfa. Teniendo en cuenta el espacio usado, con un disco de 30 GB debería ser suficiente para guardar las copias de seguridad, tanto incrementales como completas.&lt;/p&gt;
&lt;p&gt;Al disco le he instalado XFS por sus características de tolerancia a fallos, y lo he añadido a &lt;code&gt;/etc/fstab&lt;/code&gt; para que se monte automáticamente al iniciar el sistema.&lt;/p&gt;
&lt;p&gt;Creo la carpeta &lt;code&gt;/bacula&lt;/code&gt; en la que va a estar montado permanentemente el disco, y le cambio el propietario y los permisos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /bacula
chown -R bacula:bacula /bacula/
chmod 755 -R /bacula
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, añado la siguiente línea al fichero &lt;code&gt;/etc/fstab&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;UUID=5f086e6b-6937-460b-93ab-1a65a9e12544 /bacula xfs defaults 0 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Instalación de bacula&lt;/h2&gt;
&lt;p&gt;Primero instalo los paquetes de bacula&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install bacula bacula-common-mysql bacula-director-mysql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Durante la instalacion de &lt;code&gt;bacula-director-mysql&lt;/code&gt; pregunta lo siguiente, le doy a yes e introduzco la contraseña de la base de datos.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/R9BRtUi.png&quot; alt=&quot;bacula-director-mysql&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora instalo baculum, primero añado los repositorios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget -qO - http://www.bacula.org/downloads/baculum/baculum.pub | apt-key add -
echo &quot;deb http://www.bacula.org/downloads/baculum/stable/debian buster main
deb-src http://www.bacula.org/downloads/baculum/stable/debian buster main&quot; &amp;gt; /etc/apt/sources.list.d/baculum.list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Primero instalo los paquetes de la api&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt-get install apache2 baculum-common baculum-api baculum-api-apache2
a2enmod rewrite
a2ensite baculum-api
systemctl restart apache2

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y ahora los paquetes de la interfaz web&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get install baculum-common baculum-web baculum-web-apache2
a2enmod rewrite
a2ensite baculum-web
systemctl restart apache2
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Configuración de la api&lt;/h4&gt;
&lt;p&gt;Primero accedo a http://172.22.200.218:9096/ , introduzco el usuario y contraseña por defecto (admin/admin) y configuro la api:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PnRV17i.png&quot; alt=&quot;configuracion api&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, para permitir el acceso a la consola de bacula, edito el fichero &lt;code&gt;nano /etc/sudoers.d/baculum-api&lt;/code&gt; y añado las  siguiente líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Defaults:www-data !requiretty
www-data ALL = (root) NOPASSWD: /usr/sbin/bconsole
www-data ALL = (root) NOPASSWD: /usr/sbin/bdirjson
www-data ALL = (root) NOPASSWD: /usr/sbin/bsdjson
www-data ALL = (root) NOPASSWD: /usr/sbin/bfdjson
www-data ALL = (root) NOPASSWD: /usr/sbin/bbconsjson
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl start bacula-dir
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl stop bacula-dir
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl restart bacula-dir
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl start bacula-sd
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl stop bacula-sd
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl restart bacula-sd
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl start bacula-fd
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl stop bacula-fd
www-data ALL = (root) NOPASSWD: /usr/bin/systemctl restart bacula-fd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/H6QUnsE.png&quot; alt=&quot;sudoers&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/5bNs8rG.png&quot; alt=&quot;confuracionapi2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OygLekr.png&quot; alt=&quot;confuracionapi3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/57Vn21e.png&quot; alt=&quot;confuracionapi4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora creamos un usuario y una contraseña para la api&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dKYQjql.png&quot; alt=&quot;confuracionapi5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Configuramos, ahora si, baculum en http://172.22.200.218:9095/&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/mcdrvnm.png&quot; alt=&quot;confbaculum&quot; /&gt;&lt;/p&gt;
&lt;p&gt;y utilizamos las credenciales de la api en la configuración:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YnQYUAz.png&quot; alt=&quot;confbaculum2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fbTDdCT.png&quot; alt=&quot;confbaculum3&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Configuración del servidor en alfa&lt;/h2&gt;
&lt;h3&gt;Selecciona qué información es necesaria guardar&lt;/h3&gt;
&lt;p&gt;En todas las instancias voy a guardar el contenido de /home, /etc, /var, /opt, /usr/share (menos los archivos temporales de var). Además de esto, en diferentes instancias voy a guardar lo siguiente:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Instancia&lt;/th&gt;
&lt;th&gt;Servicios&lt;/th&gt;
&lt;th&gt;Localización&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;alfa&lt;/td&gt;
&lt;td&gt;bacula&lt;/td&gt;
&lt;td&gt;/var/log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bravo&lt;/td&gt;
&lt;td&gt;httpd&lt;/td&gt;
&lt;td&gt;/var/www, /etc/httpd&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;charlie&lt;/td&gt;
&lt;td&gt;dns&lt;/td&gt;
&lt;td&gt;/var/chache/bind, /etc/bind&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;delta&lt;/td&gt;
&lt;td&gt;correo&lt;/td&gt;
&lt;td&gt;/etc/postfix&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Cada día voy a hacer copias incrementales, cada semana se realizará una completa, al igual que cada mes.&lt;/p&gt;
&lt;p&gt;Teniendo en cuenta esto, voy a configurar alfa utilizando los ficheros de configuración (por facilidad respecto a la interfaz web). Primero edito el fichero &lt;code&gt;/etc/bacula/bacula-dir.conf&lt;/code&gt;, cambiando su contenido por el siguiente (es un fichero en mi github debido a la longitud del mismo):&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/robertorodriguez98/bacula/blob/main/alfa/bacula-dir.conf&quot;&gt;bacula-dir.conf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;compruebo que no hay errores en el fichero de configuración:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bacula-dir -t -c /etc/bacula/bacula-dir.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y tras eso, modifico el fichero &lt;code&gt;/etc/bacula/bacula-sd.conf&lt;/code&gt;,que contiene la configuración referente a los dispositivos de almacenamiento, cambiando su contenido por el siguiente (es un fichero en mi github debido a la longitud del mismo):&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/robertorodriguez98/bacula/blob/main/alfa/bacula-sd.conf&quot;&gt;bacula-sd.conf&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Al igual que antes, compruebo que no hay errores en el fichero de configuración:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bacula-sd -t -c /etc/bacula/bacula-sd.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y reinicio los servicios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart bacula-sd.service
systemctl enable bacula-sd.service
systemctl restart bacula-director.service
systemctl enable bacula-director.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Preparación de los clientes&lt;/h2&gt;
&lt;h3&gt;Alfa&lt;/h3&gt;
&lt;p&gt;Alfa va a ser a la vez cliente y servidor, por lo que voy a instalar el cliente también en alfa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install bacula-client
systemctl enable bacula-fd.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora modifico el fichero &lt;code&gt;/etc/bacula/bacula-fd.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Director {
  Name = alfa-dir
  Password = &quot;bacula&quot;
}

Director {
  Name = alfa-mon
  Password = &quot;bacula&quot;
  Monitor = yes
}

FileDaemon {                          # this is me
  Name = alfa-fd
  FDport = 9102                  # where we listen for the director
  WorkingDirectory = /var/lib/bacula
  Pid Directory = /run/bacula
  Maximum Concurrent Jobs = 20
  Plugin Directory = /usr/lib/bacula
  FDAddress = 10.0.0.247
}

# Send all messages except skipped files back to Director
Messages {
  Name = Standard
  director = alfa-dir = all, !skipped, !restored
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y reinicio el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart bacula-fd.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Bravo&lt;/h3&gt;
&lt;p&gt;Instalo el cliente&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dnf install bacula-client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y edito el fichero &lt;code&gt;/etc/bacula/bacula-fd.conf&lt;/code&gt; y añado las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Director {
  Name = alfa-dir
  Password = &quot;bacula&quot;
}

Director {
  Name = alfa-mon
  Password = &quot;bacula&quot;
  Monitor = yes
}

FileDaemon {
  Name = bravo-fd
  FDport = 9102
  WorkingDirectory = /var/lib/bacula
  Pid Directory = /run/bacula
  Maximum Concurrent Jobs = 20
  Plugin Directory = /usr/lib/bacula
  FDAddress = 172.16.0.200
}

Messages {
  Name = Standard
  director = alfa-dir = all, !skipped, !restored
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;firewall-cmd --permanent --add-port=22/tcp
firewall-cmd --permanent --add-port=80/tcp

firewall-cmd --permanent --add-port=9101/tcp
firewall-cmd --permanent --add-port=9102/tcp
firewall-cmd --permanent --add-port=9103/tcp
firewall-cmd --reload
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Charlie y delta&lt;/h3&gt;
&lt;p&gt;La configuración en charlie y delta es similar, por eso la pongo junta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install bacula-client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y edito el fichero &lt;code&gt;/etc/bacula/bacula-fd.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Director {
  Name = alfa-dir
  Password = &quot;bacula&quot;
}

Director {
  Name = alfa-mon
  Password = &quot;bacula&quot;
  Monitor = yes
}

FileDaemon {                          # this is me
  Name = delta-fd
  FDport = 9102                  # where we listen for the director
  WorkingDirectory = /var/lib/bacula
  Pid Directory = /run/bacula
  Maximum Concurrent Jobs = 20
  Plugin Directory = /usr/lib/bacula
  FDAddress = 192.168.0.3
}

# Send all messages except skipped files back to Director
Messages {
  Name = Standard
  director = alfa-dir = all, !skipped, !restored
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y reinicio el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart bacula-fd.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, con todos los cliente configurados, reinicio los servicios de bacula en alfa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart bacula-fd.service
systemctl restart bacula-sd.service
systemctl restart bacula-director.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y hago una prueba de conexión usando la consola de bácula y la interfaz web:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bconsole
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PCa4pzo.png&quot; alt=&quot;bconsole&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/EWP7PH0.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Nodos de almacenamiento&lt;/h2&gt;
&lt;p&gt;Ahora voy a crear los nodos de almacenamiento con bconsole&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bconsole
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pkpErri.png&quot; alt=&quot;bconsole&quot; /&gt;&lt;/p&gt;
&lt;p&gt;También se puede hacer desde la interfaz web:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DKz9aUK.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Realizo la misma configuración para vol-semanal y vol-mensual.&lt;/p&gt;
&lt;p&gt;Podemos ver que se han creado los volúmenes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/WOVsDgp.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Restauración&lt;/h2&gt;
&lt;p&gt;Voy a realizar la restauración por medio de la interfaz web. Para ello utilizo la opción de Perform restore y sigo los siguientes pasos:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/WpBBp3u.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/g4kd0Ri.png&quot; alt=&quot;web2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/SYrgbek.png&quot; alt=&quot;web3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/0Z7Z7Zg.png&quot; alt=&quot;web4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Selecciono qué quiero copiar y donde:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rgz94GM.png&quot; alt=&quot;web5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DlrJT2c.png&quot; alt=&quot;web6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;y que reemplace los ficheros más antiguos:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Y38EHgZ.png&quot; alt=&quot;web7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cuando acabe puedo ver que se ha ejecutado con éxito:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/6Gok1iW.png&quot; alt=&quot;web8&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Cortafuegos III: perimetral sobre escenario</title><link>https://www.robertops.com/posts/2023-03-02_cortafuegos3/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-03-02_cortafuegos3/</guid><pubDate>Thu, 02 Mar 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;El escenario es el siguiente&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LLToqTl.png&quot; alt=&quot;Escenario&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y las interfaces de alfa son las siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ens3: Salida a internet&lt;/li&gt;
&lt;li&gt;ens8: DMZ&lt;/li&gt;
&lt;li&gt;br-intra: LAN&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Enunciado&lt;/h2&gt;
&lt;p&gt;Sobre el escenario creado en el módulo de servicios con las máquinas Alfa (Router), Bravo (DMZ), Charlie y Delta (LAN) y empleando iptables o nftables, configura un cortafuegos perimetral en la máquina Alfa de forma que el escenario siga funcionando completamente teniendo en cuenta los siguientes puntos:&lt;/p&gt;
&lt;h2&gt;Política por defecto DROP para las cadenas INPUT, FORWARD y OUTPUT.&lt;/h2&gt;
&lt;p&gt;Antes de añadir la política por defecto voy a añadir reglas para permitir el acceso por ssh&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.22.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -d 172.22.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Estas reglas sirve para acceder desde la misma red, además añado las siguientes para acceder a través de la VPN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.29.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -d 172.29.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y permito el acceso al resto de máquinas&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -d 192.168.0.0/24 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -s 192.168.0.0/24 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

iptables -A OUTPUT -d 172.16.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -s 172.16.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Este paso lo ejecuto después de crear las reglas ssh)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;El cortafuego debe cumplir al menos estas reglas:&lt;/h2&gt;
&lt;h3&gt;La máquina Alfa tiene un servidor ssh escuchando por el puerto 22, pero al acceder desde el exterior habrá que conectar al puerto 2222.&lt;/h3&gt;
&lt;h3&gt;Desde Delta y Bravo se debe permitir la conexión ssh por el puerto 22 a la máquina Alfa&lt;/h3&gt;
&lt;p&gt;de alfa a charlie/delta&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo iptables -A OUTPUT -o br-intra -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -i br-intra -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba antes y después de crear la regla&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/oiiVuK8.png&quot; alt=&quot;Escenario&quot; /&gt;&lt;/p&gt;
&lt;p&gt;de alfa a bravo&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo iptables -A OUTPUT -o ens8 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -i ens8 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/2rAyLZy.png&quot; alt=&quot;Escenario&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;La máquina Alfa debe tener permitido el tráfico para la interfaz loopback&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i lo -p icmp -j ACCEPT
iptables -A OUTPUT -o lo -p icmp -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo que funciona, antes y después de aplicar la regla:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7RfdzSj.png&quot; alt=&quot;loopback&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;A la máquina Alfa se le puede hacer ping desde la DMZ, pero desde la LAN se le debe rechazar la conexión (REJECT) y desde el exterior se rechazará de manera silenciosa.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.16.0.200/16 -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A OUTPUT -d 172.16.0.200/16 -p icmp -m icmp --icmp-type echo-reply -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora el reject a la LAN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 192.168.1.0/24 -p icmp -m icmp --icmp-type echo-request -j REJECT
iptables -A OUTPUT -d 192.168.1.0/24 -p icmp -m icmp --icmp-type echo-reply -j REJECT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ping desde bravo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/utA5CGz.png&quot; alt=&quot;ping desde bravo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ping desde charlie:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/g6Z7xSD.png&quot; alt=&quot;ping desde charlie&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;La máquina Alfa puede hacer ping a la LAN, la DMZ y al exterior.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type echo-reply -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A la LAN:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PrsN5x0.png&quot; alt=&quot;ping a la LAN&quot; /&gt;&lt;/p&gt;
&lt;p&gt;A la DMZ:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/PealPbe.png&quot; alt=&quot;ping a la DMZ&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Al exterior:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/e5iIex8.png&quot; alt=&quot;ping al exterior&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Desde la máquina Bravo se puede hacer ping y conexión ssh a las máquinas de la LAN.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A FORWARD -s 172.16.0.200/32 -d 192.168.0.0/24 -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A FORWARD -d 172.16.0.200/32 -s 192.168.0.0/24 -p icmp -m icmp --icmp-type echo-reply -j ACCEPT

iptables -A FORWARD -s 172.16.0.200/32 -d 192.168.0.0/24 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -d 172.16.0.200/32 -s 192.168.0.0/24 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba de ping desde bravo a charlie:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qLycsbI.png&quot; alt=&quot;ping desde bravo a charlie&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Prueba de ssh desde bravo a charlie:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XZF4qGQ.png&quot; alt=&quot;ssh desde bravo a charlie&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Desde cualquier máquina de la LAN se puede conectar por ssh a la máquina Bravo.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A FORWARD -s 192.168.0.0/24 -d 172.16.0.200/32 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 172.16.0.200/32 -d 192.168.0.0/24 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo a acceder desde charlie:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/2rykuVx.png&quot; alt=&quot;ssh desde charlie a bravo&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configura la máquina Alfa para que las máquinas de LAN y DMZ puedan acceder al exterior&lt;/h3&gt;
&lt;p&gt;DMZ a exterior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -t nat -A POSTROUTING -s 172.16.0.0/16 -o ens3 -j MASQUERADE
iptables -A FORWARD -i ens8 -o ens3 -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A FORWARD -i ens3 -o ens8 -p icmp -m icmp --icmp-type echo-reply -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;LAN a exterior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -o ens3 -j MASQUERADE
iptables -A FORWARD -i br-intra -o ens3 -p icmp -m icmp --icmp-type echo-request -j ACCEPT
iptables -A FORWARD -i ens3 -o br-intra -p icmp -m icmp --icmp-type echo-reply -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba de ping desde bravo a google:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/IbX1h7r.png&quot; alt=&quot;ping desde bravo a google&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Prueba de ping desde charlie a google:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/g7GKV9E.png&quot; alt=&quot;ping desde charlie a google&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Las máquinas de la LAN pueden hacer ping al exterior y navegar&lt;/h3&gt;
&lt;p&gt;El ping lo he configurado en el punto anterior. Ahora la navegación:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A FORWARD -i br-intra -o ens3 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens3 -o br-intra -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT

iptables -A FORWARD -i br-intra -o ens3 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens3 -o br-intra -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba de navegación desde charlie (no tengo el comando curl, asi que uso nc):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ZLljVQQ.png&quot; alt=&quot;navegación desde charlie&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;La máquina Bravo puede navegar&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A FORWARD -i ens8 -o ens3 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens3 -o ens8 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT

iptables -A FORWARD -i ens8 -o ens3 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens3 -o ens8 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba de navegación desde bravo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qXEvqkP.png&quot; alt=&quot;navegación desde bravo&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configura la máquina Alfa para que los servicios web y ftp sean accesibles desde el exterior&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -t nat -A PREROUTING -p tcp -i ens3 --dport 80 -j DNAT --to 172.16.0.200
iptables -A FORWARD -i ens3 -o ens8 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens8 -o ens3 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT

iptables -t nat -A PREROUTING -p tcp -i ens3 --dport 21 -j DNAT --to 172.16.0.200
iptables -A FORWARD -i ens3 -o ens8 -p tcp --dport 21 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens8 -o ens3 -p tcp --sport 21 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba de navegación desde el exterior (La página es de un taller de Django):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/U2EgQqy.png&quot; alt=&quot;navegación desde el exterior&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;El servidor web y el servidor ftp deben ser accesibles desde la LAN y desde el exterior&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A FORWARD -i br-intra -o ens8 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens8 -o br-intra -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT

iptables -A FORWARD -i br-intra -o ens8 -p tcp --dport 21 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens8 -o br-intra -p tcp --sport 21 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prueba de navegación desde charlie, sale un error porque estoy usando el comando nc, pero aun asi se ve que responde rocky con wsgi, que es lo que hay desplegado en bravo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/J9C1mcC.png&quot; alt=&quot;navegación desde charlie&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;El servidor de correos sólo debe ser accesible desde la LAN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -A FORWARD -i br-intra -o ens8 -p tcp --dport 25 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i ens8 -o br-intra -p tcp --sport 25 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;En la máquina Charlie instala un servidor mysql si no lo tiene aún. A este servidor se puede acceder desde la DMZ, pero no desde el exterior.&lt;/h3&gt;
&lt;h3&gt;Evita ataques DoS por ICMP Flood, limitando el número de peticiones por segundo desde una misma IP.&lt;/h3&gt;
&lt;p&gt;El ping está bloqueado desde el exterior y desde la LAN, por lo que voy a evitar los ataques desde la DMZ, limitando a 1 peticion por segundo(primero hay que borrar la regla anterior):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i ens8 -p icmp -m state --state NEW --icmp-type echo-request -m limit --limit 1/s --limit-burst 1 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/L2N9GIc.png&quot; alt=&quot;ping desde la DMZ&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En la captura se ve como el 100% de los paquetes han sido rechazados&lt;/p&gt;
&lt;h3&gt;Evita ataques DoS por SYN Flood.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -t raw -D PREROUTING -p tcp -m tcp --syn -j CT --notrack
iptables -D INPUT -p tcp -m tcp -m conntrack --ctstate INVALID,UNTRACKED -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460
iptables -D INPUT -m conntrack --ctstate INVALID -j DROP 
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Evita que realicen escaneos de puertos a Alfa.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;iptables -N antiscan
iptables -A antiscan -j DROP
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Debemos implementar que el cortafuegos funcione después de un reinicio de la máquina.&lt;/h2&gt;
&lt;p&gt;Para hacer la instalación persistente, he utilizado el paquete iptables-persistent&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install iptables-persistent
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para que funcione, tras crear todas las reglas, ejecuto el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo iptables-save &amp;gt; /etc/iptables/rules.v4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras ejecutar eso, las reglas son persistentes. El fichero tiene el siguiente contenido:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/KbSN5IX.png&quot; alt=&quot;iptables-save&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Poblar un directorio LDAP desde un fichero CSV</title><link>https://www.robertops.com/posts/2023-02-22_ldap_csv/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-02-22_ldap_csv/</guid><pubDate>Wed, 22 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;En alfa creo el siguiente fichero CSV:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Belen,Nazareth,belennazareth@gmail.com,nazareth,ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC73j7AidXdLgiu5wJw7YgJuvOHyb6U8c04MuQyehYnMknMR8mTnWZr20npVHJ8VHYHDy8RlgbkMMBFgeVCgXJ+Im3A6Efp6HC4yj2SM+73hr1EKCLdRPzCzdtDSUtkqU9k+x2RdF3T6qD6H4Cg/nT8Sg3Qenqds4XORfDWOvntxFja2D0OhZv1MLPUD9pEj+a8D4erfiPx/gKW/Rtu89une+uiwVgK60B5CxnC8XXnXmPO3NhrgyQhVgzQZ658cUbLooxQURVlo1gnOmcqX5h+svUKN1SDbzTyy7HKSk7bbLHEhk7qDh7jSzcf80GLU0li8vXc2to8NpC00EOQ9POPivESz23gMNY8ooDtNU3Ll/xYvhtvXrJNTbuBiuVLzuopMvrQi6LVsQEWmPJzBiJ2qt8JW1KRLcnWRL4AezbxAPXuRYVnYBS3it6L0J4AZjZg63BkIIrfU7GYzrKb+z5mqUgDJhIZ4d5av+OAxPSSzNeVnyWEnWrI0k9kf9qmqhU= nazare@ThousandSunny 
Antonio,Marchan,antoniomarchan@gmail.com,anthony,ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC9kx67eVF+Eh5epqZvQfjOLamYck9lVB9w1e/JO9Nzx7Utxm0ikD4ZCuhANc42RRkbH/2xICo138IpxJtfnbf4ObmaxVpo65lweLw5e8w132v6IXHkCAU5jTdyJS08Gi5I/nEqq/ywEMWJ939zEu0Dfdi/1IDlqBjGPpeomCJCDOkZWHfFyuiHrpgbWniwjRBcQiXjlp7w76/k0K+1Xw5JQz2F/YuTHzEGXqVo7x2T32IprzONXprVd9cn5qItFES5DMHOv62t++3zAOZbLNTs+cFuZmfTkT4YQUoHPkqXCzQmM6hnSAihOQjNKZWy+zSOiTkL22tdd9q4jhUZu4VutNeEFKt4VDlLH1uEV0FCJjb5Fw2gzb62D9sjdsjNa8EZD2IcrjKrIJv83Ca7AyeED5egoFSAMfW1iFIse8pG4olOOv4FyeWtSgjgv9A+URiwkRqFd371lw6Xj0gdEokqO9sBYlcy3gfwT4BZCSlM3AMOjFCMP5kU1Z8uTowg9NM= antonio@debian
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creo el siguiente fichero ldif &lt;code&gt;openssh-lpk.ldif&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dn: cn=openssh-lpk,cn=schema,cn=config
objectClass: olcSchemaConfig
cn: openssh-lpk
olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME &apos;sshPublicKey&apos;
  DESC &apos;MANDATORY: OpenSSH Public key&apos;
  EQUALITY octetStringMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME &apos;ldapPublicKey&apos; SUP top AUXILIARY
  DESC &apos;MANDATORY: OpenSSH LPK objectclass&apos;
  MAY ( sshPublicKey $ uid )
  )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, creo un entorno virtual:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install python3-venv -y
python3 -m venv entorno_ldap
source entorno_ldap/bin/activate
pip install ldap3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y el siguiente programa &lt;code&gt;ldap_csv.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python

import ldap3
from ldap3 import Connection, ALL
from getpass import getpass
from sys import exit

### VARIABLES

# Shell que se le asigna a los usuarios
shell = &apos;/bin/bash&apos;

# Ruta absoluta del directorio que contiene los directorios personales de los usuarios. Terminado en &quot;/&quot;
home_dir = &apos;/home/ldap/&apos;

# El valor inicial para los UID que se asignan al insertar usuarios. 
uid_number = 500

# El GID que se le asigna a los usuarios. Si no se manda al anadir el usuario da error.
gid = 500

### VARIABLES

# Leemos el fichero .csv de los usuarios y guardamos cada linea en una lista.
with open(&apos;usuarios.csv&apos;, &apos;r&apos;) as usuarios:
  usuarios = usuarios.readlines()


### Parametros para la conexion
ldap_ip = &apos;ldap://alfa.roberto.gonzalonazareno.org:389&apos;
dominio_base = &apos;dc=roberto,dc=gonzalonazareno,dc=org&apos;
user_admin = &apos;admin&apos; 
contrasena = getpass(&apos;Contrasena: &apos;)

# Intenta realizar la conexion.
conn = Connection(ldap_ip, &apos;cn={},{}&apos;.format(user_admin, dominio_base),contrasena)

# conn.bind() devuelve &quot;True&quot; si se ha establecido la conexion y &quot;False&quot; en caso contrario.

# Si no se establece la conexion imprime por pantalla un error de conexion.
if not conn.bind():
  print(&apos;No se ha podido conectar con ldap&apos;) 
  if conn.result[&apos;description&apos;] == &apos;invalidCredentials&apos;:
    print(&apos;Credenciales no validas.&apos;)
  # Termina el script.
  exit(0)

# Recorre la lista de usuarios
for user in usuarios:
  # Separa los valores del usuario usando como delimitador &quot;,&quot;, y asigna cada valor a la variable correspondiente.
  user = user.split(&apos;,&apos;)
  cn = user[0]
  sn = user[1]
  mail = user[2]
  uid = user[3]
  ssh = user[4]

  #Anade el usuario.
  conn.add(
    &apos;uid={},ou=Personas,{}&apos;.format(uid, dominio_base),
    object_class = 
      [
      &apos;inetOrgPerson&apos;,
      &apos;posixAccount&apos;, 
      &apos;ldapPublicKey&apos;
      ],
    attributes =
      {
      &apos;cn&apos;: cn,
      &apos;sn&apos;: sn,
      &apos;mail&apos;: mail,
      &apos;uid&apos;: uid,
      &apos;uidNumber&apos;: str(uid_number),
      &apos;gidNumber&apos;: str(gid),
      &apos;homeDirectory&apos;: &apos;{}{}&apos;.format(home_dir,uid),
      &apos;loginShell&apos;: shell,
      &apos;sshPublicKey&apos;: str(ssh)
      })

  if conn.result[&apos;description&apos;] == &apos;entryAlreadyExists&apos;:
    print(&apos;El usuario {} ya existe.&apos;.format(uid))

  # Aumenta el contador para asignar un UID diferente a cada usuario (cada vez que ejecutemos el script debemos asegurarnos de ante mano que no existe dicho uid en el directorio ldap, o se solaparian los datos)
  uid_number += 1

#Cierra la conexion.
conn.unbind()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Los ejecuto&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 ldap_csv.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso, podemos realizar un &lt;code&gt;ldapsearch&lt;/code&gt; para comprobar que se han añadido los usuarios correctamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapsearch -x -D &quot;cn=admin,dc=roberto,dc=gonzalonazareno,dc=org&quot; -b &quot;dc=roberto,dc=gonzalonazareno,dc=org&quot; -W
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/BZDGSdR.png&quot; alt=&quot;image-2021033016442&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Editamos el fichero &lt;code&gt;/etc/ldap/ldap.conf&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BASE dc=roberto,dc=gonzalonazareno,dc=org
URI ldap://roberto.antonio.gonzalonazareno.org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;en el fichero &lt;code&gt;/etc/pam.d/common-session&lt;/code&gt; añadimos la siguiente linea al final:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;session    required        pam_mkhomedir.so
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creo el siguiente script para encontrar las claves públicas del árbol de LDAP en &lt;code&gt;/opt/buscarclave.sh&lt;/code&gt; y le damos permiso de ejecución:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

ldapsearch -x -u -LLL -o ldif-wrap=no &apos;(&amp;amp;(objectClass=posixAccount)(uid=&apos;&quot;$1&quot;&apos;))&apos; &apos;sshPublicKey&apos; | sed -n &apos;s/^[ \t]*sshPublicKey::[ \t]*\(.*\)/\1/p&apos; | base64 -d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y le pongo permisos 755:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chmod 755 /opt/buscarclave.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora compruebo que funciona:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/zz3brvp.png&quot; alt=&quot;clave&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ahora edito el fichero &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; y reinicio el servicio sshd:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AuthorizedKeysCommand /opt/buscarclave.sh
AuthorizedKeysCommandUser nobody
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora tras eso, Antonio puede conectarse con su usuario:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fZ4nDBJ.jpg&quot; alt=&quot;image-2021033016593&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Administración de Bases de Datos - Auditoria</title><link>https://www.robertops.com/posts/2023-02-21_auditoria/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-02-21_auditoria/</guid><pubDate>Tue, 21 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Activa desde SQL*Plus la auditoría de los intentos de acceso exitosos al sistema. Comprueba su funcionamiento&lt;/h2&gt;
&lt;p&gt;Para activar la auditoría y que los datos se almacenen en la base de datos, ejecutamos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER SYSTEM SET audit_trail=db scope=spfile;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para comprobar que se ha activado correctamente, ejecutamos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT name, value FROM v$parameter WHERE name like &apos;audit_trail&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/juhQKXU.png&quot; alt=&quot;audit_trail&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para activar la auditoría de los intentos de acceso exitosos al sistema, ejecutamos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AUDIT CREATE SESSION WHENEVER SUCCESSFUL;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, tras acceder a la base de datos con el usuario restaurante (de una práctica anterior), haciendo la siguiente consulta, puedo ver que se ha almacenado la información del acceso:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT OS_USERNAME, USERNAME, EXTENDED_TIMESTAMP, ACTION_NAME FROM DBA_AUDIT_SESSION;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Y0qJUmC.png&quot; alt=&quot;auditoria&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Realiza un procedimiento en PL/SQL que te muestre los accesos fallidos junto con el motivo de los mismos, transformando el código de error almacenado en un mensaje de texto comprensible. Contempla todos los motivos posibles para que un acceso sea fallido&lt;/h2&gt;
&lt;p&gt;Primero, para vaciar la tabla de auditoría, ejecuto el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TRUNCATE table sys.AUD$;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creo una sesión de auditoría para los accesos fallidos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AUDIT CREATE SESSION WHENEVER NOT SUCCESSFUL;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;He obtenido el significado de los códigos de error de la siguiente página: &lt;a href=&quot;http://johanlouwers.blogspot.com/2013/01/oracle-database-login-audit.html&quot;&gt;http://johanlouwers.blogspot.com/2013/01/oracle-database-login-audit.html&lt;/a&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Código&lt;/th&gt;
&lt;th&gt;Significado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;00911&lt;/td&gt;
&lt;td&gt;El nombre de usuario o la contraseña contiene un carácter no válido&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;00988&lt;/td&gt;
&lt;td&gt;Falta la contraseña o no es válida&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01004&lt;/td&gt;
&lt;td&gt;Inicio de sesión denegado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01005&lt;/td&gt;
&lt;td&gt;Contraseña nula&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01017&lt;/td&gt;
&lt;td&gt;Contraseña / usuario no válidos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01031&lt;/td&gt;
&lt;td&gt;Sin privilegios&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01045&lt;/td&gt;
&lt;td&gt;El usuario no tiene el privilegio CREATE SESSION&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01918&lt;/td&gt;
&lt;td&gt;No existe el user ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;01920&lt;/td&gt;
&lt;td&gt;No existe el rol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09911&lt;/td&gt;
&lt;td&gt;Contraseña incorrecta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28000&lt;/td&gt;
&lt;td&gt;La cuenta está bloqueada&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28001&lt;/td&gt;
&lt;td&gt;La contraseña ha caducado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28002&lt;/td&gt;
&lt;td&gt;La contraseña caducará pronto, se debe cambiar ahora&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28003&lt;/td&gt;
&lt;td&gt;La contraseña no es lo suficientemente compleja&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28007&lt;/td&gt;
&lt;td&gt;La contraseña no se puede reutilizar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28008&lt;/td&gt;
&lt;td&gt;Contraseña antigua no válida&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28009&lt;/td&gt;
&lt;td&gt;La conexión a sys se debe realizar desde sysdba o sysoper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28011&lt;/td&gt;
&lt;td&gt;La cuenta va a caducar pronto, se debe cambiar la contraseña&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;28221&lt;/td&gt;
&lt;td&gt;La contraseña original no ha sido suministrada&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Ahora, creo el procedimiento en PL/SQL:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE accesosFallidos
IS
    CURSOR c_accesos IS
        SELECT USERNAME, EXTENDED_TIMESTAMP, ACTION_NAME, RETURNCODE
        FROM DBA_AUDIT_SESSION
        WHERE RETURNCODE &amp;lt;&amp;gt; 0;
begin
    for i in c_accesos loop
        dbms_output.put_line(&apos;HORA: &apos; || i.EXTENDED_TIMESTAMP);
        dbms_output.put_line(CHR(9)||&apos;-USUARIO: &apos; || i.USERNAME);
        case i.RETURNCODE
            when 00911 then
                dbms_output.put_line(CHR(9)||&apos;-El nombre de usuario o la contrasena contiene un caracter no valido&apos;);
            when 00988 then
                dbms_output.put_line(CHR(9)||&apos;-Falta la contrasena o no es valida&apos;);
            when 01004 then
                dbms_output.put_line(CHR(9)||&apos;-Inicio de sesion denegado&apos;);
            when 01005 then
                dbms_output.put_line(CHR(9)||&apos;-contrasena nula&apos;);
            when 01017 then
                dbms_output.put_line(CHR(9)||&apos;-contrasena / usuario no validos&apos;);
            when 01031 then
                dbms_output.put_line(CHR(9)||&apos;-Sin privilegios&apos;);
            when 01045 then
                dbms_output.put_line(CHR(9)||&apos;-El usuario no tiene el privilegio CREATE SESSION&apos;);
            when 01918 then
                dbms_output.put_line(CHR(9)||&apos;-No existe el user ID&apos;);
            when 01920 then
                dbms_output.put_line(CHR(9)||&apos;-No existe el rol&apos;);
            when 09911 then
                dbms_output.put_line(CHR(9)||&apos;-contrasena incorrecta&apos;);
            when 28000 then
                dbms_output.put_line(CHR(9)||&apos;-La cuenta esta bloqueada&apos;);
            when 28001 then
                dbms_output.put_line(CHR(9)||&apos;-La contrasena ha caducado&apos;);
            when 28002 then
                dbms_output.put_line(CHR(9)||&apos;-La contrasena caducara pronto, se debe cambiar ahora&apos;);
            when 28003 then
                dbms_output.put_line(CHR(9)||&apos;-La contrasena no es lo suficientemente compleja&apos;);
            when 28007 then
                dbms_output.put_line(CHR(9)||&apos;-La contrasena no se puede reutilizar&apos;);
            when 28008 then
                dbms_output.put_line(CHR(9)||&apos;-contrasena antigua no valida&apos;);
            when 28009 then
                dbms_output.put_line(CHR(9)||&apos;-La conexión a sys se debe realizar desde sysdba o sysoper&apos;);
            when 28011 then
                dbms_output.put_line(CHR(9)||&apos;-La cuenta va a caducar pronto, se debe cambiar la contrasena&apos;);
            when 28221 then
                dbms_output.put_line(CHR(9)||&apos;-La contrasena original no ha sido suministrada&apos;);
        end case;
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compruebo que funciona correctamente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FGHbSQV.png&quot; alt=&quot;accesosFallidos&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Activa la auditoría de las operaciones DML realizadas por SCOTT. Comprueba su funcionamiento&lt;/h2&gt;
&lt;p&gt;Activo la auditoría de las operaciones DML realizadas por SCOTT:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AUDIT INSERT TABLE, UPDATE TABLE, DELETE TABLE BY SCOTT BY ACCESS;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora inserto un empleado en la tabla emp:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO emp VALUES (9999, &apos;Roberto&apos;, &apos;Director&apos;, 7839, TO_DATE(&apos;21/02/2023&apos;, &apos;DD/MM/YYYY&apos;), 5000, 0, 10);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y se ve reflectado en la tabla de auditoría:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT USERNAME, OBJ_NAME, ACTION_NAME, EXTENDED_TIMESTAMP FROM DBA_AUDIT_OBJECT;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/33VAXWP.png&quot; alt=&quot;auditoriaDML&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Realiza una auditoría de grano fino para almacenar información sobre la inserción de empleados con sueldo superior a 2000 en la tabla emp de scott&lt;/h2&gt;
&lt;p&gt;La auditoría de grano fino (FGA) es como una versión extendida de la auditoría estándar. Registra los cambios en datos muy concretos a nivel de columna.&lt;/p&gt;
&lt;p&gt;Para realizar la auditoría de grano fino, primero tengo que crear una política de auditoría:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BEGIN
    DBMS_FGA.ADD_POLICY (
    OBJECT_SCHEMA      =&amp;gt; &apos;SCOTT&apos;,
    OBJECT_NAME        =&amp;gt; &apos;EMP&apos;,
    POLICY_NAME        =&amp;gt; &apos;SALARIO_MAYOR_2000&apos;,
    AUDIT_CONDITION    =&amp;gt; &apos;SAL &amp;lt; 2000&apos;,
    STATEMENT_TYPES    =&amp;gt; &apos;INSERT&apos;
    );
END;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora inserto varios empleados:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INSERT INTO emp VALUES (2222, &apos;Bill Gates&apos;, &apos;Director&apos;, 7839, TO_DATE(&apos;21/02/2023&apos;, &apos;DD/MM/YYYY&apos;), 1000, 0, 10);
INSERT INTO emp VALUES (3333, &apos;Steve Jobs&apos;, &apos;Director&apos;, 7839, TO_DATE(&apos;21/02/2023&apos;, &apos;DD/MM/YYYY&apos;), 5000, 0, 10);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rqLZNLq.png&quot; alt=&quot;auditoriaGranoFino&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y compruebo que aparece en la tabla de auditoría:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT DB_USER, OBJECT_NAME, SQL_TEXT, EXTENDED_TIMESTAMP FROM DBA_FGA_AUDIT_TRAIL WHERE POLICY_NAME=&apos;SALARIO_MAYOR_2000&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/EseLG8U.png&quot; alt=&quot;auditoriaGranoFino&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Explica la diferencia entre auditar una operación by access o by session ilustrándolo con ejemplos&lt;/h2&gt;
&lt;p&gt;Las operaciones by access se auditan por cada acceso a la base de datos, mientras que las operaciones by session se auditan por cada sesión de usuario. Por ejemplo, si un usuario realiza 10 accesos a la base de datos, se auditarán 10 operaciones by access, mientras que si realiza 10 accesos en una misma sesión, se auditarán 1 operación by session.&lt;/p&gt;
&lt;p&gt;La sintaxis es la siguiente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AUDIT [operación] [tabla] BY [usuario] BY {ACCESS | SESSION};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Documenta las diferencias entre los valores db y db, extended del parámetro audit_trail de ORACLE. Demuéstralas poniendo un ejemplo de la información sobre una operación concreta recopilada con cada uno de ellos&lt;/h2&gt;
&lt;p&gt;Ambos parámetros indican que la auditoría está activada. La diferencia es que el parámetro db guarda la información en la tabla de auditoría, mientras que el parámetro db, extended guarda la información en la tabla de auditoría y en el archivo de alerta.&lt;/p&gt;
&lt;p&gt;Para cambiar el parámetro, utilizo el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALTER SYSTEM SET audit_trail = db, extended SCOPE = SPFILE;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reinicio la base de datos y compruebo que el parámetro se ha cambiado correctamente, con la consulta del ejercicio 1:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/sIytDAc.png&quot; alt=&quot;auditTrail&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Averigua si en Postgres se pueden realizar los cuatro primeros apartados. Si es así, documenta el proceso adecuadamente&lt;/h2&gt;
&lt;h3&gt;Ejercicio 1&lt;/h3&gt;
&lt;p&gt;En postgres no se puede realizar el ejercicio 1, ya que se registran los inicios de sesión fallidos en el archivo de log, pero no los exitosos.&lt;/p&gt;
&lt;h3&gt;Ejercicio 2&lt;/h3&gt;
&lt;p&gt;No se puede realizar un procedimiento ya que los accesos fallidos no está registrado en la base de datos, sino en el archivo de log, sin embargo, en el propio archivo de log, se encuentran explicados con palabras y en español, el objetivo final del procedimiento:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/n9IuvtS.png&quot; alt=&quot;accesosFallidos&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Ejercicio 3&lt;/h3&gt;
&lt;p&gt;Para realizar la auditoría voy a usar &lt;strong&gt;Trigger 91plus&lt;/strong&gt;, una herramienta creada por la comunidad que permite auditar las operaciones DML en postgres.&lt;/p&gt;
&lt;p&gt;Para instalarla, primero tengo que descargar de github el siguiente fichero&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://raw.githubusercontent.com/2ndQuadrant/audit-trigger/master/audit.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y luego lo instalo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\i audit.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/yYJq9ye.png&quot; alt=&quot;instalacionTrigger&quot; /&gt;&lt;/p&gt;
&lt;p&gt;No puede realizar auditorías de todo lo que realiza un usuario, sino de tablas. Por lo que voy a especificar las tablas del usuario scott que quiero auditar:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT audit.audit_table(&apos;scott.emp&apos;);
SELECT audit.audit_table(&apos;scott.dept&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Averigua si en MySQL se pueden realizar los apartados 1, 3 y 4. Si es así, documenta el proceso adecuadamente&lt;/h2&gt;
&lt;h3&gt;Ejercicio 1&lt;/h3&gt;
&lt;p&gt;Para obtener los datos de los inicios de sesión, edito el fichero &lt;code&gt;/etc/mysql/mariadb.conf.d/50-server.cnf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;general_log_file       = /var/log/mysql/mysql.log
general_log            = 1
log_error = /var/log/mysql/error.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cambio el propietario del directorio de los logs y reinicio el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chown -R mysql:mysql /var/log/mysql
systemctl restart mariadb.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, tras varios inicios de sesión, compruebo el fichero de logs &lt;code&gt;/var/log/mysql/mysql.log&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fpOjaLH.png&quot; alt=&quot;mysqlLog&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Ejercicio 3&lt;/h3&gt;
&lt;p&gt;Para realizar la auditoría voy a instalar el plugin &lt;code&gt;server_audit&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;INSTALL SONAME &apos;server_audit&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora edito el fichero &lt;code&gt;/etc/mysql/mariadb.conf.d/50-server.cnf&lt;/code&gt; y reinicio mariadb:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[server]
server_audit_events=CONNECT,QUERY,TABLE
server_audit_logging=ON
server_audit_incl_users=scott
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras insertar un nuevo empleado, compruebo el fichero de log &lt;code&gt;/var/lib/mysql/server_audit.log&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GAOO4Kz.png&quot; alt=&quot;mysqlLog&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Averigua las posibilidades que ofrece MongoDB para auditar los cambios que va sufriendo un documento. Demuestra su funcionamiento&lt;/h2&gt;
&lt;p&gt;Para realizar las auditorías, es necesario instalar la versión Enterprise. La documentación de instalación oficial se encuentra en el siguiente enlace &lt;a href=&quot;https://www.mongodb.com/docs/manual/tutorial/install-mongodb-enterprise-on-debian/&quot;&gt;https://www.mongodb.com/docs/manual/tutorial/install-mongodb-enterprise-on-debian/&lt;/a&gt;. Una vez instalado podemos hacer lo siguiente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Habilitar las auditorías en el syslog desde la consola:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mongod --dbpath /var/lib/mongodb/ --auditDestination syslog
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Habilitar las auditorías en un fichero JSON desde la consola:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mongod --dbpath /var/lib/mongodb/ --auditDestination file --auditFormat JSON --auditPath /var/lib/mongodb/auditLog.json
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Habilitar las auditorías en un fichero BSON desde la consola:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mongod --dbpath /var/lib/mongodb/ --auditDestination file --auditFormat BSON --auditPath /var/lib/mongodb/auditLog.bson
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Habilitar las auditorías en la consola desde la consola:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mongod --dbpath /var/lib/mongodb/ --auditDestination console
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;He utilizado la salida por consola y preferencia de formato ya que se trata también de un json.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mongod --dbpath /var/lib/mongodb/ --auditDestination console | jq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DNdM1aZ.png&quot; alt=&quot;mongoAudit&quot; /&gt;&lt;/p&gt;
&lt;p&gt;##. Averigua si en MongoDB se pueden auditar los accesos a una colección concreta. Demuestra su funcionamiento&lt;/p&gt;
</content:encoded></item><item><title>Cortafuegos II: Perimetral con nftables</title><link>https://www.robertops.com/posts/2023-02-17_cortafuegos2/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-02-17_cortafuegos2/</guid><pubDate>Fri, 17 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Enunciado&lt;/h2&gt;
&lt;p&gt;Realiza con nftables el ejercicio de la página &lt;a href=&quot;https://fp.josedomingo.org/seguridadgs/u03/perimetral_iptables.html&quot;&gt;https://fp.josedomingo.org/seguridadgs/u03/perimetral_iptables.html&lt;/a&gt; documentando las pruebas de funcionamiento realizadas.&lt;/p&gt;
&lt;p&gt;Debes añadir después las reglas necesarias para que se permitan las siguientes operaciones:&lt;/p&gt;
&lt;h2&gt;Preparación&lt;/h2&gt;
&lt;p&gt;El escenario es el siguiente&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ynMPcoB.png&quot; alt=&quot;escenario&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Instalo nftables en el cortafuegos&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install nftables
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creo las tablas de filter y nat&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add table inet filter
sudo nft add table inet nat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;creo las cadenas de filter&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add chain inet filter input { type filter hook input priority 0 \; counter \; policy accept \; }
sudo nft add chain inet filter output { type filter hook output priority 0 \; counter \; policy accept \; }
sudo nft add chain inet filter forward { type filter hook forward priority 0 \; counter \; policy accept \; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;creo las cadenas de nat&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add chain inet nat prerouting { type nat hook prerouting priority 0 \; }
sudo nft add chain inet nat postrouting { type nat hook postrouting priority 100 \; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SSH al cortafuegos&lt;/h3&gt;
&lt;p&gt;Ahora acepto el tráfico ssh entrante al router-fw&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter input ip saddr 172.22.0.0/16 tcp dport 22 ct state new,established counter accept
sudo nft add rule inet filter output ip daddr 172.22.0.0/16 tcp sport 22 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y entrante desde la VPN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter input ip saddr 172.29.0.0/16 tcp dport 22 ct state new,established counter accept
sudo nft add rule inet filter output ip daddr 172.29.0.0/16 tcp sport 22 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Política por defecto&lt;/h3&gt;
&lt;p&gt;Y pongo la política por defecto a drop:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft chain inet filter input { policy drop \; }
sudo nft chain inet filter output { policy drop \; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora compruebo que puedo hacer ssh&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/T2NL5kC.png&quot; alt=&quot;ssh&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7o6qhGY.png&quot; alt=&quot;ssh2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Activar el bit de forward&lt;/h3&gt;
&lt;p&gt;En el cortafuegos, activo el bit de forwarding&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Ooq3yo9.png&quot; alt=&quot;bitforward&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;SNAT&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet nat postrouting oifname &quot;ens3&quot; ip saddr 192.168.100.0/24 counter masquerade
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/bWXZJ8j.png&quot; alt=&quot;snat&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;SSH desde el cortafuego a la LAN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter output oifname &quot;ens4&quot; ip daddr 192.168.100.0/24 tcp dport 22 ct state new,established counter accept
sudo nft add rule inet filter input iifname &quot;ens4&quot; ip saddr 192.168.100.0/24 tcp sport 22 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/WRuODxe.png&quot; alt=&quot;ssh3&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Tráfico para la interfaz loopback&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter output oifname &quot;lo&quot; counter accept
sudo nft add rule inet filter input iifname &quot;lo&quot; counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/fEVtFCW.png&quot; alt=&quot;loopback&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Peticiones y respuestas protocolo ICMP&lt;/h3&gt;
&lt;p&gt;Desde el cortafuegos a internet&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter input iifname &quot;ens3&quot; icmp type echo-request counter accept
sudo nft add rule inet filter output oifname &quot;ens3&quot; icmp type echo-reply counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora desde mi portátil hago un ping al cortafuegos:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vO8QNUq.png&quot; alt=&quot;icmp&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Reglas forward&lt;/h3&gt;
&lt;h4&gt;ping desde la LAN&lt;/h4&gt;
&lt;p&gt;Desde el cortafuegos a la LAN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter input iifname &quot;ens4&quot; icmp type echo-reply counter accept
sudo nft add rule inet filter output oifname &quot;ens4&quot; icmp type echo-request counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/oeg8xx6.png&quot; alt=&quot;icmp&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Consultas y respuestas DNS desde la LAN&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter forward iifname &quot;ens4&quot; oifname &quot;ens3&quot; ip saddr 192.168.100.0/24 udp dport 53 ct state new,established counter accept
sudo nft add rule inet filter forward iifname &quot;ens3&quot; oifname &quot;ens4&quot; ip daddr 192.168.100.0/24 udp sport 53 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vnJEfIP.png&quot; alt=&quot;dns&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Permitimos la navegación web desde la LAN&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter output oifname &quot;ens3&quot; ip protocol tcp tcp dport { 80,443 } ct state new,established counter accept
sudo nft add rule inet filter input iifname &quot;ens3&quot; ip protocol tcp tcp sport { 80,443 } ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/s4Yw8Cl.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Permitimos el acceso a nuestro servidor web de la LAN desde el exterior&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter forward iifname &quot;ens3&quot; oifname &quot;ens4&quot; ip daddr 192.168.100.0/24 tcp dport 80 ct state new,established counter accept
sudo nft add rule inet filter forward iifname &quot;ens4&quot; oifname &quot;ens3&quot; ip saddr 192.168.100.0/24 tcp sport 80 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet nat prerouting iifname &quot;ens3&quot; tcp dport 80 counter dnat ip to 192.168.100.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/C69ZdJ8.png&quot; alt=&quot;web2&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Reglas del enunciado&lt;/h2&gt;
&lt;h3&gt;Permite poder hacer conexiones ssh al exterior desde la máquina cortafuegos&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter output oifname &quot;ens3&quot; tcp dport 22 ct state new,established counter accept
sudo nft add rule inet filter input iifname &quot;ens3&quot; tcp sport 22 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora pruebo accediendo a otra máquina desde el cortafuegos por ssh&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YsoD8Cx.png&quot; alt=&quot;ssh&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Permite hacer consultas DNS desde la máquina cortafuegos sólo al servidor 192.168.202.2. Comprueba que no puedes hacer un dig @1.1.1.1.&lt;/h3&gt;
&lt;p&gt;Creo la regla&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter output ip daddr 192.168.202.2 udp dport 53 ct state new,established counter accept
sudo nft add rule inet filter input ip saddr 192.168.202.2 udp sport 53 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compruebo que no puedo hacer un dig @1.1.1.1 y si uno con @192.168.202.2&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XKHU64n.png&quot; alt=&quot;dns&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Permite que la máquina cortafuegos pueda navegar por internet.&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter output oifname &quot;ens3&quot; ip protocol tcp tcp dport { 80,443 } ct state new,established counter accept
sudo nft add rule inet filter input iifname &quot;ens3&quot; ip protocol tcp tcp sport { 80,443 } ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compruebo que puedo navegar por internet&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dXNkvJz.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Los equipos de la red local deben poder tener conexión al exterior.&lt;/h3&gt;
&lt;p&gt;Este paso lo realicé en la preparación en el siguiente apartado: &lt;a href=&quot;#reglas-forward&quot;&gt;Reglas forward&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Permitimos el ssh desde el cortafuego a la LAN&lt;/h3&gt;
&lt;p&gt;Este paso lo realicé en la preparación en el siguiente apartado: &lt;a href=&quot;#ssh-desde-el-cortafuego-a-la-lan&quot;&gt;SSH desde el cortafuego a la LAN&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Pruebo que funciona (usando mi clave privada)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JdG9dTH.png&quot; alt=&quot;ssh2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Permitimos hacer ping desde la LAN a la máquina cortafuegos&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter input iifname &quot;ens4&quot; icmp type echo-request counter accept
sudo nft add rule inet filter output oifname &quot;ens4&quot; icmp type echo-reply counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compruebo que funciona&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/reyRJ34.png&quot; alt=&quot;icmp&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Permite realizar conexiones ssh desde los equipos de la LAN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter forward iifname &quot;ens4&quot; oifname &quot;ens3&quot; ip saddr 192.168.100.0/24 tcp dport 22 ct state new,established counter accept
sudo nft add rule inet filter forward iifname &quot;ens3&quot; oifname &quot;ens4&quot; ip daddr 192.168.100.0/24 tcp sport 22 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, tras copiar mi clave privada a la máquina LAN, accedo a alfa por ssh&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/eZ9IVZf.png&quot; alt=&quot;ssh3&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Instala un servidor de correos en la máquina de la LAN. Permite el acceso desde el exterior y desde el cortafuego al servidor de correos. Para probarlo puedes ejecutar un telnet al puerto 25 tcp&lt;/h3&gt;
&lt;h3&gt;Permite poder hacer conexiones ssh desde exterior a la LAN&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter forward iifname &quot;ens3&quot; oifname &quot;ens4&quot; ip daddr 192.168.100.0/24 tcp dport 22 ct state new,established counter accept
sudo nft add rule inet filter forward iifname &quot;ens4&quot; oifname &quot;ens3&quot; ip saddr 192.168.100.0/24 tcp sport 22 ct state established counter accept
sudo nft add rule inet nat prerouting iifname &quot;ens3&quot; tcp dport 22 counter dnat ip to 192.168.100.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compruebo que al acceder a la IP del firewall entro en la maquina LAN&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/VzI01Oo.png&quot; alt=&quot;ssh4&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Modifica la regla anterior, para que al acceder desde el exterior por ssh tengamos que conectar al puerto 2222, aunque el servidor ssh este configurado para acceder por el puerto 22&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet nat prerouting iifname &quot;ens3&quot; tcp dport 2222 counter dnat ip to 192.168.100.10:22
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora entro usando el puerto 2222&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Mm45WLy.png&quot; alt=&quot;ssh5&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Permite hacer consultas DNS desde la LAN sólo al servidor 192.168.202.2. Comprueba que no puedes hacer un dig @1.1.1.1&lt;/h3&gt;
&lt;p&gt;Como ya tengo una regla para las consultas dns desde la lan,busco los handles que tiene para borrarla&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft -a list ruleset 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/IH9k9GK.png&quot; alt=&quot;dns2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;son el 22 y el 23. Ahora los borro:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft delete rule inet filter forward handle 22
sudo nft delete rule inet filter forward handle 23
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora añado las reglas para la LAN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo nft add rule inet filter forward iifname &quot;ens4&quot; oifname &quot;ens3&quot; ip saddr 192.168.100.0/24 ip daddr 192.168.202.2 udp dport 53 ct state new,established counter accept
sudo nft add rule inet filter forward iifname &quot;ens3&quot; oifname &quot;ens4&quot; ip saddr 192.168.202.2 ip daddr 192.168.100.0/24 udp sport 53 ct state established counter accept
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1zmmeCt.png&quot; alt=&quot;dns3&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Permite que los equipos de la LAN puedan navegar por internet.&lt;/h3&gt;
&lt;p&gt;Este paso lo realicé en la preparación en el siguiente apartado: &lt;a href=&quot;#permite-que-los-equipos-de-la-lan-puedan-navegar-por-internet&quot;&gt;Permite que los equipos de la LAN puedan navegar por internet.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Compruebo que funciona&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/wyT7DEF.png&quot; alt=&quot;web2&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Cortafuegos I: De nodo con iptables</title><link>https://www.robertops.com/posts/2023-02-15_cortafuegos1/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-02-15_cortafuegos1/</guid><pubDate>Wed, 15 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Enunciado: &lt;a href=&quot;https://fp.josedomingo.org/seguridadgs/u03/ejercicio1.html&quot;&gt;https://fp.josedomingo.org/seguridadgs/u03/ejercicio1.html&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Preparación&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Limpiamos las reglas previas&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -F
iptables -t nat -F
iptables -Z
iptables -t nat -Z
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Acceso por SSH&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Antes de añadir la política por defecto voy a añadir reglas para permitir el acceso por ssh&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.22.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -d 172.22.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Estas reglas sirve para acceder desde la misma red, además añado las siguientes para acceder a través de la VPN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.29.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -d 172.29.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora añado las &lt;strong&gt;políticas por defecto&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -P INPUT DROP
iptables -P OUTPUT DROP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora pruebo que se han aplicado, por ejemplo, haciendo un ping:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GgWurZe.png&quot; alt=&quot;ping&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permitimos tráfico por la interfaz de loopback&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i lo -p icmp -j ACCEPT
iptables -A OUTPUT -o lo -p icmp -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Peticiones y respuestas del protocolo ICMP&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i ens3 -p icmp --icmp-type echo-reply -j ACCEPT
iptables -A OUTPUT -o ens3 -p icmp --icmp-type echo-request -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo que funciona haciendo un ping&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/eTTjjEV.png&quot; alt=&quot;ping2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consultas y respuestas DNS&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -o ens3 -p udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i ens3 -p udp --sport 53 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo que funciona haciendo una consulta DNS&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Ep1vuVz.png&quot; alt=&quot;dns&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tráfico HTTP&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -o ens3 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i ens3 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo que funciona haciendo una consulta HTTP&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/aB17haB.png&quot; alt=&quot;http&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tráfico HTTPS&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -o ens3 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i ens3 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo que funciona haciendo una consulta HTTPS&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/BzyJeje.png&quot; alt=&quot;https&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tráfico HTTP/HTTPs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Los dos puntos anteriores se pueden resumir en una sola regla&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -o ens3 -p tcp -m multiport --dports 80,443 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i ens3 -p tcp -m multiport --sports 80,443 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Acceso al servidor web&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -i ens3 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o ens3 -p tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pruebo que funciona haciendo una consulta HTTP desde el exterior&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1TbKhY2.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Ejercicios&lt;/h2&gt;
&lt;h3&gt;1. Permite poder hacer conexiones ssh al exterior&lt;/h3&gt;
&lt;p&gt;Para esto uso las reglas de ssh citas anteriormente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.22.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -d 172.22.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Estas reglas sirve para acceder desde la misma red, además añado las siguientes para acceder a través de la VPN&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A INPUT -s 172.29.0.0/16 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -d 172.29.0.0/16 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/8h6tmFh.png&quot; alt=&quot;ssh1&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/xhNYful.png&quot; alt=&quot;ssh2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2. Deniega el acceso a tu servidor web desde una ip concreta&lt;/h3&gt;
&lt;p&gt;Como ya he creado en la preparación una regla que permite el acceso al servidor web, para que el bloque funcione tengo que añadir la regla antes, ya que el orden es importante en iptables. Para ello, primero miro la posición de la regla que permite el acceso al servidor web&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -L -n -v --line-numbers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/VLs4Xd7.png&quot; alt=&quot;iptables&quot; /&gt;&lt;/p&gt;
&lt;p&gt;La regla que permite el acceso está en la dirección 8; ahora creo la regla con la ip de mi máqunina:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -I INPUT 8 -s 172.29.0.42 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j DROP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/T361Yz0.png&quot; alt=&quot;iptables2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora compruebo que no puedo acceder al servidor web desde mi máquina&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/9xydTG9.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. Permite hacer consultas DNS sólo al servidor 192.168.202.2. Comprueba que no puedes hacer un dig @1.1.1.1&lt;/h3&gt;
&lt;p&gt;Primero borro las reglas de DNS que he creado anteriormente&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -D OUTPUT -o ens3 -p udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -D INPUT -i ens3 -p udp --sport 53 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y ahora añado las reglas nuevas, permitiendo solo el servidor 192.168.202.2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -d 192.168.202.2 -p udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -s 192.168.202.2 -p udp --sport 53 -m state --state ESTABLISHED -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora compruebo que no puedo hacer un &lt;code&gt;dig@1.1.1.1 www.josedomingo.org&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/May5Dwa.png&quot; alt=&quot;dns&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y si que puedo usando el dns, &lt;code&gt;dig@192.168.202.2 www.josedomingo.org&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1O9FXoM.png&quot; alt=&quot;dns2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;4. No permitir el acceso al servidor web de www.josedomingo.org, Tienes que utilizar la ip. ¿Puedes acceder a fp.josedomingo.org?&lt;/h3&gt;
&lt;p&gt;Al igual que en el ejercicio 2, primero miro la posición de la regla que permite el acceso al servidor web&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/HbcTZPw.png&quot; alt=&quot;iptables&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Está en el lugar 5. Ahora creo la regla con la ip de josedomingo.org:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -I OUTPUT 5 -d 37.187.119.60 -p tcp --dport 80 -m state --state NEW,ESTABLISHED -j DROP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compruebo que se ha creado correctamente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Xp4mGZu.png&quot; alt=&quot;iptables2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora compruebo que no puedo acceder al servidor web de josedomingo.org&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/POagTDZ.png&quot; alt=&quot;web&quot; /&gt;&lt;/p&gt;
&lt;p&gt;y si que puedo acceder a portquiz.net:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/f0SccEm.png&quot; alt=&quot;web2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;5. Permite mandar un correo usando nuestro servidor de correo: babuino-smtp. Para probarlo ejecuta un telnet babuino-smtp.gonzalonazareno.org 25&lt;/h3&gt;
&lt;p&gt;Para hacerlo utilizo la ip de babuino:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iptables -A OUTPUT -d 192.168.203.3 -p tcp --dport 25 -j ACCEPT
iptables -A INPUT -s 192.168.203.3 -p tcp --sport 25 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/tBoRNWu.png&quot; alt=&quot;smtp&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;6. Instala un servidor mariadb, y permite los accesos desde la ip de tu cliente. Comprueba que desde otro cliente no se puede acceder&lt;/h3&gt;
&lt;p&gt;Instalo MariaDB:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install mariadb-server
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configuro el acceso remoto, editando el fichero &lt;code&gt;/etc/mysql/mariadb.conf.d/50-server.cnf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bind-address            = 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso, reinicio el servicio y añado la regla para permitir el acceso desde mi cliente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo iptables -A INPUT -s 172.29.0.42 -p tcp --dport 3306 -j ACCEPT
sudo iptables -A OUTPUT -d 172.29.0.42 -p tcp --sport 3306 -j ACCEPT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, en la siguiente captura, realizo una conexión con la base de datos desde el cliente, tras eso, desconecto la VPN para dejar de tener la IP que está autorizada, y vuelvo a intentar acceder, pero no puedo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1Z8hCqe.png&quot; alt=&quot;mariadb&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Informática forense</title><link>https://www.robertops.com/posts/2023-02-08_informatica_forense/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-02-08_informatica_forense/</guid><pubDate>Wed, 08 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Enunciado&lt;/h2&gt;
&lt;p&gt;La informática forense es el conjunto de técnicas que nos permite obtener la máxima información posible tras un incidente o delito informático.&lt;/p&gt;
&lt;p&gt;En esta práctica, realizarás la fase de toma de evidencias y análisis de las mismas sobre una máquina Linux y otra Windows. Supondremos que pillamos al delincuente in fraganti y las máquinas se encontraban encendidas. Opcionalmente, podéis realizar el análisis de un dispositivo Android.&lt;/p&gt;
&lt;p&gt;Sobre cada una de las máquinas debes realizar un volcado de memoria y otro de disco duro, tomando las medidas necesarias para certificar posteriormente la cadena de custodia.&lt;/p&gt;
&lt;h2&gt;Apartado A. Máquina Windows&lt;/h2&gt;
&lt;h3&gt;Volcado de memoria&lt;/h3&gt;
&lt;p&gt;Para realizar el volcado de memoria he usado la herramienta &lt;strong&gt;FTK Imager&lt;/strong&gt;, que permite realizar volcados de disco y memoria. Para ello hago lo siguiente. Utilizo las siguientes opciones:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/tD6w8MG.png&quot; alt=&quot;ftk4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, para analizar los datos, utilizo &lt;strong&gt;Volatility&lt;/strong&gt; en mi máquina debian; para ello, especifico el fichero que he creado con FTK Imager al utilizar los comandos.&lt;/p&gt;
&lt;h4&gt;1. Procesos en ejecución&lt;/h4&gt;
&lt;p&gt;Uso el comando &lt;code&gt;pslist&lt;/code&gt; para ver los procesos en ejecución:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python vol.py -f &quot;/media/roberto/usb/memdump.mem&quot; windows.pslist.PsList
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/tMhTGFN.png&quot; alt=&quot;vol1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. Servicios en ejecución&lt;/h4&gt;
&lt;p&gt;Uso el comando &lt;code&gt;getservicesids&lt;/code&gt; para ver los servicios en ejecución:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python vol.py -f &quot;/media/roberto/usb/memdump.mem&quot; windows.getservicesids.GetServiceSIDs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/BZjmc2d.png&quot; alt=&quot;vol2&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. Puertos abiertos&lt;/h4&gt;
&lt;p&gt;Uso el comando &lt;code&gt;netstat&lt;/code&gt; para ver los puertos abiertos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python vol.py -f &quot;/media/roberto/usb/memdump.mem&quot; windows.netstat.NetStat 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DnH0We9.png&quot; alt=&quot;vol3&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. Conexiones establecidas por la máquina&lt;/h4&gt;
&lt;p&gt;Uso el comando &lt;code&gt;netscan&lt;/code&gt; para ver las conexiones establecidas por la máquina:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python vol.py -f &quot;/media/roberto/usb/memdump.mem&quot; windows.netscan.NetScan
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7ZrLSg4.png&quot; alt=&quot;vol4&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;5. Sesiones de usuario establecidas remotamente&lt;/h4&gt;
&lt;p&gt;Uso el comando &lt;code&gt;sessions&lt;/code&gt; para ver las sesiones de usuario establecidas remotamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python vol.py -f &quot;/media/roberto/usb/memdump.mem&quot; windows.sessions.Sessions 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/YcTjn7U.png&quot; alt=&quot;vol5&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;6. Ficheros transferidos recientemente por NetBios&lt;/h4&gt;
&lt;h4&gt;7. Contenido de la caché DNS&lt;/h4&gt;
&lt;h4&gt;8. Variables de entorno&lt;/h4&gt;
&lt;p&gt;Para ver las variables de entorno, uso el comando &lt;code&gt;envars&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python vol.py -f &quot;/media/roberto/usb/memdump.mem&quot;  windows.envars.Envars
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/IUSyeJ2.png&quot; alt=&quot;vol6&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Volcado del registro&lt;/h3&gt;
&lt;p&gt;Para realizar el volcado de registro he usado también la herramienta &lt;strong&gt;FTK Imager&lt;/strong&gt;. Utilizo la opción &lt;code&gt;Obtain system files&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JUcvnmy.png&quot; alt=&quot;ftk5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una vez finalizado el volcado, utilizo el programa &lt;strong&gt;Registry Viewer&lt;/strong&gt; para analizar los datos obtenidos.&lt;/p&gt;
&lt;h4&gt;10. Redes wifi utilizadas recientemente&lt;/h4&gt;
&lt;p&gt;Para ver las redes wifi usadas recientemente, abro el archivo &lt;code&gt;software&lt;/code&gt;, y ahí sigo la siguiente ruta: &lt;code&gt;Microsoft/Windows NT/CurrentVersion/NetworkList/Profiles&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/RFzUv61.png&quot; alt=&quot;reg1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;11. Configuración del firewall de nodo&lt;/h4&gt;
&lt;p&gt;Para ver la configuración del firewall de nodo, abro el archivo &lt;code&gt;system&lt;/code&gt;, y ahí sigo la siguiente ruta: &lt;code&gt;ControlSet001/Services/SharedAccess/Parameters/FirewallPolicy/FirewallRules&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4b7ZsO1.png&quot; alt=&quot;reg2&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;12. Programas que se ejecutan en el Inicio&lt;/h4&gt;
&lt;p&gt;Para ver los programas que se ejecutan en el inicio, abro el archivo &lt;code&gt;software&lt;/code&gt;, y ahí sigo la siguiente ruta: &lt;code&gt;Microsoft/Windows/CurrentVersion/Run&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/6MsrP73.png&quot; alt=&quot;reg3&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;13. Asociación de extensiones de ficheros y aplicaciones&lt;/h4&gt;
&lt;p&gt;No he podido encontrar en la aplicación &lt;strong&gt;Registry Viewer&lt;/strong&gt; los registros, pero usando regedit en la máquina, se encuentran en la siguiente localización: &lt;code&gt;Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Ig5FCUk.png&quot; alt=&quot;reg4&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Volcado de disco&lt;/h3&gt;
&lt;p&gt;Para realizar el volcado de disco he usado la herramienta FTK Imager también:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/tUwr02N.png&quot; alt=&quot;ftk1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Selecciono &lt;strong&gt;RAW&lt;/strong&gt; como tipo de imagen:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GWoxYEU.png&quot; alt=&quot;ftk2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y, como indica la ayuda, en Fragment size pongo 0, además de indicar la ruta y el nombre del fichero:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/cTgXwqp.png&quot; alt=&quot;ftk3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;El resto de los pasos del proceso los dejo con los valores por defecto.&lt;/p&gt;
&lt;p&gt;Ahora, para analizar los datos, utilizo Autopsy; para ello, abro el fichero que he creado con FTK Imager, indicamos el direcorio del fichero , y en tipo, selecciono &lt;strong&gt;Disk image or VM file&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JHCRjQ2.png&quot; alt=&quot;autopsy1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tras finalizar la configuración, empezará a analizar el volcado de disco. Este proceso tarda bastante tiempo en completarse. Una vez acabado podemos analizar los datos obtenidos.&lt;/p&gt;
&lt;h4&gt;9. Dispositivos USB conectados&lt;/h4&gt;
&lt;p&gt;Para ver los dispositivos USB conectados, utilizo la opción &lt;code&gt;USB Device Attached&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1XfoSfH.png&quot; alt=&quot;autopsy8&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;14. Aplicaciones usadas recientemente&lt;/h4&gt;
&lt;p&gt;Para ver las aplicaciones usadas recientemente, utilizo la opción &lt;code&gt;User Activity&lt;/code&gt; del disco duro:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vthHo40.png&quot; alt=&quot;autopsy9&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;15. Ficheros abiertos recientemente&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ZQtfblE.png&quot; alt=&quot;autopsy9&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;16. Software Instalado&lt;/h4&gt;
&lt;p&gt;Para ver el software instalado, utilizo la opción &lt;code&gt;Installed Programs&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xsFb4yR.png&quot; alt=&quot;autopsy10&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;18. Cuentas de Usuario&lt;/h4&gt;
&lt;p&gt;Para ver las cuentas de usuario, utilizo la opción &lt;code&gt;OS Accounts&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/A1Ha5e6.png&quot; alt=&quot;autopsy11&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;19. Historial de navegación y descargas, cookies&lt;/h4&gt;
&lt;p&gt;Para ver el historial de navegación, utilizo la opción &lt;code&gt;Web History&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1EN0RI4.png&quot; alt=&quot;autopsy12&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para ver las cookies utilizo la opción &lt;code&gt;Web Cookies&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rDaxg3p.png&quot; alt=&quot;autopsy13&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para ver el historial de descargas, utilizo la opción &lt;code&gt;Web Downloads&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/A6x55kE.png&quot; alt=&quot;autopsy14&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;21.  Archivos con extensión cambiada&lt;/h4&gt;
&lt;p&gt;Los archivos con extensión cambiada aparecen en la sección &lt;code&gt;Analysis Results/Extension Mismatch Detected&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/hmLGaGi.png&quot; alt=&quot;autopsy2&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;22.  Archivos eliminados&lt;/h4&gt;
&lt;p&gt;Los archivos eliminados aparecen en la sección &lt;code&gt;Recycle Bin&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XmQIyKC.png&quot; alt=&quot;autopsy3&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;23.  Archivos Ocultos&lt;/h4&gt;
&lt;p&gt;En la opción para explorar el sistema de archivos, se pueden distinguir los archivos ocultos porque en los metadatos, aparece la etiqueta: &lt;code&gt;Hidden&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OcIKUYP.png&quot; alt=&quot;autopsy4&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;24.  Archivos que contienen una cadena determinada&lt;/h4&gt;
&lt;p&gt;Para buscar archivos que contengan una cadena determinada, utilizo la opción &lt;code&gt;Keyword Search&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/B9ezFRy.png&quot; alt=&quot;autopsy5&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;25.  Búsqueda de imágenes por ubicación&lt;/h4&gt;
&lt;p&gt;Para buscar imágenes por ubicación, utilizo la opción &lt;code&gt;Geolocation&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/G9nTp6h.png&quot; alt=&quot;autopsy6&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;26.  Búsqueda de archivos por autor&lt;/h4&gt;
&lt;p&gt;Para buscar archivos por autor, utilizo la opción &lt;code&gt;Metadata&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XcQPTmy.png&quot; alt=&quot;autopsy7&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Apartado B) Máquina Linux&lt;/h2&gt;
&lt;p&gt;Intenta realizar las mismas operaciones en una máquina Linux para aquellos apartados que tengan sentido y no se realicen de manera idéntica a Windows.&lt;/p&gt;
&lt;h3&gt;Volcado de memoria&lt;/h3&gt;
&lt;p&gt;He realizado el volcado de memoria con &lt;strong&gt;Lime&lt;/strong&gt;, que es una herramienta que se utiliza para realizar volcados de memoria en Linux. Para ello, he usado el comando de la siguiente manera  (he instalado Lime meadiante su repositorio en github):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;insmod lime-4.19.0-23-amd64.ko &quot;path=/mnt/linux/memorialinux.mem format=lime&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Es importante que para realizar el volcado, hay que iniciar el sistema de forma insegura, ya que si no, no permite utilizar el comando.&lt;/p&gt;
&lt;p&gt;No he sido capaz de realizar el análisis utilizando volatility, ya que es necesario instalar plugins para interpretar el volcado de memoria, y no los reconoce, por lo que voy a usar los comandos en lugar de trabajar sobre el volcado.&lt;/p&gt;
&lt;h4&gt;1. Procesos en ejecución&lt;/h4&gt;
&lt;p&gt;Para ver los procesos en ejecución, utilizo el comando &lt;code&gt;ps aux&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/I6H5Egn.png&quot; alt=&quot;ps1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;2. Servicios en ejecución&lt;/h4&gt;
&lt;p&gt;Para ver los servicios en ejecución, utilizo el comando &lt;code&gt;systemctl&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl list-units --type=service --state=running
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lh2yFCl.png&quot; alt=&quot;systemctl1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;3. Puertos abiertos&lt;/h4&gt;
&lt;p&gt;Para ver los puertos abiertos, utilizo el comando &lt;code&gt;netstat&lt;/code&gt;, que se encuentra en el paquete &lt;code&gt;net-tools&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install net-tools
netstat -tulpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/QmwsCnY.png&quot; alt=&quot;netstat1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;4. Conexiones establecidas por la máquina&lt;/h4&gt;
&lt;p&gt;Las sesiones se pueden observar en el comando netstat utilizado anteriormente.&lt;/p&gt;
&lt;h4&gt;5. Sesiones de usuario establecidas remotamente&lt;/h4&gt;
&lt;p&gt;Para ver las sesiones de usuario establecidas remotamente, utilizo el comando &lt;code&gt;who&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;who -a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/m6c6R6z.png&quot; alt=&quot;who1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;7. Contenido de la caché DNS&lt;/h4&gt;
&lt;p&gt;En debian, la caché dns está deshabilitada por defecto. Para habilitarla, voy a instalar el paquete &lt;strong&gt;nscd&lt;/strong&gt; (demonio de caché para servicio de nombres), y leer el fichero que genera. El fichero tiene un formato binario, por lo que voy a usar el comando &lt;code&gt;strings&lt;/code&gt; para ver su contenido:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;strings /var/cache/nscd/hosts
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4rfJMeG.png&quot; alt=&quot;nscd1&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;8. Variables de entorno&lt;/h4&gt;
&lt;p&gt;Para ver las variables de entorno, utilizo el comando &lt;code&gt;env&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/O4fIJRT.png&quot; alt=&quot;env1&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Volcado de disco&lt;/h3&gt;
&lt;p&gt;El volcado de disco en linux lo he realizado con el comando dd, que se utiliza para copiar archivos y volúmenes. Para ello, he usado el comando de la siguiente manera:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dd if=/dev/vda2 of=/mnt/linux/volcado_linux.001 bs=64K
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/6P6cdSJ.png&quot; alt=&quot;dd1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Es imporate que, durante el análisis de la imagen en Autopsy, el tiempo es mucho mayor, y que es de hecho la última fase, cuando ya se encuentra al 100% la que toma más tiempo.&lt;/p&gt;
&lt;p&gt;Los apartados rellativos al análisis usando autopsy, o se realizan exactamente igual que en Windows, o no tienen sentido en Linux, por lo que no los están documentados aquí.&lt;/p&gt;
</content:encoded></item><item><title>Práctica: Servidor de correos</title><link>https://www.robertops.com/posts/2023-02-07_servidor_correo/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-02-07_servidor_correo/</guid><pubDate>Tue, 07 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Gestión de correo desde el servidor&lt;/h2&gt;
&lt;h3&gt;Tarea 1&lt;/h3&gt;
&lt;p&gt;Documenta una prueba de funcionamiento, donde envíes desde tu servidor local al exterior. Muestra el log donde se vea el envío. Muestra el correo que has recibido. Muestra el registro SPF.&lt;/p&gt;
&lt;p&gt;Creo las siguientes entradas en el DNS  de mi dominio:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/izykpPC.png&quot; alt=&quot;MX&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Donde hay un registro MX que apunta a mail.admichin.es, que a su vez es un registro A que apunta a la IP de mi servidor. Además, hay un registro SPF que apunta a la ip de la máquina.&lt;/p&gt;
&lt;p&gt;Además, configuro resolución inversa en la configuración de la VPS:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Q8x5DC1.png&quot; alt=&quot;Reverse&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, en la VPS instalo los siguientes paquetes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt install postfix bsd-mailx -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Durante la configuración, selecciono &lt;strong&gt;Internet Site&lt;/strong&gt; y &lt;strong&gt;admichin.es&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Envío un correo a mi cuenta personal:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mail robertorodriguezmarquez98@gmail.com
Subject: Prueba de funcionamiento
Hola buenos días
Cc:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vemos el log de postfix:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tail /var/log/mail.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lii209w.png&quot; alt=&quot;Log&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y el correo recibido:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FWNO7aB.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Tarea 2&lt;/h3&gt;
&lt;p&gt;Documenta una prueba de funcionamiento, donde envíes un correo desde el exterior (gmail, hotmail,…) a tu servidor local. Muestra el log donde se vea el envío. Muestra cómo has leído el correo. Muestra el registro MX de tu dominio.&lt;/p&gt;
&lt;p&gt;Ahora envío un correo desde mi cuenta personal a mi servidor:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FWNO7aB.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y compruebo que el correo ha llegado a mi servidor:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JWSTUmj.png&quot; alt=&quot;Correo&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/D0xKQDi.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y el log de postfix:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pAWEPrP.png&quot; alt=&quot;Log&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Uso de alias y redirecciones&lt;/h2&gt;
&lt;h3&gt;Tarea 3&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Usos de alias y redirecciones&lt;/strong&gt;. Vamos a comprobar como los procesos del servidor pueden mandar correos para informar sobre su estado. Por ejemplo cada vez que se ejecuta una tarea cron podemos enviar un correo informando del resultado. Normalmente estos correos se mandan al usuario &lt;code&gt;root&lt;/code&gt; del servidor, para ello:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; crontab -e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;E indico donde se envía el correo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MAILTO=root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Puedes poner alguna tarea en el cron para ver como se mandan correo.&lt;/p&gt;
&lt;p&gt;Posteriormente usando alias y redirecciones podemos hacer llegar esos correos a nuestro correo personal.&lt;/p&gt;
&lt;p&gt;Configura el cron para enviar correo al usuario root. Comprueba que están llegando esos correos al root. Crea un nuevo alias para que se manden a un usuario sin privilegios. Comprueban que llegan a ese usuario. Por último crea una redirección para enviar esos correo a tu correo personal (gmail,hotmail,…).&lt;/p&gt;
&lt;p&gt;Voy a crear un script que muestre la fecha y el espacio en el disco. Lo guardo en &lt;code&gt;/root/script-espacio.sh&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash

echo &quot;##################################&quot;
echo &quot;Fecha y hora: $(date)&quot;
echo &quot;##################################&quot;
echo &quot;Espacio en el disco:&quot;
df -h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creamos la tarea de cron para que se ejecute cada 5 minutos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;crontab -e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y añadimos las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MAILTO = root

*/5 * * * * /root/script-espacio.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cuando pasan 5 minutos, recibimos el correo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/XVtl86N.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora voy a crear un alias para que se envíen los correos a un usuario sin privilegios (en este caso, calcetines), editando el fichero &lt;code&gt;/etc/aliases&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root: calcetines
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y ejecuto el comando &lt;code&gt;newaliases&lt;/code&gt; para que se actualicen los alias.&lt;/p&gt;
&lt;p&gt;Ahora, cuando pasen 5 minutos, recibimos el correo en el usuario sin privilegios:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/aogu98o.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora voy a crear una redirección para que se envíen los correos a mi correo personal, editando el fichero &lt;code&gt;/home/calcetines/.forward&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robertorodriguezmarquez98@gmail.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y ahora, cuando pasen 5 minutos, recibimos el correo en mi correo personal:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/UdaG5gg.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Para asegurar el envío&lt;/h2&gt;
&lt;h3&gt;Tarea 4&lt;/h3&gt;
&lt;p&gt;Configura de manera adecuada DKIM es tu sistema de correos. Comprueba el registro DKIM en la página https://mxtoolbox.com/dkim.aspx. Configura postfix para que firme los correos que envía. Manda un correo y comprueba la verificación de las firmas en ellos.
}&lt;/p&gt;
&lt;p&gt;Voy a instalar el paquete &lt;code&gt;opendkim&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install opendkim opendkim-tools -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En el fichero &lt;code&gt;/etc/opendkim.conf&lt;/code&gt;, edito las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Domain                  admichin.es
Selector                default
KeyFile                 /etc/opendkim/keys/admichin.es/default.private
#Socket                 local:/run/opendkim/opendkim.sock
Socket                  local:/var/spool/postfix/opendkim/opendkim.sock
PidFile                 /run/opendkim/opendkim.pid
TrustAnchorFile         /usr/share/dns/root.key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora añado el socket en el fichero &lt;code&gt;/etc/default/opendkim&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Tras eso, en el fichero &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt;, añado las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;milter_default_action = accept
milter_protocol = 6
smtpd_milters = local:opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora genero los ficheros de claves:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir /etc/opendkim/keys/admichin.es
cd /etc/opendkim/keys/admichin.es
opendkim-genkey -b 2048 -d admichin.es -D /etc/opendkim/keys/admichin.es -s default -v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, utilizando el contenido de &lt;code&gt;/etc/opendkim/keys/admichin.es/default.txt&lt;/code&gt;, añado un registro TXT en el dominio &lt;code&gt;admichin.es&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rWLIaWm.png&quot; alt=&quot;Registro&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Reinicio los servicios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart opendkim postfix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, cuando envío un correo, se añade la firma DKIM:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/SjhdRvn.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finalmente, compruebo la verificación de la firma en la página &lt;strong&gt;mxtoolbox&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/leht5mi.png&quot; alt=&quot;Verificación&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Para luchar contra el spam&lt;/h2&gt;
&lt;h2&gt;Gestión de correos desde un cliente&lt;/h2&gt;
&lt;h3&gt;Tarea 8&lt;/h3&gt;
&lt;p&gt;Configura el buzón de los usuarios de tipo Maildir. Envía un correo a tu usuario y comprueba que el correo se ha guardado en el buzón Maildir del usuario del sistema correspondiente. Recuerda que ese tipo de buzón no se puede leer con la utilidad mail.&lt;/p&gt;
&lt;p&gt;Voy a cambiar el tipo de buzón de los usuarios, editando el fichero &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;home_mailbox = Maildir/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora instalamos el cliente mutt para poder leer los correos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install mutt -y
systemctl restart postfix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tengo que hacer la siguiente configuración en cada usuario:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nano ~/.muttrc
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;set mbox_type=Maildir
set mbox=&quot;~/Maildir&quot;
set folder=&quot;~/Maildir&quot;
set spoolfile=&quot;~/Maildir&quot;
set record=&quot;+.Sent&quot;
set postponed=&quot;+.Drafts&quot;
set mask=&quot;!^\\.[^.]&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos ver el contenido del directorio &lt;code&gt;Maildir&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xJmTIsK.png&quot; alt=&quot;Directorio&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, cuando envío un correo, se guarda en el buzón Maildir del usuario del sistema correspondiente, y lo podemos leer con mutt:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/mCg6V9I.png&quot; alt=&quot;Correo&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Tarea 9&lt;/h3&gt;
&lt;p&gt;Instala configura dovecot para ofrecer el protocolo IMAP. Configura dovecot de manera adecuada para ofrecer autentificación y cifrado.&lt;/p&gt;
&lt;p&gt;Instalo dovecot:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install dovecot-imapd -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modifico el fichero &lt;code&gt;/etc/dovecot/conf.d/10-ssl.conf&lt;/code&gt; para añadir el certificado que generamos con certbot al crear la página,  modificando las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssl_cert = &amp;lt;/etc/letsencrypt/live/admichin.es-0001/fullchain.pem
ssl_key = &amp;lt;/etc/letsencrypt/live/admichin.es-0001/privkey.pem
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora cambiamos la localización de los mailbox en el fichero &lt;code&gt;/etc/dovecot/conf.d/10-mail.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mail_location = maildir:~/Maildir
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tarea 10&lt;/h3&gt;
&lt;p&gt;Instala un webmail (roundcube, horde, rainloop) para gestionar el correo del equipo mediante una interfaz web. Muestra la configuración necesaria y cómo eres capaz de leer los correos que recibe tu usuario.&lt;/p&gt;
&lt;p&gt;Voy a instalar &lt;strong&gt;roundcube&lt;/strong&gt; utilizando docker:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install docker.io -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creo una entrada de tipo CNAME con el nombre webmail:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dLpPCex.png&quot; alt=&quot;CNAME&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora vamos a crear la configuración  en un directorio que montaremos posteriormente por medio de bind mount en el docker;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir /root/cuboredondo
nano -cl /root/cuboredondo/custom.inc.php
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$config[&apos;mail_domain&apos;] = array(
    &apos;mail.admichin.es&apos; =&amp;gt; &apos;admichin.es&apos;
);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creo el contenedor de docker:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -d --name docker-cuboredondo \
-v /root/cuboredondo/:/var/roundcube/config/ \
-e ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mail.admichin.es \
-e ROUNDCUBEMAIL_SMTP_SERVER=ssl://mail.admichin.es \
-e ROUNDCUBEMAIL_SMTP_PORT=465 \
-e ROUNDCUBEMAIL_DEFAULT_PORT=993 \
-p 8001:80 \ 
roundcube/roundcubemail
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;o en una sola linea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -d --name docker-cuboredondo -v /root/cuboredondo/:/var/roundcube/config/ -e ROUNDCUBEMAIL_DEFAULT_HOST=ssl://mail.admichin.es -e ROUNDCUBEMAIL_SMTP_SERVER=ssl://mail.admichin.es -e ROUNDCUBEMAIL_SMTP_PORT=465 -e ROUNDCUBEMAIL_DEFAULT_PORT=993 -p 8001:80 roundcube/roundcubemail
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creo el virtualhost para el dominio en &lt;code&gt;/etc/nginx/sites-available/webmail.admichin.es&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
        listen 80;
        listen [::]:80;

        server_name webmail.admichin.es;

        return 301 https://$host$request_uri;
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;

        ssl    on;
        ssl_certificate /etc/letsencrypt/live/admichin.es-0001/fullchain.pem;
        ssl_certificate_key     /etc/letsencrypt/live/admichin.es-0001/privkey.pem;

        index index.html index.php index.htm index.nginx-debian.html;

        server_name webmail.admichin.es;

        location / {
                proxy_pass http://localhost:8001;
                include proxy_params;
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora activo el sitio y reinicio nginx:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ln -s /etc/nginx/sites-available/webmail.admichin.es /etc/nginx/sites-enabled/
systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora podemos acceder a la instancia de roundcube desde el navegador:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/CEWcKuI.png&quot; alt=&quot;Roundcube&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Como se puede ver acabo de recibir el correo del ejercicio de la tarea del cron. Ahora un correo enviado desde fuera:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/bqHtnNH.png&quot; alt=&quot;Correo enviado desde fuera&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Tarea 11&lt;/h3&gt;
&lt;p&gt;Configura de manera adecuada postfix para que podamos mandar un correo desde un cliente remoto. La conexión entre cliente y servidor debe estar autentificada con SASL usando dovecor y además debe estar cifrada. Para cifrar esta comunicación puedes usar dos opciones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ESMTP + STARTTLS&lt;/strong&gt;: Usando el puerto 567/tcp enviamos de forma segura el correo al servidor.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SMTPS&lt;/strong&gt;: Utiliza un puerto no estándar (465) para SMTPS (Simple Mail Transfer Protocol Secure). No es una extensión de smtp. Es muy parecido a HTTPS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Elige una de las opciones anterior para realizar el cifrado. Y muestra la configuración de un cliente de correo (evolution, thunderbird, …) y muestra como puedes enviar los correos.&lt;/p&gt;
&lt;p&gt;Usaré los mismos certificados que he generado antes para cifrar los emails que envío y recibo. Para ello, modifico la configuración de postfix en &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;smtpd_tls_cert_file = /etc/letsencrypt/live/admichin.es-0001/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/admichin.es-0001/privkey.pem

smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_authenticated_header = yes
broken_sasl_auth_clients = yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora edito &lt;code&gt;/etc/postfix/master.cf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;submission inet n       -       y       -       -       smtpd
  -o content_filter=spamassassin
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=$mua_client_restrictions
  -o smtpd_helo_restrictions=$mua_helo_restrictions
  -o smtpd_sender_restrictions=$mua_sender_restrictions
  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=$mua_client_restrictions
  -o smtpd_helo_restrictions=$mua_helo_restrictions
  -o smtpd_sender_restrictions=$mua_sender_restrictions
  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le indico a dovecot como debe realizar la auntentificación en el fichero &lt;code&gt;/etc/dovecot/conf.d/10-master.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;service auth {
  ...
  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
  }
  ...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora hay que abrir los puertos 465 y 993 en la vps. Tras eso, reiniciamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart postfix dovecot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora configuramos el cliente de correo &lt;strong&gt;evolution&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pdVsGVt.png&quot; alt=&quot;Configuración de evolution&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/Asvo17x.png&quot; alt=&quot;Configuración de evolution&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tras configurarlo probamos la recepción de un correo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4g08tu3.png&quot; alt=&quot;Correo recibido&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Y el envío:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ZBCNpp7.png&quot; alt=&quot;Correo enviado&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/jbV8Ojo.png&quot; alt=&quot;Correo enviado&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Tarea 12&lt;/h3&gt;
&lt;p&gt;Configura el cliente webmail para el envío de correo. Realiza una prueba de envío con el webmail.&lt;/p&gt;
&lt;p&gt;Esta tarea consiste en poder enviar correos desde el cliente roundcube, y se han realizado las configuraciones necesarias en la tarea 10. Ahora solo queda probarlo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/CGQ8zuf.png&quot; alt=&quot;Correo enviado desde roundcube&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/miQ63i6.png&quot; alt=&quot;Correo enviado desde roundcube&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Comprobación final&lt;/h2&gt;
&lt;h3&gt;Tarea 13&lt;/h3&gt;
&lt;p&gt;Prueba de envío de correo. En esta &lt;a href=&quot;https://www.mail-tester.com/&quot;&gt;página&lt;/a&gt; tenemos una herramienta completa y fácil de usar a la que podemos enviar un correo para que verifique y puntúe el correo que enviamos. Captura la pantalla y muestra la puntuación que has sacado.&lt;/p&gt;
&lt;p&gt;Enviamos un correo para comprobar la puntuación:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pt2pp6m.png&quot; alt=&quot;Correo enviado para comprobar la puntuación&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/X5klV3K.png&quot; alt=&quot;Correo enviado para comprobar la puntuación&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Integridad, firmas y autenticación</title><link>https://www.robertops.com/posts/2023-01-30_integridad_firmas/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-30_integridad_firmas/</guid><pubDate>Mon, 30 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Tarea 1: Firmas electrónicas&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Manda un documento y la firma electrónica del mismo a un compañero. Verifica la firma que tu has recibido.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;DOCUMENTO CIFRADO SECRETO&quot; &amp;gt; documento_rober.txt\n
gpg --output firmarober.sig --detach-sig documento_rober.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Envío los ficheros por correo.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;¿Qué significa el mensaje que aparece en el momento de verificar la firma?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dOFJJTl.png&quot; alt=&quot;Firma&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Significa que, aunque el fichero está firmado por esa firma, no se puede asegurar que la firma pertenezca a la persona que dice ser.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Vamos a crear un anillo de confianza entre los miembros de nuestra clase, para ello.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tu clave pública debe estar en un servidor de claves&lt;/li&gt;
&lt;li&gt;Escribe tu fingerprint en un papel y dárselo a tu compañero, para que puede descargarse tu clave pública.&lt;/li&gt;
&lt;li&gt;Te debes bajar al menos tres claves públicas de compañeros. Firma estas claves.&lt;/li&gt;
&lt;li&gt;Tu te debes asegurar que tu clave pública es firmada por al menos tres compañeros de la clase.&lt;/li&gt;
&lt;li&gt;Una vez que firmes una clave se la tendrás que devolver a su dueño, para que otra persona se la firme.&lt;/li&gt;
&lt;li&gt;Cuando tengas las tres firmas sube la clave al servidor de claves y rellena tus datos en la tabla Claves públicas PGP 2020-2021&lt;/li&gt;
&lt;li&gt;Asegurate que te vuelves a bajar las claves públicas de tus compañeros que tengan las tres firmas.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Muestra las firmas que tiene tu clave pública.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Listamos las firmas con el comando &lt;code&gt;gpg --list-sigs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LnWbJLH.png&quot; alt=&quot;Firmas&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Comprueba que ya puedes verificar sin “problemas” una firma recibida por una persona en la que confías.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Como se puede ver en la imagen anterior, nuestra firma está verificada por &lt;strong&gt;Antonio&lt;/strong&gt;, y viceversa. Por lo que podemos verificar sin problemas un documento firmado por &lt;strong&gt;Antonio&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LnLceFT.png&quot; alt=&quot;Verificación&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Comprueba que puedes verificar con confianza una firma de una persona en las que no confías, pero sin embargo si confía otra persona en la que tu tienes confianza total.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Ahora, vamos a utilizar un fichero firmado por &lt;strong&gt;María Jesús&lt;/strong&gt;, en la que no confiamos directamente, pero en la que sí tiene confianza plena &lt;strong&gt;Antonio&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dzHItTJ.png&quot; alt=&quot;Verificación 2&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Tarea 3: Integridad de ficheros&lt;/h2&gt;
&lt;p&gt;Para validar el contenido de la imagen CD, solo asegúrese de usar la herramienta apropiada para sumas de verificación. Para cada versión publicada existen archivos de suma de comprobación con algoritmos fuertes (SHA256 y SHA512); debería usar las herramientas sha256sum o sha512sum para trabajar con ellos.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Verifica que el contenido del hash que has utilizado no ha sido manipulado, usando la firma digital que encontrarás en el repositorio. Puedes encontrar una guía para realizarlo en este artículo: How to verify an authenticity of downloaded Debian ISO images&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-11.6.0-amd64-netinst.iso
wget https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/SHA256SUMS
sha256sum -c SHA256SUMS 2&amp;gt; /dev/null | grep netinst
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/aAHo0GB.png&quot; alt=&quot;Verificación de hash&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Tarea 4: Integridad y autenticidad&lt;/h2&gt;
&lt;p&gt;Busca información sobre &lt;strong&gt;apt secure&lt;/strong&gt; y responde las siguientes preguntas:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;¿Qué software utiliza apt secure para realizar la criptografía asimétrica?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Utiliza &lt;strong&gt;GnuPG&lt;/strong&gt; para realizar la criptografía asimétrica.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;¿Para que sirve el comando apt-key? ¿Qué muestra el comando apt-key list?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;El comando &lt;code&gt;apt-key&lt;/code&gt; sirve para administrar las claves de los repositorios de paquetes. El comando &lt;code&gt;apt-key list&lt;/code&gt; muestra las claves de los repositorios de paquetes.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;En que fichero se guarda el anillo de claves que guarda la herramienta apt-key?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;El anillo de claves se guarda en el fichero &lt;code&gt;/etc/apt/trusted.gpg&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;¿Qué contiene el archivo Release de un repositorio de paquetes?. ¿Y el archivo Release.gpg?. Puedes ver estos archivos en el repositorio http://ftp.debian.org/debian/dists/Debian10.1/. Estos archivos se descargan cuando hacemos un apt update.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;El archivo &lt;code&gt;Release&lt;/code&gt; contiene la lista de paquetes que contiene el repositorio de paquetes. El archivo &lt;code&gt;Release.gpg&lt;/code&gt; contiene la firma digital del archivo &lt;code&gt;Release&lt;/code&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Explica el proceso por el cual el sistema nos asegura que los ficheros que estamos descargando son legítimos.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;El sistema nos asegura que los ficheros que estamos descargando son legítimos mediante la verificación de la firma digital del fichero &lt;code&gt;Release&lt;/code&gt; que se encuentra en el repositorio de paquetes.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;añade de forma correcta el repositorio de virtualbox añadiendo la clave pública de virtualbox como se indica en la documentación.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Se hace con los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;deb https://download.virtualbox.org/virtualbox/debian buster contrib&quot; &amp;gt;&amp;gt; /etc/apt/sources.list
wget -q https://www.virtualbox.org/download/oracle\_vbox\_2016.asc -O- | apt-key add -
wget -q https://www.virtualbox.org/download/oracle\_vbox.asc -O- | apt-key add -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras esto, se puede instalar virtualbox con el comando &lt;code&gt;apt install virtualbox&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Tarea 5: Autentificación: ejemplo SSH&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Explica los pasos que se producen entre el cliente y el servidor para que el protocolo cifre la información que se transmite? ¿Para qué se utiliza la criptografía simétrica? ¿Y la asimétrica?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Los pasos que se producen entre el cliente y el servidor para que el protocolo cifre la información que se transmite son los siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Se lleva a cabo un &apos;handshake&apos; (apretón de manos) encriptado para que el cliente pueda verificar que se está comunicando con el servidor correcto&lt;/li&gt;
&lt;li&gt;La capa de transporte de la conexión entre el cliente y la máquina remota es encriptada mediante un código simétrico&lt;/li&gt;
&lt;li&gt;El cliente se autentica ante el servidor.&lt;/li&gt;
&lt;li&gt;El cliente remoto interactua con la máquina remota sobre la conexión encriptada.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La criptografía simétrica se utiliza para encriptar la información que se transmite entre el cliente y el servidor. La criptografía asimétrica se utiliza para autentificar al cliente y al servidor.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Explica los dos métodos principales de autentificación: por contraseña y utilizando un par de claves públicas y privadas.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Los dos métodos principales de autentificación son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Por contraseña: el cliente envía la contraseña al servidor para que este pueda autentificar al cliente.&lt;/li&gt;
&lt;li&gt;Por par de claves públicas y privadas: el cliente envía su clave pública al servidor para que este pueda autentificar al cliente. El cliente envía su clave privada al servidor para que este pueda autentificar al cliente.&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;En el cliente para que sirve el contenido que se guarda en el fichero ~/.ssh/know_hosts?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;El contenido que se guarda en el fichero ~/.ssh/know_hosts sirve para que el cliente pueda autentificar al servidor.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;¿Qué significa este mensaje que aparece la primera vez que nos conectamos a un servidor?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt; $ ssh debian@172.22.200.74
 The authenticity of host &apos;172.22.200.74 (172.22.200.74)&apos; can&apos;t be established.
 ECDSA key fingerprint is SHA256:7ZoNZPCbQTnDso1meVSNoKszn38ZwUI4i6saebbfL4M.
 Are you sure you want to continue connecting (yes/no)? 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Implica que la conexión es nueva y que el cliente no tiene guardada la clave pública del servidor. Por lo tanto, el cliente no puede autentificar al servidor. El cliente pregunta al usuario si quiere continuar con la conexión.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;En ocasiones cuando estamos trabajando en el cloud, y reutilizamos una ip flotante nos aparece este mensaje:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt; $ ssh debian@172.22.200.74
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 @    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
 IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
 Someone could be eavesdropping on you right now (man-in-the-middle attack)!
 It is also possible that a host key has just been changed.
 The fingerprint for the ECDSA key sent by the remote host is
 SHA256:W05RrybmcnJxD3fbwJOgSNNWATkVftsQl7EzfeKJgNc.
 Please contact your system administrator.
 Add correct host key in /home/jose/.ssh/known_hosts to get rid of this message.
 Offending ECDSA key in /home/jose/.ssh/known_hosts:103
   remove with:
   ssh-keygen -f &quot;/home/jose/.ssh/known_hosts&quot; -R &quot;172.22.200.74&quot;
 ECDSA host key for 172.22.200.74 has changed and you have requested strict checking.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Significa que la clave pública que teníamos asociada a esa dirección IP ha cambiado. Por lo tanto, el cliente no puede autentificar al servidor. Por lo que podría implicar  que se está suplantando su identidad. Si aún así queremos continuar, hay que ejecutar el comando &lt;code&gt;ssh-keygen -f &quot;/home/jose/.ssh/known_hosts&quot; -R &quot;172.22.200.74&quot;&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;¿Qué guardamos y para qué sirve el fichero en el servidor ~/.ssh/authorized_keys?&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;En ese fichero se guardan las claves públicas de los clientes que pueden acceder al servidor. Sirve para que el servidor pueda autentificar a los clientes.&lt;/p&gt;
</content:encoded></item><item><title>Redes Privadas Virtuales con WireGuard</title><link>https://www.robertops.com/posts/2023-01-26_vpn_wireguard/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-26_vpn_wireguard/</guid><pubDate>Thu, 26 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Caso C: VPN de acceso remoto con WireGuard&lt;/h2&gt;
&lt;p&gt;Monta una VPN de acceso remoto usando Wireguard. Intenta probarla con clientes Windows, Linux y Android. Documenta el proceso adecuadamente y compáralo con el del apartado A.&lt;/p&gt;
&lt;p&gt;El Escenario es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vob221j.png&quot; alt=&quot;escenario1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vamos a utilizar un VagrantFile igual al utilizado en el caso A para montar la infraestructura necesaria para la práctica:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&quot;2&quot;) do |config|

config.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true

  config.vm.define :cliente3 do |cliente3|
    cliente3.vm.box = &quot;debian/bullseye64&quot;
    cliente3.vm.hostname = &quot;cliente3&quot;
    cliente3.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-externa3&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.20&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :servidor3 do |servidor3|
    servidor3.vm.box = &quot;debian/bullseye64&quot;
    servidor3.vm.hostname = &quot;servidor3&quot;
    servidor3.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-externa3&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
    servidor3.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-interna3&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.1.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :maquina3 do |maquina3|
    maquina3.vm.box = &quot;debian/bullseye64&quot;
    maquina3.vm.hostname = &quot;maquina3&quot;
    maquina3.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-interna3&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.1.30&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a configurar las máquinas:&lt;/p&gt;
&lt;h3&gt;Servidor&lt;/h3&gt;
&lt;p&gt;Instalamos wireguard:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install wireguard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el bit de forwarding en el servidor editando el fichero &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; y descomentando la siguiente línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net.ipv4.ip_forward=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y hacemos los cambios efectivos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo sysctl -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a generar los pares de claves para el servidor (como usuario root):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wg genkey | tee /etc/wireguard/server_private.key | wg pubkey | tee /etc/wireguard/server_public.key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a crear el fichero de configuración del servidor en &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;,  utilizando las claves que hemos generado en el paso anterior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Interface]
Address = 10.99.99.1/24
ListenPort = 51820
PrivateKey = aB9gyJdOH835WVK4bqbb2VqrGeW5wFuOIyHFx7suu1w=


&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al tratarse de fichero sensibles, tienen que tener permisos de solo lectura, por lo que vamos a cambiar los permisos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo chmod -R 600 /etc/wireguard/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos la interfaz:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wg-quick up /etc/wireguard/wg0.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4avm7tC.png&quot; alt=&quot;wgup&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Cliente&lt;/h3&gt;
&lt;p&gt;Instalamos wireguard:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install wireguard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a generar los pares de claves para el cliente  (como usuario root):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wg genkey | tee /etc/wireguard/client_private.key | wg pubkey | tee /etc/wireguard/client_public.key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;hora vamos a crear el fichero de configuración del cliente en &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt;,  utilizando la clave privada generada en el paso anterior y la clave pública del servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Interface]
Address = 10.99.99.2/24
PrivateKey = 8DFSQc0qbT3P9Dhnbg44bxU+W2uRraXsebB+suQH+nQ=

[Peer]
PublicKey = voKHGdJUz8B6Q6jRYJspzhSWDLmXI4jroPc89VkMCHQ=
AllowedIPs = 0.0.0.0/0
Endpoint = 192.168.0.10:51820
PersistentKeepalive = 25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al tratarse de fichero sensibles,  tienen que tener permisos de solo lectura, por lo que vamos a cambiar los permisos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo chmod -R 600 /etc/wireguard/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos la interfaz:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wg-quick up /etc/wireguard/wg0.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/hsQTSjy.png&quot; alt=&quot;wgup2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una vez hecho esto, tenemos que añadir la clave pública del cliente al fichero de configuración del &lt;strong&gt;servidor&lt;/strong&gt;, añadiendo las siguentes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Peer]
PublicKey = eh7Ap8nkBQf5vL60sp2ORzqLtz2YWnbjABbTMrukaCo=
AllowedIPs = 10.99.99.2/32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y reiniciamos la interfaz:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wg-quick down /etc/wireguard/wg0.conf
sudo wg-quick up /etc/wireguard/wg0.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos ver el estado de la interfez con el comando &lt;code&gt;wg&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GIJCLAo.png&quot; alt=&quot;wg&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Rutas&lt;/h3&gt;
&lt;p&gt;Para que los mensajes que se envíen desde la máquina cliente a la máquina servidor, pasen por el túnel VPN, tenemos que cambiar la ruta por defecto para que sea a través del servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 10.99.99.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tenemos que cambiar la ruta por defecto también de la máquina interna para que sea a través del servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 192.168.1.10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Comprobación&lt;/h3&gt;
&lt;p&gt;Para comprobar que funciona, vamos a hacer un &lt;code&gt;traceroute&lt;/code&gt; desde la máquina cliente a la máquina servidor:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/RxFLZJh.png&quot; alt=&quot;traceroute1&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Caso D: VPN sitio a sitio con WireGuard&lt;/h2&gt;
&lt;p&gt;Configura una VPN sitio a sitio usando WireGuard. Documenta el proceso adecuadamente y compáralo con el del apartado B.&lt;/p&gt;
&lt;p&gt;El Escenario es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/VGD6hgl.png&quot; alt=&quot;escenario2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;El escenario es similar al del caso B por lo que utilizaremos el mismo vagrantfile:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&quot;2&quot;) do |config|

config.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true

  config.vm.define :maquina1 do |maquina1|
    maquina1.vm.box = &quot;debian/bullseye64&quot;
    maquina1.vm.hostname = &quot;maquina1&quot;
    maquina1.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna1&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.20&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :cliente do |cliente|
    cliente.vm.box = &quot;debian/bullseye64&quot;
    cliente.vm.hostname = &quot;cliente&quot;
    cliente.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna1&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
    cliente.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;internet&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;10.20.30.1&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :servidor do |servidor|
    servidor.vm.box = &quot;debian/bullseye64&quot;
    servidor.vm.hostname = &quot;servidor&quot;
    servidor.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;internet&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;10.20.30.2&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
    servidor.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna2&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;172.22.0.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.0.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :maquina2 do |maquina2|
    maquina2.vm.box = &quot;debian/bullseye64&quot;
    maquina2.vm.hostname = &quot;maquina2&quot;
    maquina2.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna2&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;172.22.0.20&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.0.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a configurar las máquinas:&lt;/p&gt;
&lt;h3&gt;Servidor&lt;/h3&gt;
&lt;p&gt;La configuración es similar a la del caso B, con la diferencia de que el fichero de configuración en &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; es el siguiente:&lt;/p&gt;
&lt;p&gt;Las claves son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;privada: &lt;code&gt;0N0kdHNHajtKY78rAGaI5uHzY8QGvZMCuiyw3WXU0n0=&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;pública: &lt;code&gt;SC7FuKTw2GnjJmJDmP0GuZGzgr9CBHUbcBOAXKKrCSU=&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[Interface]
Address = 10.99.99.1
ListenPort = 51820
PrivateKey = 0N0kdHNHajtKY78rAGaI5uHzY8QGvZMCuiyw3WXU0n0=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos la interfaz con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Cliente&lt;/h3&gt;
&lt;p&gt;La configuración es similar a la del caso B, con la diferencia de que el fichero de configuración en &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; es el siguiente:&lt;/p&gt;
&lt;p&gt;Las claves son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;privada: &lt;code&gt;OLKDIsseCywbzWOwME1gxzIZLhopGljCeyYgibrHwm0=&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;pública: &lt;code&gt;jwqTMNZ4lQkVk2OLrlMGZGDCkkCLJFmcUjlF7aPvrWc=&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;[Interface]
Address = 10.99.99.2
PrivateKey = OLKDIsseCywbzWOwME1gxzIZLhopGljCeyYgibrHwm0=
ListenPort = 51820

[Peer]
PublicKey = SC7FuKTw2GnjJmJDmP0GuZGzgr9CBHUbcBOAXKKrCSU=
AllowedIPs = 0.0.0.0/0
Endpoint = 10.20.30.2:51820
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos la interfaz con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el bit de forwarding en el servidor editando el fichero &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; y descomentando la siguiente línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net.ipv4.ip_forward=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y hacemos los cambios efectivos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo sysctl -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, como tenemos la clave pública del otro servidor, modificamos el fichero &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; del servidor para añadir la siguiente sección:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Peer]
Publickey = jwqTMNZ4lQkVk2OLrlMGZGDCkkCLJFmcUjlF7aPvrWc=
AllowedIPs = 0.0.0.0/0
PersistentKeepAlive = 25
Endpoint = 10.20.30.1:51820
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y reiniciamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wg-quick down wg0
sudo wg-quick up wg0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Máquinas&lt;/h3&gt;
&lt;p&gt;Ahora vamos a configurar las rutas en las maquinas internas:&lt;/p&gt;
&lt;p&gt;Máquina1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 192.168.0.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Máquina2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 172.22.0.10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Prueba de funcionamiento&lt;/h3&gt;
&lt;p&gt;traceroute desde maquina1 a maquina2:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/0HeraM7.png&quot; alt=&quot;traceroute2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;traceroute desde maquina2 a maquina1:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4WHGAH0.png&quot; alt=&quot;traceroute3&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Utilizando WireGuard la configuración es más sencilla de realizar y tiene menos ficheros, por lo que es más fácil de gestionar y de depurar errores. Aparte de esto, el funcionamiento es el mismo que el de OpenVPN.&lt;/p&gt;
</content:encoded></item><item><title>Introducción a OpenLDAP</title><link>https://www.robertops.com/posts/2023-01-25_introduccion_ldap/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-25_introduccion_ldap/</guid><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;El protocolo &lt;strong&gt;LDAP&lt;/strong&gt; es muy utilizado actualmente por empresa que apuestan por el software libre al utilizar distribuciones de Linux para ejercer las funciones propias de un &lt;strong&gt;directorio activo&lt;/strong&gt; en el que se gestionarán las credenciales y permisos de los trabajadores y estaciones de trabajo en redes LAN corporativas en conexiones cliente/servidor.&lt;/p&gt;
&lt;p&gt;Realiza la instalación y configuración básica de OpenLDAP en alfa,utilizando como base el nombre DNS asignado. Deberás crear un usuario llamado prueba y configurar una máquina cliente basada en Debian y Rocky para que pueda validarse en servidor ldap configurado anteriormente con el usuario prueba.&lt;/p&gt;
&lt;h2&gt;Servidor&lt;/h2&gt;
&lt;h3&gt;Instalación de OpenLDAP&lt;/h3&gt;
&lt;p&gt;Instalaremos OpenLDAP en el servidor alfa, para ello ejecutaremos los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt install slapd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Durante la instalación nos pedirá que introduzcamos la contraseña de administrador del directorio:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/NaNjKFA.png&quot; alt=&quot;Instalación de OpenLDAP&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una vez instalado con el comando &lt;code&gt;netstat -tulpn&lt;/code&gt; comprobaremos que el servicio está escuchando en el puerto 389:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/KGi2Vz6.png&quot; alt=&quot;Instalación de OpenLDAP&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, utilizando el binario &lt;code&gt;ldapsearch&lt;/code&gt; incluido en el paquete &lt;code&gt;ldap-utils&lt;/code&gt; podemos buscar sobre el directorio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapsearch -x -b &quot;dc=roberto,dc=gonzalonazareno,dc=org&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FgmXMPY.png&quot; alt=&quot;ldapsearch1&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración de OpenLDAP&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qRgLvAz.png&quot; alt=&quot;esquemaLDAP&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para lograr una mejor estructura, la información suele organizarse en forma de &lt;strong&gt;ramas&lt;/strong&gt; de las que cuelgan objetos similares (por ejemplo, una rama para usuarios y otra para grupos). Organizar de esta manera la estructura nos aporta también una mayor agilidad en las búsquedas, así como una gestión más eficiente sobre los permisos. Cada rama se denomina &lt;strong&gt;organizational unit&lt;/strong&gt; (OU) y cada objeto que cuelga de ella se denomina &lt;strong&gt;entry&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;ara definir dichos objetos, haremos uso de un fichero con extensión &lt;code&gt;.ldif&lt;/code&gt;, en este caso he creado el fichero &lt;code&gt;unidades.ldif&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dn: ou=Personas,dc=roberto,dc=gonzalonazareno,dc=org
objectClass: organizationalUnit
ou: Personas 

dn: ou=Grupos,dc=roberto,dc=gonzalonazareno,dc=org
objectClass: organizationalUnit
ou: Grupos
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para activar el fichero de configuración, ejecutaremos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapadd -x -D &quot;cn=admin,dc=roberto,dc=gonzalonazareno,dc=org&quot; -f unidades.ldif -W
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/QlLeqHB.png&quot; alt=&quot;ldapadd&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, podemos comprobar que se ha creado las ramas &lt;code&gt;Personas&lt;/code&gt; y &lt;code&gt;Grupos&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapsearch -x -b &quot;dc=roberto,dc=gonzalonazareno,dc=org&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ypKuRlq.png&quot; alt=&quot;ldapsearch2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Podemos borrar los objetos creados con el comando &lt;code&gt;ldapdelete&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapdelete -x -D &apos;cn=admin,dc=roberto,dc=gonzalonazareno,dc=org&apos; -W ou=Personas,dc=roberto,dc=gonzalonazareno,dc=org
ldapdelete -x -D &apos;cn=admin,dc=roberto,dc=gonzalonazareno,dc=org&apos; -W ou=Grupos,dc=roberto,dc=gonzalonazareno,dc=org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a crear un grupo llamado prueba en el fichero &lt;code&gt;grupos.ldif&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dn: cn=prueba,ou=Grupos,dc=roberto,dc=gonzalonazareno,dc=org
objectClass: posixGroup
gidNumber: 2001
cn: prueba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para activar el fichero de configuración, ejecutaremos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapadd -x -D &apos;cn=admin,dc=roberto,dc=gonzalonazareno,dc=org&apos; -W -f grupos.ldif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ejecutando el comando &lt;code&gt;ldapsearch&lt;/code&gt; podemos comprobar que se ha creado el grupo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/MZxmh26.png&quot; alt=&quot;ldapadd2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Creación del usuario&lt;/h3&gt;
&lt;p&gt;Ahora vamos a crear un usuario llamado &lt;em&gt;prueba&lt;/em&gt;. Antes de crearlo, vamos a ejecutar el comando &lt;code&gt;slappasswd&lt;/code&gt; para crear una contraseña cifrada para el usuario:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/RrnHM66.png&quot; alt=&quot;slappasswd&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, vamos a crear el usuario en el fichero &lt;code&gt;usuarios.ldif&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dn: uid=prueba,ou=Personas,dc=roberto,dc=gonzalonazareno,dc=org
objectClass: posixAccount
objectClass: inetOrgPerson
objectClass: person
cn: prueba
uid: prueba
uidNumber: 2001
gidNumber: 2001
homeDirectory: /nfs/prueba
loginShell: /bin/bash
userPassword: {SSHA}sDPbVb9gQ37YaNSg5nPyIe776dmlU2bq
sn: prueba
mail: prueba@gmail.com
givenName: prueba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para activar el fichero de configuración, ejecutaremos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapadd -x -D &apos;cn=admin,dc=roberto,dc=gonzalonazareno,dc=org&apos; -W -f usuarios.ldif
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ejecutando el comando &lt;code&gt;ldapsearch&lt;/code&gt; podemos comprobar que se ha creado el usuario:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/9pq9gxy.png&quot; alt=&quot;ldapadd3&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración de NFS&lt;/h3&gt;
&lt;p&gt;Ahora vamos a configurar el servidor NFS para que pueda compartir el directorio &lt;code&gt;/nfs&lt;/code&gt; con los clientes.&lt;/p&gt;
&lt;p&gt;Primero vamos a crear el directorio &lt;code&gt;/nfs/prueba&lt;/code&gt; y le vamos a dar permisos al usuario &lt;code&gt;prueba&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir /nfs/prueba
chown 2001:2001 /nfs/prueba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a editar el fichero &lt;code&gt;/etc/exports&lt;/code&gt; y añadir la siguiente línea, que permite que el usuario &lt;code&gt;prueba&lt;/code&gt; pueda acceder al directorio &lt;code&gt;/nfs/prueba&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/nfs       *(rw,fsid=0,subtree_check)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a reiniciar el servicio NFS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart nfs-server
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración final del servidor LDAP&lt;/h3&gt;
&lt;p&gt;Ahora vamos a configurar el servidor LDAP para que sea capaz de resolver nombres de grupos y de usuarios, consultar información a un directorio LDAP, identificarse o cachear la resolución de nombres.&lt;/p&gt;
&lt;p&gt;Para ello, instalamos los siguientes paquetes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install libpam-ldapd nscd libnss-ldap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Durante la instalación, dejamos los valores por defecto menos en las siguientes preguntas:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/9xawQHF.png&quot; alt=&quot;ldapinstall&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/mnPntoB.png&quot; alt=&quot;ldapinstall2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Como se indica al final de la instalación, vamos a editar el fichero &lt;code&gt;/etc/nsswitch.conf&lt;/code&gt; y añadir las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;passwd:         files ldap
group:          files ldap
shadow:         files ldap
gshadow:        files ldap

hosts:          files dns mymachines
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reiniciamos el servicio &lt;code&gt;nscd&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart nscd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora con el comando &lt;code&gt;id prueba&lt;/code&gt; podemos comprobar que se resuelven los nombres:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ucsbVhG.png&quot; alt=&quot;id&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finalmente podemos iniciar sesión con el comando &lt;code&gt;login prueba&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/zxXslPM.png&quot; alt=&quot;login&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Cliente Ubuntu&lt;/h2&gt;
&lt;p&gt;Instalamos el siguiente paquete:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install ldap-utils
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a editar el fichero &lt;code&gt;/etc/ldap/ldap.conf&lt;/code&gt; y añadir las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;BASE dc=roberto,dc=gonzalonazareno,dc=org
URI ldap://alfa.roberto.gonzalonazareno.org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después de esto, con el siguiente comando comprobamos que funciona correctamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ldapsearch -x -b &quot;dc=roberto,dc=gonzalonazareno,dc=org&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FKUMW0Q.png&quot; alt=&quot;ldapsearch&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora vamos a instalar los paquetes para las resoluciones:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install libnss-ldap libpam-ldapd nscd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La instalación es similar a la del sevidor, dejando valores por defecto y cambiando los mismos, además de añadir un usuario sin privilegios.&lt;/p&gt;
&lt;p&gt;Como se indica al final de la instalación, vamos a editar el fichero &lt;code&gt;/etc/nsswitch.conf&lt;/code&gt; y añadir las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;passwd:         files systemd ldap
group:          files systemd ldap
shadow:         files ldap 
gshadow:        files ldap

hosts:          files dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

netgroup:       nis
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reiniciamos el servicio &lt;code&gt;nscd&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart nscd
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;NFS&lt;/h3&gt;
&lt;p&gt;Ahora vamos a instalar el paquete &lt;code&gt;nfs-common&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install nfs-common
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el servicio&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl start nfs-client.target &amp;amp; systemctl enable nfs-client.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a crear los directorios que vamos a montar:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /home/nfs/prueba
chown 2001:2001 /home/nfs/prueba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a montar la carpeta mediante NFS. Primero, cargamos el módulo&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;modprobe nfs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con la siguiente línea se carga automáticamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo NFS | tee -a /etc/modules
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y vamos a hacer un montaje mediante SystemD a través del fichero &lt;code&gt;/etc/systemd/system/home-nfs.mount&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=script de montaje NFS
Requires=network-online.target
After=network-online.target
[Mount]
What=192.168.0.1:/nfs
Where=/home/nfs
Options=_netdev,auto
Type=nfs
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y lo activamos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl daemon-reload
systemctl start home-nfs.mount
systemctl enable home-nfs.mount
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras esto, ya podremos entrar correctamente con &lt;code&gt;login prueba&lt;/code&gt;. Podemos comprobar como los ficheros creados en el servidor se ven reflejados en el cliente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/J1lCNBZ.png&quot; alt=&quot;nfs&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Redes Privadas Virtuales con OpenVPN</title><link>https://www.robertops.com/posts/2023-01-25_vpn/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-25_vpn/</guid><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Caso A: VPN de acceso remoto con OpenVPN y certificados x509&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Uno de los dos equipos (el que actuará como servidor) estará conectado a dos redes&lt;/li&gt;
&lt;li&gt;Para la autenticación de los extremos se usarán obligatoriamente certificados digitales, que se generarán utilizando openssl y se almacenarán en el directorio &lt;code&gt;/etc/openvpn&lt;/code&gt;, junto con  los parámetros Diffie-Helman y el certificado de la propia Autoridad de Certificación. * Se utilizarán direcciones de la red &lt;code&gt;10.99.99.0/24&lt;/code&gt; para las direcciones virtuales de la VPN. La dirección &lt;code&gt;10.99.99.1&lt;/code&gt; se asignará al servidor VPN.&lt;/li&gt;
&lt;li&gt;Los ficheros de configuración del servidor y del cliente se crearán en el directorio /etc/openvpn de cada máquina, y se llamarán servidor.conf y cliente.conf respectivamente.&lt;/li&gt;
&lt;li&gt;Tras el establecimiento de la VPN, la máquina cliente debe ser capaz de acceder a una máquina que esté en la otra red a la que está conectado el servidor.
Documenta el proceso detalladamente.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El Escenario es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/vob221j.png&quot; alt=&quot;escenario1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vamos a generar dicho en Vagrant utilizando el siguiente Vagrantfile:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&quot;2&quot;) do |config|

config.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true

  config.vm.define :cliente do |cliente|
    cliente.vm.box = &quot;debian/bullseye64&quot;
    cliente.vm.hostname = &quot;cliente&quot;
    cliente.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-externa&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.20&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :servidor do |servidor|
    servidor.vm.box = &quot;debian/bullseye64&quot;
    servidor.vm.hostname = &quot;servidor&quot;
    servidor.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-externa&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
    servidor.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-interna&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.1.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :maquina do |maquina|
    maquina.vm.box = &quot;debian/bullseye64&quot;
    maquina.vm.hostname = &quot;maquina&quot;
    maquina.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;red-interna&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.1.30&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a configurar las máquinas:&lt;/p&gt;
&lt;h3&gt;Configuración de la máquina servidor&lt;/h3&gt;
&lt;p&gt;Vamos a instalar OpenVPN en la máquina servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt-get update
apt-get install openvpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el bit de forwarding en el servidor editando el fichero &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; y descomentando la siguiente línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net.ipv4.ip_forward=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y hacemos los cambios efectivos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo sysctl -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Como indica el enunciado, hay que copiar los ficheros del directorio &lt;code&gt;/usr/easy-rsa/&lt;/code&gt; al directorio &lt;code&gt;/etc/openvpn/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cp -r /usr/share/easy-rsa /etc/openvpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ejecutamos el script de infraestructura de clave pública (PKI) para generar los certificados:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /etc/openvpn/easy-rsa
sudo ./easyrsa init-pki
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora podemos generar la clave privada:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ./easyrsa build-ca
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/HnrLwoR.png&quot; alt=&quot;builca&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Se guardarán en el directorio &lt;code&gt;/etc/openvpn/easy-rsa/pki/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ahora generamos el certificado del servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ./easyrsa build-server-full server nopass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JWiael9.png&quot; alt=&quot;buildserver&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora vamos a generar los certificados diffie-helman (este proceso puede tardar varios minutos):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ./easyrsa gen-dh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Por último, vamos a generar el certificado y la clave para la máquina cliente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ./easyrsa build-client-full cliente nopass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/RRTumPA.png&quot; alt=&quot;buildclient&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Como indica el comando, la clave se encuentra en &lt;code&gt;/etc/openvpn/easy-rsa/pki/private/cliente.key&lt;/code&gt;y el certificado en &lt;code&gt;/etc/openvpn/easy-rsa/pki/issued/cliente.crt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ahora copiamos los ficheros del cliente a la máquina cliente:&lt;/p&gt;
&lt;p&gt;primero los movemos al directorio home:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo cp /etc/openvpn/easy-rsa/pki/ca.crt ~
sudo mv /etc/openvpn/easy-rsa/pki/issued/cliente.crt ~
sudo mv /etc/openvpn/easy-rsa/pki/private/cliente.key ~
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cambiamos el propietario de los ficheros (aprovechando que los tres ficheros empiezan por c*):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo chown vagrant: c*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y ahora los copiamos a la máquina  (aprovechando también que los tres ficheros empiezan por c* para ahorrar código):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scp c* vagrant@cliente:/home/vagrant
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/04lHcb0.png&quot; alt=&quot;scp&quot; /&gt;&lt;/p&gt;
&lt;p&gt;utilizando como plantilla el fichero que se encuentra en &lt;code&gt;/usr/share/doc/openvpn/examples/sample-config-files/server.conf&lt;/code&gt;, creamos el fichero de configuración del servidor en &lt;code&gt;/etc/openvpn/server/servidor.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;port 1194
proto udp
dev tun

ca /etc/openvpn/easy-rsa/pki/ca.crt
cert /etc/openvpn/easy-rsa/pki/issued/server.crt
key /etc/openvpn/easy-rsa/pki/private/server.key
dh /etc/openvpn/easy-rsa/pki/dh.pem

topology subnet

# DIRECCIONAMIENTO PARA EL TÚNEL
#
# El servidor será: 10.99.99.1

server 10.99.99.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt

push &quot;route 192.168.1.0 255.255.255.0&quot;

keepalive 10 120
cipher AES-256-CBC
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
verb 3
explicit-exit-notify 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora activamos el servicio de OpenVPN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable --now openvpn-server@servidor
sudo systemctl status openvpn-server@servidor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/S58gXhP.png&quot; alt=&quot;openvpnserver&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración de la máquina cliente&lt;/h3&gt;
&lt;p&gt;Ahora vamos a configurar la máquina cliente. Primero instalamos OpenVPN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install openvpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Movemos los ficheros que hemos copiado de la máquina servidor a la máquina cliente a la ruta &lt;code&gt;/etc/openvpn/client/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mv c* /etc/openvpn/client/
sudo chown root: /etc/openvpn/client/*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al igual que con el servidor, tomando de ejemplo el fichero &lt;code&gt;/usr/share/doc/openvpn/examples/sample-config-files/client.conf&lt;/code&gt;, creamos el fichero de configuración del cliente en &lt;code&gt;/etc/openvpn/client/cliente.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;client
dev tun
proto udp
´
remote 192.168.0.10 1194
resolv-retry infinite
nobind

persist-key
persist-tun

ca /etc/openvpn/client/ca.crt
cert /etc/openvpn/client/cliente.crt
key /etc/openvpn/client/cliente.key

remote-cert-tls server
cipher AES-256-CBC
verb 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora activamos el servicio de OpenVPN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable --now openvpn-client@cliente
sudo systemctl status openvpn-client@cliente
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ErQ7bE9.png&quot; alt=&quot;openvpnclient&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración de la máquina interna&lt;/h3&gt;
&lt;p&gt;Tenemos que cambiar la ruta por defecto también de la máquina interna para que sea a través del servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 192.168.1.10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Comprobación de funcionamiento&lt;/h3&gt;
&lt;p&gt;Ahora vamos a comprobar que funciona el túnel. En la máquina cliente, vamos a hacer un traceroute a la máquina de la red interna (de ip &lt;code&gt;192.168.1.30&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;traceroute 192.168.1.30
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/mBLm0VW.png&quot; alt=&quot;traceroute1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Como podemos ver, el primer salto es a la máquina servidor utilizando el túnel VPN del servidor, y el segundo salto es a la máquina de la red interna.&lt;/p&gt;
&lt;p&gt;Podemos incluso, realizar una conexión SSH a la máquina de la red interna:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pnaKGtg.png&quot; alt=&quot;ssh1&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Caso B: VPN sitio a sitio con OpenVPN y certificados x509&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Cada equipo estará conectado a dos redes, una de ellas en común
&lt;ul&gt;
&lt;li&gt;Para la autenticación de los extremos se usarán obligatoriamente certificados digitales, que se generarán utilizando openssl y se almacenarán en el directorio /etc/openvpn, junto con con los parámetros Diffie-Helman y el certificado de la propia Autoridad de Certificación.&lt;/li&gt;
&lt;li&gt;Se utilizarán direcciones de la red 10.99.99.0/24 para las direcciones virtuales de la VPN.&lt;/li&gt;
&lt;li&gt;Tras el establecimiento de la VPN, una máquina de cada red detrás de cada servidor VPN debe ser capaz de acceder a una máquina del otro extremo.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El Escenario es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/VGD6hgl.png&quot; alt=&quot;escenario2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Vamos a generar dicho en Vagrant utilizando el siguiente Vagrantfile:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&quot;2&quot;) do |config|

config.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true

  config.vm.define :maquina1 do |maquina1|
    maquina1.vm.box = &quot;debian/bullseye64&quot;
    maquina1.vm.hostname = &quot;maquina1&quot;
    maquina1.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna1&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.20&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :cliente do |cliente|
    cliente.vm.box = &quot;debian/bullseye64&quot;
    cliente.vm.hostname = &quot;cliente&quot;
    cliente.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna1&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;192.168.0.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
    cliente.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;internet&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;10.20.30.1&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :servidor do |servidor|
    servidor.vm.box = &quot;debian/bullseye64&quot;
    servidor.vm.hostname = &quot;servidor&quot;
    servidor.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;internet&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;10.20.30.2&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.255.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
    servidor.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna2&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;172.22.0.10&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.0.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end

  config.vm.define :maquina2 do |maquina2|
    maquina2.vm.box = &quot;debian/bullseye64&quot;
    maquina2.vm.hostname = &quot;maquina2&quot;
    maquina2.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;interna2&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;172.22.0.20&quot;,
      :libvirt__netmask =&amp;gt; &apos;255.255.0.0&apos;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora configuraremos las máquinas;&lt;/p&gt;
&lt;h3&gt;Configuración del servidor&lt;/h3&gt;
&lt;p&gt;La configuración es exactamente igual que en el caso anterior, por lo que tenemos que replicar la misma configuración (incluyendo copiar los ficheros al cliente) hasta el momento de editar el fichero &lt;code&gt;/etc/openvpn/server/servidor.conf&lt;/code&gt;, donde empieza la configuración diferente.&lt;/p&gt;
&lt;p&gt;Ahora creamos el fichero &lt;code&gt;/etc/openvpn/server/servidor.conf&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dev tun
ifconfig 10.99.99.1 10.99.99.2
route 192.168.0.0 255.255.255.0
tls-server

dh /etc/openvpn/easy-rsa/pki/dh.pem
ca /etc/openvpn/easy-rsa/pki/ca.crt
cert /etc/openvpn/easy-rsa/pki/issued/server.crt
key /etc/openvpn/easy-rsa/pki/private/server.key

comp-lzo
keepalive 10 60
log /var/log/openvpn/prueba.log

verb 3
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable --now openvpn-server@servidor
sudo systemctl status openvpn-server@servidor
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/JFIpjzj.png&quot; alt=&quot;servidor2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración del cliente&lt;/h3&gt;
&lt;p&gt;Ahora vamos a configurar la máquina cliente. Primero instalamos OpenVPN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install openvpn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el bit de forwarding en el servidor editando el fichero &lt;code&gt;/etc/sysctl.conf&lt;/code&gt; y descomentando la siguiente línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net.ipv4.ip_forward=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y hacemos los cambios efectivos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo sysctl -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Movemos los ficheros que hemos copiado de la máquina servidor a la máquina cliente a la ruta &lt;code&gt;/etc/openvpn/client/&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mv c* /etc/openvpn/client/
sudo chown root: /etc/openvpn/client/*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Al igual que con el servidor, tomando de ejemplo el fichero &lt;code&gt;/usr/share/doc/openvpn/examples/sample-config-files/client.conf&lt;/code&gt;, creamos el fichero de configuración del cliente en &lt;code&gt;/etc/openvpn/client/cliente.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dev tun
remote 10.20.30.2
ifconfig 10.99.99.2 10.99.99.1
route 172.22.0.0 255.255.0.0
tls-client
ca /etc/openvpn/client/ca.crt
cert /etc/openvpn/client/cliente.crt
key /etc/openvpn/client/cliente.key
comp-lzo
keepalive 10 60
log /var/log/openvpn/cliente.log

verb 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora activamos el servicio de OpenVPN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable --now openvpn-client@cliente
sudo systemctl status openvpn-client@cliente
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DuigNzf.png&quot; alt=&quot;cliente2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuramos las máquinas internas&lt;/h3&gt;
&lt;p&gt;Ahora vamos a configurar las rutas en las maquinas internas:&lt;/p&gt;
&lt;p&gt;Máquina1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 192.168.0.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Máquina2:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ip route del default
sudo ip route add default via 172.22.0.10
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Pruebas de funcionamiento&lt;/h3&gt;
&lt;p&gt;traceroute desde maquina1 a maquina2:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xGemQ2V.png&quot; alt=&quot;traceroute2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;traceroute desde maquina2 a maquina1:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/g5BS0qP.png&quot; alt=&quot;traceroute3&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Despliegue de aplicaciones de java usando tomcat</title><link>https://www.robertops.com/posts/2023-01-24_despliegue_java/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-24_despliegue_java/</guid><pubDate>Tue, 24 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Tarea 1 Desarrollo y despliegue de una aplicación Java simple&lt;/h2&gt;
&lt;p&gt;De forma similar a lo que hemos hecho en el Taller 1, desplegamos de forma manual la aplicación Java que encontramos en el repositorio &lt;a href=&quot;https://github.com/josedom24/rock-paper-scissors&quot;&gt;https://github.com/josedom24/rock-paper-scissors&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Una vez tenemos la carpeta, vamos a compilarla con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mvn package
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez compilada, vamos a desplegarla en el servidor Tomcat. Para ello, copiamos el fichero war que se ha generado en la carpeta target en la carpeta webapps del servidor Tomcat. Una vez copiado, reiniciamos el servidor Tomcat.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cp roshambo.war /var/lib/tomcat9/webapps/
systemctl restart tomcat9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OpvZ5tu.png&quot; alt=&quot;rock-paper-scissors&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Tarea 2 Despliegue de un CMS Java&lt;/h2&gt;
&lt;h3&gt;Instalación de OpenCMS&lt;/h3&gt;
&lt;p&gt;Para instalar OpenCMS, vamos a descargar el fichero war de la página de descargas de OpenCMS. Una vez descargado, lo copiamos en la carpeta webapps de Tomcat.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scp -r opencms.war debian@172.22.200.19:/home/debian/
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;cp opencms.war /var/lib/tomcat9/webapps/
systemctl restart tomcat9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instalamos la base de datos MariaDB y creamos la base de datos opencms.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install mariadb-server
sudo mysql -u root -p
CREATE DATABASE opencms;
CREATE USER &apos;java&apos;@&apos;localhost&apos; IDENTIFIED BY &apos;java&apos;;
grant all privileges on opencms. * to &apos;java&apos;@&apos;localhost&apos; with grant option;
flush privileges;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez instalado, accedemos a la aplicación con la url &lt;a href=&quot;http://172.22.200.19:8080/opencms/setup&quot;&gt;http://172.22.200.19:8080/opencms/setup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Seguimos los pasos de la instalación, indicando la base de datos que hemos creado y el usuario java/java.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/f96vUjU.png&quot; alt=&quot;opencms&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/xlAvYTT.png&quot; alt=&quot;opencms2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/e6qvEXP.png&quot; alt=&quot;opencms3&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Tarea 3 Acceso a las aplicaciones&lt;/h2&gt;
&lt;p&gt;Instalamos nginx para realizar un proxy inverso para acceder a las aplicaciones.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y creamos el siguiente virtualhost en &lt;code&gt;/etc/nginx/sites-available/opencms.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen 80;
    listen [::]:80;

    index index.html index.htm index.nginx-debian.html;

    server_name java.roberto.org;

    location / {
        return 301 http://$host/opencms/;
    }

    location /opencms/ {
        proxy_pass http://localhost:8080;
        include proxy_params;
    }

    location /game/ {
        proxy_pass http://localhost:8080/roshambo/;
        include proxy_params;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y lo activamos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ln -s /etc/nginx/sites-available/opencms.conf /etc/nginx/sites-enabled/
sudo systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez hecho podemos acceder a las aplicaciones con las siguientes urls:&lt;/p&gt;
&lt;p&gt;piedra papel tijera -&amp;gt; &lt;a href=&quot;http://java.roberto.org/game/&quot;&gt;http://java.roberto.org/game/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/C9svFA4.png&quot; alt=&quot;piedra&quot; /&gt;&lt;/p&gt;
&lt;p&gt;opencms -&amp;gt; &lt;a href=&quot;http://java.roberto.org/&quot;&gt;http://java.roberto.org/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/QAkYCko.png&quot; alt=&quot;opencms5&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Despliegue de aplicaciones python sobre django</title><link>https://www.robertops.com/posts/2023-01-23_despliegue_django/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-23_despliegue_django/</guid><pubDate>Mon, 23 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Tarea 1 Entorno de desarrollo&lt;/h2&gt;
&lt;p&gt;He elegido como entorno de desarrollo la máquina bravo.&lt;/p&gt;
&lt;p&gt;Primero creamos un entorno virtual de python3 e instalamos las dependencias necesarias para que funcione el proyecto.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m venv django 
source django/bin/activate
pip install django
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora descargamos el proyecto de github:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/robertorodriguez98/django_tutorial
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración de django&lt;/h3&gt;
&lt;p&gt;Comprobamos que vamos a trabajar con una base de datos sqlite. Para ello tenemos que consultar el fichero settings.py. En este caso la base de datos se llama db.sqlite3.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ cd django_tutorial
(django)$ cat django_tutorial/settings.py | egrep -A 5 &apos;DATABASES&apos;;
DATABASES = {
    &apos;default&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.sqlite3&apos;,
        &apos;NAME&apos;: BASE_DIR / &apos;db.sqlite3&apos;,
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para poder acceder desde la dirección www.roberto.gonzalonazareno.org, tenemos que modificar el fichero &lt;code&gt;django_tutorial/settings.py&lt;/code&gt; y añadir la dirección a la lista de hosts permitidos de la siguiente manera:.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALLOWED_HOSTS = [&apos;www.roberto.gonzalonazareno.org&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creamos la base de datos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y el usuario administrador:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ python3 manage.py createsuperuser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora ejecutamos el servidor web de desarrollo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ python3 manage.py runserver 0.0.0.0:8000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y accedemos a la zona de administración, en este caso en la dirección &lt;a&gt;www.roberto.gonzalonazareno.org:8000/admin&lt;/a&gt; y añadimos dos preguntas con sus posibles respuestas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DZW2zzb.png&quot; alt=&quot;Zona de administración&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comprueba en el navegador que la aplicación está funcionando, accede a la url /polls.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/p4ZeXNj.png&quot; alt=&quot;Página principal&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Bq4Tpml.png&quot; alt=&quot;Página de votación1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DWNxLFr.png&quot; alt=&quot;Página de votación2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración del servidor web&lt;/h3&gt;
&lt;p&gt;En bravo, el servidor web apache2 ya está instalado y configurado para servir páginas web estáticas. Para servir páginas web dinámicas, tenemos que instalar el módulo wsgi.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dnf install python3-mod_wsgi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, para que el entorno sea similar al de producción, movemos el proyecto y el entorno virtual a la ruta &lt;code&gt;/var/www/html/django_tutorial&lt;/code&gt; y &lt;code&gt;/var/www/html/django&lt;/code&gt; respectivamente.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mv django_tutorial /var/www/html/
sudo mv django /var/www/html/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos el contenido estático de la aplicación:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo python3 manage.py collectstatic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Editamos el fichero &lt;code&gt;/var/www/html/django_tutorial/django_tutorial/settings.py&lt;/code&gt; y modificamos las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ALLOWED_HOSTS = [&apos;*&apos;]
STATIC_ROOT = &apos;/var/www/html/django_tutorial/static/&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora configuraremos el servidor apache para que sirva la aplicación django. Para ello, tenemos que crear un fichero de configuración en la ruta &lt;code&gt;/etc/httpd/sites-available/django.conf&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost python.roberto.gonzalonazareno.org:80&amp;gt;
    ServerName python.roberto.gonzalonazareno.org
    DocumentRoot /var/www/html/django_tutorial

    Alias /static/ /var/www/html/django_tutorial/static/

    WSGIDaemonProcess django_tutorial python-path=/var/www/html/django_tutorial:/var/www/html/django/lib/python3.9/site-packages
    WSGIProcessGroup django_tutorial
    WSGIScriptAlias / /var/www/html/django_tutorial/django_tutorial/wsgi.py

    ErrorLog /var/log/httpd/django_tutorial_error.log
    CustomLog /var/log/httpd/django_tutorial_access.log combined
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para que funcione la configuración tenemos que activar el sitio web y reiniciar el servidor web:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ln -s /etc/httpd/sites-available/django.conf /etc/httpd/sites-enabled/django.conf
sudo systemctl restart httpd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora podemos acceder a la aplicación desde la dirección &lt;a href=&quot;http://python.roberto.gonzalonazareno.org&quot;&gt;python.roberto.gonzalonazareno.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Tarea 2: Entorno de producción&lt;/h2&gt;
&lt;p&gt;Vamos a realizar el despliegue de nuestra aplicación en un entorno de producción, para ello vamos a utilizar nuestro VPS, sigue los siguientes pasos:&lt;/p&gt;
&lt;h3&gt;Instalación de django y base de datos&lt;/h3&gt;
&lt;p&gt;Clonamos el repositorio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/robertorodriguez98/django_tutorial
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos el entorno virtual:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m venv django 
source django/bin/activate
cd django_tutorial
pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instalamos el módulo que permite que python trabaje con mysql:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install libmariadb-dev
pip install mysqlclient
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras crear un usuario y la base de datos,  configuramos la aplicación para trabajar con mysql, para ello modifica la configuración de la base de datos en el archivo &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DATABASES = {
    &apos;default&apos;: {
        &apos;ENGINE&apos;: &apos;django.db.backends.mysql&apos;,
        &apos;NAME&apos;: &apos;django&apos;,
        &apos;USER&apos;: &apos;djadmin&apos;,
        &apos;PASSWORD&apos;: &apos;djadmin&apos;,
        &apos;HOST&apos;: &apos;localhost&apos;,
        &apos;PORT&apos;: &apos;&apos;,
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos la copia de seguridad de la aplicación en bravo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo python manage.py dumpdata &amp;gt; /home/roberto/basedatos.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y la restauramos en el VPS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ python manage.py migrate
(django)$ python manage.py loaddata basedatos.json
Installed 57 object(s) from 1 fixture(s)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente generamos el contenido estático:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ python manage.py collectstatic
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración de nginx&lt;/h3&gt;
&lt;p&gt;Instalamos uwsgi:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install uwsgi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos comprobar que funciona correctamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uwsgi --http :8080 --chdir /home/calcetines/django_tutorial --wsgi-file django_tutorial/wsgi.py --process 4 --threads 2 --master 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos el fichero de configuración de uwsgi en &lt;code&gt;/home/calcetines/django/servidor.ini&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[uwsgi]
http = :8080
chdir = /home/calcetines/django_tutorial 
wsgi-file = django_tutorial/wsgi.py
processes = 4
threads = 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora crearemos la unidad de systemd para que se ejecute uwsgi como servicio en el fichero &lt;code&gt;/etc/systemd/system/uwsgi-django.service&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=uwsgi-django
After=network.target

[Install]
WantedBy=multi-user.target

[Service]
User=www-data
Group=www-data
Restart=always

ExecStart=/home/calcetines/django/bin/uwsgi /home/calcetines/django/servidor.ini
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

WorkingDirectory=/home/calcetines/django_tutorial
Environment=PYTHONPATH=&apos;/home/calcetines/django_tutorial:/home/calcetines/django/lib/python3.9/site-packages&apos;

PrivateTmp=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;activamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl enable uwsgi-django.service
systemctl start uwsgi-django.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora configuramos nginx como proxy inverso añadiendo la siguiente configuración en el fichero &lt;code&gt;/etc/nginx/sites-available/django&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen          80;
    server_name     python.admichin.es;
    
    if ($host ~ ^[^.]+\.admichin\.es$) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

}

server {
    listen          443 ssl;
    server_name     python.admichin.es;
    access_log      /var/log/nginx/example.com_access.log combined;
    error_log       /var/log/nginx/example.com_error.log error;

        ssl_certificate /etc/letsencrypt/live/admichin.es-0001/fullchain.pem; # managed by Certbot
        ssl_certificate_key /etc/letsencrypt/live/admichin.es-0001/privkey.pem; # managed by Certbot

    location /static/ {
        root /home/calcetines/django_tutorial;
    }

    location / {
        proxy_pass         http://localhost:8080/;
        include proxy_params;
        proxy_redirect     off;

        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Desactivamos el modo debug en el fichero &lt;code&gt;settings.py&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DEBUG = False
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente accedemos a la página web en &lt;a href=&quot;https://python.admichin.es&quot;&gt;python.admichin.es&lt;/a&gt; y comprobamos que funciona correctamente.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/ULmiDVn.png&quot; alt=&quot;base&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/c77oBhu.png&quot; alt=&quot;admin&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/TnfrXlx.png&quot; alt=&quot;polls&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Tarea 3: Modificación de nuestra aplicación&lt;/h2&gt;
&lt;h3&gt;En el entorno de desarrollo&lt;/h3&gt;
&lt;p&gt;Vamos a hacer las siguientes modificaciones:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Modificamos el fichero &lt;code&gt;django_tutorial/polls/templates/polls/index.html&lt;/code&gt; para que aparezca nuestro nombre.&lt;/li&gt;
&lt;li&gt;Modificamos la imagen de fondo que se ve la aplicación. Para hacerlo debemos modificar el fichero &lt;code&gt;django_tutorial/polls/static/polls/css/style.css&lt;/code&gt; y cambiar la siguiente línea, estando la imagen en la carpeta &lt;code&gt;django_tutorial/polls/static/polls/images&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;    background: white url(&quot;images/gundam.jpg&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a crear una nueva tabla en la base de datos para almacenar las categorías de las preguntas. Para ello sigue los siguientes pasos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Se Añade un nuevo modelo al fichero &lt;code&gt;django_tutorial/polls/models.py&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;class Categoria(models.Model):	
    Abr = models.CharField(max_length=4)
    Nombre = models.CharField(max_length=50)

    def __str__(self):
        return self.Abr+&quot; - &quot;+self.Nombre
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Se crea una nueva migración.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;(django)$ sudo python manage.py makemigrations --name tabla_categoria

Migrations for &apos;polls&apos;:
  polls/migrations/0002_tabla_categoria.py
    - Create model Categoria
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Se realiza la migración.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;(django)$ sudo python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0002_tabla_categoria... OK
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Se añade el nuevo modelo al sitio de administración de django modificando el fichero &lt;code&gt;django_tutorial/polls/admin.py&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;from .models import Choice, Question, Categoria

[...]

admin.site.register(Categoria)  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para que los cambios se trasladen al entorno de producción debemos realizar los siguientes pasos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(django)$ sudo python manage.py
(django)$ sudo python manage.py dumpdata &amp;gt; basedatos2.json
(django)$ sudo python manage.py collectstatic # este no hace falta para el entorno de producción
(django)$ git push
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;En el entorno de producción&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Se realiza la migración.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;(django)$ git pull
(django)$ sudo python manage.py migrate
(django)$ sudo python manage.py loaddata basedatos2.json
(django)$ sudo python manage.py collectstatic
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos comprobar que se han producido los cambios:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/nSoCgbk.png&quot; alt=&quot;base&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/s5iUxOB.png&quot; alt=&quot;admin&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/UrEhNQj.png&quot; alt=&quot;polls&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Recolección centralizada de logs de sistema, mediante Journald</title><link>https://www.robertops.com/posts/2023-01-15_journald/</link><guid isPermaLink="true">https://www.robertops.com/posts/2023-01-15_journald/</guid><pubDate>Sun, 15 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;El escenario de OpenStack es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/I7aSQqg.png&quot; alt=&quot;escenario&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Enunciado&lt;/h2&gt;
&lt;p&gt;Implementa en tu escenario de trabajo de Openstack, un sistema de recolección de log mediante journald. Para ello debes, implementar un sistema de recolección de log mediante el paquete systemd-journal-remote, o similares.&lt;/p&gt;
&lt;h2&gt;Preparación&lt;/h2&gt;
&lt;p&gt;En cada instancia, voy a instalar el paquete &lt;code&gt;systemd-journal-remote&lt;/code&gt;; en alfa, charlie y delta ejecuto el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install systemd-journal-remote
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;en bravo, ejecuto el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dnf install systemd-journal-remote
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configuración&lt;/h2&gt;
&lt;p&gt;He elegido el servidor alfa como servidor de logs, por lo que en él, voy a configurar el servicio. No voy a usar https ni autenticación, por lo que en el fichero &lt;code&gt;/lib/systemd/system/systemd-journal-remote.service&lt;/code&gt; voy a modificar la siguientes línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ExecStart=/lib/systemd/systemd-journal-remote --listen-http=-3 --output=/var/log/journal/remote/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora inicio el servicio y lo habilito para que se inicie en el arranque:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl enable --now systemd-journal-remote.socket
sudo systemctl enable --now systemd-journal-remote.service

sudo systemctl status systemd-journal-remote.socket
sudo systemctl status systemd-journal-remote.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/nKnk0fe.png&quot; alt=&quot;status&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Configuración de los clientes&lt;/h2&gt;
&lt;p&gt;Para ello, en cada uno crearé un usuario llamado &lt;code&gt;systemd-journal-upload&lt;/code&gt; configurado de la siguiente manera:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;en charlie y delta&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo adduser --system --home /run/systemd --no-create-home --disabled-login --group systemd-journal-upload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;en bravo&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo adduser --system --home-dir /run/systemd --no-create-home --user-group systemd-journal-upload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora modifico en el fichero &lt;code&gt;/etc/systemd/journal-upload.conf&lt;/code&gt; la siguiente línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;URL=http://alfa.roberto.gonzalonazareno.org:19532
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora reinicio el servicio en todas las máquinas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart systemd-journal-upload.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Comprobación&lt;/h2&gt;
&lt;p&gt;En el servidor alfa, voy a comprobar que se están recibiendo los logs de los clientes&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/4HNPxgv.png&quot; alt=&quot;logs&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para ver los logs de los clientes, en el servidor alfa, ejecuto el siguiente comando (en este caso, el de delta):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo journalctl --file /var/log/journal/remote/remote-192.168.0.3.journal
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qnJuI9g.png&quot; alt=&quot;logs&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En un ejemplo anterior: &lt;a href=&quot;https://github.com/josedom24/curso_kubernetes_ies/blob/main/modulo6/temperaturas.md&quot;&gt;Ejemplo completo: Desplegando y accediendo a la aplicación Temperaturas&lt;/a&gt; habíamos desplegado una aplicación formada por dos microservicios que nos permitía visualizar las temperaturas de municipios.&lt;/p&gt;
</content:encoded></item><item><title>Escenario DNS</title><link>https://www.robertops.com/posts/2022-12-19_escenario-dns/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-12-19_escenario-dns/</guid><pubDate>Mon, 19 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;El enunciado de la prácica se encuentra en el siguiente &lt;a href=&quot;https://fp.josedomingo.org/sri2223/5_dns/practica.html&quot;&gt;enlace&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;1. Configuración DNS de cada máquina&lt;/h2&gt;
&lt;p&gt;Para no tocar la configuración de los servidores DHCP de &lt;strong&gt;OpenStack&lt;/strong&gt;, cambiamos manualmente la configuración de las máquinas &lt;strong&gt;Alfa, Bravo y Delta&lt;/strong&gt; para que tengan como servidor DNS el servidor &lt;strong&gt;bind9&lt;/strong&gt; en &lt;strong&gt;Charlie&lt;/strong&gt;, así como el dns del centro (Babuino). Para ello, editamos el fichero &lt;code&gt;/etc/resolv.conf&lt;/code&gt; y añadimos las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nameserver 192.168.0.2
nameserver 192.168.202.2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para hacer los cambios persistentes en debian/ubuntu, instalamos el paquete &lt;code&gt;resolvconf&lt;/code&gt;y añadimos los dns al final del fichero &lt;code&gt;/etc/resolvconf/resolv.conf.d/head&lt;/code&gt;. Para aplicar los cambios sin tener que reiniciar tenemos que ejecutar el comando &lt;code&gt;resolvconf -u&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;VISTAS&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;view interna {
    match-clients { 192.168.0.0/24; 127.0.0.1; };
    allow-recursion { any; };
        zone &quot;roberto.gonzalonazareno.org&quot;
        {
        type master;
        file &quot;db.interna.roberto.gonzalonazareno.org&quot;;
        };
        zone &quot;0.168.192.in-addr.arpa&quot;
        {
        type master;
        file &quot;db.0.168.192&quot;;
        };
        zone &quot;16.172.in-addr.arpa&quot;
        {
        type master;
        file &quot;db.0.16.172&quot;;
        };
        include &quot;/etc/bind/zones.rfc1918&quot;;
        include &quot;/etc/bind/named.conf.default-zones&quot;;
    };

view dmz {
    match-clients { 172.16.0/16;};
    allow-recursion { any; };
        zone &quot;roberto.gonzalonazareno.org&quot;
        {
        type master;
        file &quot;db.dmz.roberto.gonzalonazareno.org&quot;;
        };
        zone &quot;16.172.in-addr.arpa&quot;
        {
        type master;
        file &quot;db.16.172&quot;;
        };
        include &quot;/etc/bind/zones.rfc1918&quot;;
        include &quot;/etc/bind/named.conf.default-zones&quot;;
    };

view externa {
    match-clients { 172.22.0.0/16; 172.29.0.0/16; 192.168.202.2; };
    allow-recursion { any; };
        zone &quot;roberto.gonzalonazareno.org&quot;
        {
        type master;
        file &quot;db.externa.roberto.gonzalonazareno.org&quot;;
        };
        include &quot;/etc/bind/zones.rfc1918&quot;;
        include &quot;/etc/bind/named.conf.default-zones&quot;;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Definición de las zonas&lt;/h2&gt;
&lt;h3&gt;INTERNA /var/cache/bind/db.interna.roberto.gonzalonazareno.org&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$TTL    86400
@       IN      SOA     charlie.roberto.gonzalonazareno.org. root.roberto.gonzalonazareno.org. (
                            1           ; Serial
                        604800          ; Refresh
                        86400           ; Retry
                        2419200         ; Expire
                        86400 )         ; Negative Cache TTL
;
@	IN	NS		charlie.roberto.gonzalonazareno.org.
@	IN	MX	10	mail.roberto.gonzalonazareno.org.

$ORIGIN roberto.gonzalonazareno.org.

alfa        IN  A       192.168.0.1
bravo       IN  A       172.16.0.200
charlie     IN  A       192.168.0.2
delta       IN  A       192.168.0.3
www         IN  CNAME   bravo
bd          IN  CNAME   delta
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;INTERNA INVERSA /var/cache/bind/db.0.168.192&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$TTL    86400
@       IN      SOA     charlie.roberto.gonzalonazareno.org. root.roberto.gonzalonazareno.org. (
                            1         ; Serial
                        604800         ; Refresh
                        86400         ; Retry
                        2419200         ; Expire
                        86400 )       ; Negative Cache TTL
;
@	IN	NS		charlie.roberto.gonzalonazareno.org.

$ORIGIN 0.168.192.in-addr.arpa.

1			IN	PTR		alfa.roberto.gonzalonazareno.org.
2            IN	PTR		charlie.roberto.gonzalonazareno.org.
3            IN	PTR		delta.roberto.gonzalonazareno.org.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;INTERNA INVERSA /var/cache/bind/db.16.172&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$TTL    86400
@       IN      SOA     charlie.roberto.gonzalonazareno.org. root.roberto.gonzalonazareno.org. (
                            1         ; Serial
                        604800         ; Refresh
                        86400         ; Retry
                        2419200         ; Expire
                        86400 )       ; Negative Cache TTL
;
@	IN	NS		charlie.roberto.gonzalonazareno.org.

$ORIGIN 16.172.in-addr.arpa.

1.0			IN	PTR		alfa.roberto.gonzalonazareno.org.
200.0            IN	PTR		bravo.roberto.gonzalonazareno.org.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;DMZ /var/cache/bind/db.dmz.roberto.gonzalonazareno.org&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$TTL    86400
@       IN      SOA     charlie.roberto.gonzalonazareno.org. root.roberto.gonzalonazareno.org. (
                            1           ; Serial
                        604800          ; Refresh
                        86400           ; Retry
                        2419200         ; Expire
                        86400 )         ; Negative Cache TTL
;
@	IN	NS		charlie.roberto.gonzalonazareno.org.

$ORIGIN roberto.gonzalonazareno.org.

alfa        IN  A       172.16.0.1
bravo       IN  A       172.16.0.200
charlie     IN  A       192.168.0.2
delta       IN  A       192.168.0.3
www         IN  CNAME   bravo
bd          IN  CNAME   delta
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;EXTERNA /var/cache/bind/db.externa.roberto.gonzalonazareno.org&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;$TTL    86400
@       IN      SOA     alfa.roberto.gonzalonazareno.org. root.roberto.gonzalonazareno.org. (
                            1           ; Serial
                        604800          ; Refresh
                        86400           ; Retry
                        2419200         ; Expire
                        86400 )         ; Negative Cache TTL
;
@	IN	NS		alfa.roberto.gonzalonazareno.org.

$ORIGIN roberto.gonzalonazareno.org.

alfa        IN  A       172.22.200.218
www         IN  CNAME   alfa
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configuración de los servidores web&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
    ServerName www.roberto.gonzalonazareno.org

    ServerAdmin webmaster@localhost

    DocumentRoot /var/www/html

    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    &amp;lt;FilesMatch &quot;\.php$&quot;&amp;gt;
        SetHandler &quot;proxy:unix:/run/php/php7.4-fpm.sock|fcgi://127.0.0.1/&quot; 
        SetHandler &quot;proxy:fcgi://127.0.0.1:9000&quot; 
    &amp;lt;/FilesMatch&amp;gt;

&amp;lt;/VirtualHost&amp;gt;


&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Resolución de problemas de tablas mutantes</title><link>https://www.robertops.com/posts/2022-12-19_tabla-mutante/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-12-19_tabla-mutante/</guid><pubDate>Mon, 19 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;El enunciado a resolver y que contiene una tabla mutante es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6 - Realiza los módulos de programación necesarios para evitar que un catador puntue más de tres aspectos de una misma versión de un experimento.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;En este caso, el trigger principal sería sobre la tabla puntuaciones, y dentro, para comprobar que se cumple la condición de que un catador no puntúa más de 3 aspectos, se tendría que hacer una consulta a la misma tabla puntuaciones, que en ese caso estaría mutando. Para resolver el problema vamos a realizar los siguientes pasos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Leer bien los requisitos del problema e identificar la
información de la tabla que debo guardar en variables
persistentes.&lt;/li&gt;
&lt;li&gt;Crear el paquete declarando los tipos de datos y las variables
necesarias para guardar dicha información.&lt;/li&gt;
&lt;li&gt;Hacer un trigger before por sentencia que rellene dichas
variables consultando la tabla mutante.&lt;/li&gt;
&lt;li&gt;Hacer un trigger before por fila que compruebe si el registro
que se está manejando cumple la condición especificada
consultando las variables persistentes.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Leer bien los requisitos del problema e identificar la información de la tabla que debo guardar en variables persistentes&lt;/h2&gt;
&lt;p&gt;El contenido de la tabla de puntuaciones es el siguiente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/wMxNzMj.png&quot; alt=&quot;Tabla puntuaciones&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En este caso, tendríamos que almacenar en variables persistentes los datos de la consulta, que en este caso son los siguientes campos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NIFCatador&lt;/li&gt;
&lt;li&gt;CodigoAspecto&lt;/li&gt;
&lt;li&gt;CodigoExperimento&lt;/li&gt;
&lt;li&gt;CodigoVersion&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Crear el paquete declarando los tipos de datos y las variables necesarias para guardar dicha información&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PACKAGE ControlPuntuaciones
AS
TYPE tRegistroTablaPuntuaciones IS RECORD --defino el tipo de datos registro
(
NIFCatador Puntuaciones.NIFCatador%TYPE,
CodigoAspecto Puntuaciones.CodigoAspecto%TYPE,
CodigoExperimento Puntuaciones.CodigoExperimento%TYPE,
CodigoVersion Puntuaciones.CodigoVersion%TYPE
);

TYPE tTablasPuntuaciones IS TABLE OF tRegistroTablaPuntuaciones -- defino el tipo de datos tabla
INDEX BY BINARY_INTEGER;
PuntuacionesCatador tTablasPuntuaciones;
-- declaro una variable del tipo tabla antes creado
END ControlPuntuaciones;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Hacer un trigger before por sentencia que rellene dichas variables consultando la tabla mutante&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE TRIGGER RELLENARPUNTUACIONES
BEFORE INSERT OR UPDATE ON Puntuaciones
FOR EACH ROW
DECLARE
CURSOR c_puntuaciones IS SELECT NIFCatador,CodigoAspecto,CodigoExperimento,CodigoVersion
FROM Puntuaciones;
INDICE NUMBER:=0;
indice_u number;
BEGIN

-- vacio el contenido de la tabla
ControlPuntuaciones.PuntuacionesCatador.DELETE;
-- relleno la tabla de puntuaciones los datos que me interesan
FOR v_puntuacion IN c_puntuaciones LOOP
ControlPuntuaciones.PuntuacionesCatador(INDICE).NIFCatador := v_puntuacion.NIFCatador;
ControlPuntuaciones.PuntuacionesCatador(INDICE).CodigoAspecto := v_puntuacion.CodigoAspecto;
ControlPuntuaciones.PuntuacionesCatador(INDICE).CodigoExperimento := v_puntuacion.CodigoExperimento;
ControlPuntuaciones.PuntuacionesCatador(INDICE).CodigoVersion := v_puntuacion.CodigoVersion;
INDICE := INDICE + 1;
END LOOP;
if inserting then
indice_u := ControlPuntuaciones.PuntuacionesCatador.LAST + 1;
ControlPuntuaciones.PuntuacionesCatador(indice_u).NIFCatador := :NEW.NIFCatador;
ControlPuntuaciones.PuntuacionesCatador(indice_u).CodigoAspecto := :NEW.CodigoAspecto;
ControlPuntuaciones.PuntuacionesCatador(indice_u).CodigoExperimento := :NEW.CodigoExperimento;
ControlPuntuaciones.PuntuacionesCatador(indice_u).CodigoVersion := :NEW.CodigoVersion;
end if;

END RELLENARPUNTUACIONES;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Hacer un trigger before por fila que compruebe si el registro que se está manejando cumple la condición especificada consultando las variables persistentes&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE TRIGGER ControlarPuntuaciones
BEFORE INSERT OR UPDATE ON Puntuaciones
FOR EACH ROW
DECLARE
BEGIN
-- compruebo que el catador no haya puntuado más de 3 aspectos
IF (NumeroPuntuacionesCatador(:NEW.NIFCatador,:NEW.CodigoExperimento,:NEW.CodigoVersion) &amp;gt; 3) THEN
RAISE_APPLICATION_ERROR(-20001,&apos;El catador no puede puntuar mas de 3 aspectos&apos;);
end if;
END;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Voy a crear una funcion para comprobar el numero de puntuaciones que tiene un catador en una versión de un experimento:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION NumeroPuntuacionesCatador
( p_NIFCatador Puntuaciones.NIFCatador%TYPE, p_CodigoExperimento Puntuaciones.CodigoExperimento%TYPE, p_CodigoVersion Puntuaciones.CodigoVersion%TYPE)
RETURN NUMBER
IS
    v_NumeroPuntuaciones NUMBER:= 1;
    v_cantidad NUMBER;
BEGIN
    for i in ControlPuntuaciones.PuntuacionesCatador.FIRST..ControlPuntuaciones.PuntuacionesCatador.LAST
    LOOP
        if (ControlPuntuaciones.PuntuacionesCatador(i).NIFCatador = p_NIFCatador) and (ControlPuntuaciones.PuntuacionesCatador(i).CodigoExperimento = p_CodigoExperimento) and (ControlPuntuaciones.PuntuacionesCatador(i).CodigoVersion = p_CodigoVersion) then
            v_NumeroPuntuaciones := v_NumeroPuntuaciones + 1;
        end if;
    END LOOP;
    return v_NumeroPuntuaciones;
end;
/

--- prueba

create or replace procedure prueba
is
numero number;
begin
numero:=NumeroPuntuacionesCatador(&apos;14425879A&apos;,&apos;A0003-A&apos;,&apos;0.0.2&apos;);
dbms_output.put_line(numero);
end;
/
insert into puntuaciones values(&apos;14425879A&apos;,&apos;0004&apos;,&apos;A0003-A&apos;,&apos;0.0.2&apos;,7.5);


create or replace procedure imprimirtabla
is
begin
    for i in ControlPuntuaciones.PuntuacionesCatador.FIRST..ControlPuntuaciones.PuntuacionesCatador.LAST
    LOOP
        dbms_output.put_line(&apos;Registro numero &apos;|| i);
        dbms_output.put_line(&apos;NIFCatador: &apos;|| ControlPuntuaciones.PuntuacionesCatador(i).NIFCatador);
        dbms_output.put_line(&apos;CodigoAspecto: &apos;|| ControlPuntuaciones.PuntuacionesCatador(i).CodigoAspecto);
        dbms_output.put_line(&apos;CodigoExperimento: &apos;|| ControlPuntuaciones.PuntuacionesCatador(i).CodigoExperimento);
        dbms_output.put_line(&apos;CodigoVersion: &apos;|| ControlPuntuaciones.PuntuacionesCatador(i).CodigoVersion);


    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;insert into puntuaciones values(&apos;14425879A&apos;,&apos;0004&apos;,&apos;A0003-A&apos;,&apos;0.0.2&apos;,7.5);
insert into puntuaciones values(&apos;14425879A&apos;,&apos;0005&apos;,&apos;A0003-A&apos;,&apos;0.0.2&apos;,7.5);
insert into puntuaciones values(&apos;14425879A&apos;,&apos;0006&apos;,&apos;A0003-A&apos;,&apos;0.0.2&apos;,7.5);
insert into puntuaciones values(&apos;14425879A&apos;,&apos;0007&apos;,&apos;A0003-A&apos;,&apos;0.0.2&apos;,7.5);
select * from puntuaciones where nifcatador=&apos;14425879A&apos; and codigoexperimento=&apos;A0003-A&apos; and codigoversion=&apos;0.0.2&apos;;
insert into puntuaciones values(&apos;14425879A&apos;,&apos;0005&apos;,&apos;A0003-A&apos;,&apos;0.0.1&apos;,7.5);
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Montaje NFS mediante systemd</title><link>https://www.robertops.com/posts/2022-12-15_nfs/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-12-15_nfs/</guid><pubDate>Thu, 15 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;En una instancia del cloud, basada en la distribución de tu elección, anexa un volumen de 2GB. En dicha instancia deberás configurar el servicio nfs de exportación y en el volumen un punto de montaje de la exportación mediante systemd.&lt;/p&gt;
&lt;h3&gt;Escenario&lt;/h3&gt;
&lt;p&gt;El escenario se compone de dos máquinas, alfa, con debian 11 y que será el servidor nfs; y bravo, con rocky linux 8 y que será el cliente nfs.&lt;/p&gt;
&lt;h2&gt;Servidor NFS&lt;/h2&gt;
&lt;p&gt;Instalamos los paquetes necesarios para el servicio nfs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install nfs-kernel-server nfs-common
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos el fichero  &lt;code&gt;/etc/systemd/system/mnt-carpeta.mount&lt;/code&gt; con el siguiente contenido. El nombre del fichero, tiene que ser el mismo que el del punto de montaje en el que vamos a montar el dispositivo, además de sustituyendo las &quot;/&quot; por &quot;-&quot; (menos la primera):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Montaje de disco para compartir

[Mount]
What= /dev/vdb
Where= /mnt/carpeta/
Type=ext4
Options=defaults

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl enable mnt-carpeta.mount
systemctl start mnt-carpeta.mount
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si no funciona el montaje de la unidad, podemos comprobar os errores con &lt;code&gt;journalctl -xe&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Tras el montaje podemos comprobar que el disco se ha montado correctamente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/8cUkODO.png&quot; alt=&quot;Montaje de disco&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Finalmente, añadimos la siguiente línea al fichero &lt;code&gt;/etc/exports&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/mnt/carpeta 172.16.0.0/16(rw,no_all_squash,no_subtree_check)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y reiniciamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart nfs-server
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cliente NFS&lt;/h2&gt;
&lt;p&gt;Instalamos los paquetes necesarios para el servicio nfs:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dnf install nfs-utils
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos ver los dispositivos de bloques que se están compartiendo por nfs (la ip del servidor nfs es &lt;code&gt;172.16.0.1&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;showmount -e 172.16.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/oaYEhYA.png&quot; alt=&quot;Dispositivos compartidos&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Creamos el fichero  &lt;code&gt;/etc/systemd/system/mnt-carpetaNFS.mount&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Montaje del disco compartido por red usando NFS

[Mount]
What=172.16.0.1:/mnt/carpeta
Where=/mnt/carpetaNFS
Type=nfs
Options=defaults

[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Activamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl enable mnt-carpetaNFS.mount
systemctl start mnt-carpetaNFS.mount
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos comprobar que el disco se ha montado correctamente:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/l4fEpSh.png&quot; alt=&quot;Montaje de disco 2&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Comprobación&lt;/h2&gt;
&lt;p&gt;Comprobamos que el dispositivo de bloques se está compartiendo por nfs:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/16NCruU.png&quot; alt=&quot;Comprobación&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Compilación Kernel</title><link>https://www.robertops.com/posts/2022-12-07_compilacion-kernel/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-12-07_compilacion-kernel/</guid><pubDate>Wed, 07 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Compilación de un Kernel linux a medida&lt;/h2&gt;
&lt;p&gt;Al ser linux un kérnel libre, es posible descargar el código fuente, configurarlo y comprimirlo. Además, esta tarea a priori compleja, es más sencilla de lo que parece gracias a las herramientas disponibles.
En esta tarea debes tratar de compilar un kérnel completamente funcional que reconozca todo el hardware básico de tu equipo y que sea a la vez lo más pequeño posible, es decir que incluya un vmlinuz lo más pequeño posible y que incorpore sólo los módulos imprescindibles. Para ello utiliza el método explicado en clase y entrega finalmente el fichero deb con el kérnel compilado por ti.
El hardware básico incluye como mínimo el teclado, la interfaz de red y la consola gráfica (texto).&lt;/p&gt;
&lt;h2&gt;Descarga&lt;/h2&gt;
&lt;p&gt;En este caso vamos a descargar el último kernel de linux de la &lt;a href=&quot;https://kernel.org/&quot;&gt;página oficial&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.0.7.tar.xz
tar xf linux-6.0.7.tar.xz
cd linux-6.0.7
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Compilación del kernel&lt;/h2&gt;
&lt;p&gt;Para configurar los módulos que tenemos cargados actualmente en el sistema tenemos que introducir losa siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make oldconfig
make localyesconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con lo siguiente podemos comprobar el número de módulos estáticos y dinámicos que tenemos actualmente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;egrep &apos;=y&apos; .config | wc -l
egrep &apos;=m&apos; .config | wc -l
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para compilar el kernel, tenemos que ejecutar el siguiente comando, que aprovecha el número de núcleos que tenemos para reducir al máximo el tiempo de compilación:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;time make -j $(nproc) bindeb-pkg
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Reducir el kernel&lt;/h2&gt;
&lt;p&gt;Para reducir el tamaño del kernel tenemos que desactivar módulos. Éstos se desactivan ejecutando el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;make clean
make xconfig
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;que abre una interfaz gráfica en la que podemos seleccionar los módulos quq queremos activar o desactivar:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/cndWKk6.png&quot; alt=&quot;https://i.imgur.com/cndWKk6.png&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Instalación/migración de aplicaciones web PHP</title><link>https://www.robertops.com/posts/2022-12-07_migracion_php/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-12-07_migracion_php/</guid><pubDate>Wed, 07 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Escenario&lt;/h2&gt;
&lt;p&gt;Vamos a hacer un escenario de vagrant utilizando el siguiente &lt;code&gt;Vagrantfile&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vagrant.configure(&quot;2&quot;) do |config|

config.vm.define :web do |web|
    web.vm.box = &quot;debian/bullseye64&quot;
    web.vm.hostname = &quot;servidor-web-roberto&quot;
    web.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true
    web.vm.network :public_network,
      :dev =&amp;gt; &quot;bridge0&quot;,
      :mode =&amp;gt; &quot;bridge&quot;,
      :type =&amp;gt; &quot;bridge&quot;,
      use_dhcp_assigned_default_route: true
    web.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;net1&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;10.0.0.1&quot;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end
  config.vm.define :bd do |bd|
    bd.vm.box = &quot;debian/bullseye64&quot;
    bd.vm.hostname = &quot;servidor-bd-roberto&quot;
    bd.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true
    bd.vm.network :private_network,
      :libvirt__network_name =&amp;gt; &quot;net1&quot;,
      :libvirt__dhcp_enabled =&amp;gt; false,
      :ip =&amp;gt; &quot;10.0.0.2&quot;,
      :libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
  end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configuración de resolución estática&lt;/h2&gt;
&lt;p&gt;Vamos a configurar la resolución estática de las páginas utilizando la IP pública de la máquina web:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/n78aYSy.png&quot; alt=&quot;resolucion&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Instalación de un CMS PHP en mi servidor local&lt;/h2&gt;
&lt;p&gt;En este caso el CMS que vamos a instalar es &lt;a href=&quot;https://www.mediawiki.org/wiki/Manual:Installing_MediaWiki&quot;&gt;Media Wiki&lt;/a&gt;. Ahora vamos a configurar el servidor y la base de datos.&lt;/p&gt;
&lt;p&gt;En el servidor web instalamos apache con php:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt install apache2 libapache2-mod-php php php-mysql
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración del VirtualHost&lt;/h3&gt;
&lt;p&gt;Vamos a instalar el cms en &lt;code&gt;/var/www/mediawiki&lt;/code&gt;, por lo configuramos el vhost en el fichero &lt;code&gt;etc/apache2/sites-available/mediawiki.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
        ServerName www.roberto.org

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/mediawiki

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined
&amp;lt;/VirtualHost&amp;gt;

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora activamos el VirtualHost&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a2ensite mediawiki
systemctl reload apache2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración de la base de datos&lt;/h3&gt;
&lt;p&gt;En el servidor que va a tener la base de datos, instalamos mariadb:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt update
apt install mariadb-server
mariadb -u root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dentro vamos a crear una base de datos para el CMS y un usuario con permisos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON *.* TO &apos;remoto&apos;@&apos;%&apos;
IDENTIFIED BY &apos;remoto&apos; WITH GRANT OPTION;

create database mediawiki;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para configurar el acceso remoto, tenemos que modificar en fichero &lt;code&gt;/etc/mysql/mariadb.conf.d/50-server.cnf&lt;/code&gt;, la siguiente línea:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bind-address            = 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y reiniciamos el servicio.&lt;/p&gt;
&lt;p&gt;Ahora desde el cliente creamos el siguiente usuario:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON *.* TO &apos;remoto&apos;@&apos;10.0.0.2&apos; IDENTIFIED BY &apos;remoto&apos; WITH GRANT OPTION;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y podemos conectarnos a la base de datos remota con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql -u remoto -h 10.0.0.2 --password=remoto
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Instalación MediaWiki&lt;/h3&gt;
&lt;p&gt;Para instalar media wiki, tenemos que descargar la última versión de la página oficial:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://releases.wikimedia.org/mediawiki/1.38/mediawiki-1.38.4.tar.gz
tar -xf mediawiki-1.38.4.tar.gz
cp -r mediawiki-1.38.4/* /var/www/mediawiki/
chown -R www-data:www-data /var/www/mediawiki/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Antes de iniciar la instalación tenemos que instalar los siguientes paquetes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install php-mbstring php-xml php-intl -y
systemctl restart apache2.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora seguimos la instalación normalmente, pero en la configuración de la base de datos es importante especificar la IP:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/lUVzDxN.png&quot; alt=&quot;p2-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una vez finalizada la configuración inicial, se descarga el fichero &lt;code&gt;LocalSettings.php&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Ecn7gng.png&quot; alt=&quot;p2-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tenemos que moverlo al Document Root de MediaWiki:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chown www-data:www-data LocalSettings.php
mv LocalSettings.php /var/www/mediawiki/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez realizada la configuración, accediendo a &lt;code&gt;www.roberto.org&lt;/code&gt; aparece la wiki:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/mT4mBno.png&quot; alt=&quot;p2-3&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Instalación de un módulo&lt;/h4&gt;
&lt;p&gt;Tras configurar el tema, vamos a instalar un módulo: &lt;a href=&quot;https://www.mediawiki.org/wiki/Extension:SimpleCalendar&quot;&gt;SimpleCalendar&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://extdist.wmflabs.org/dist/extensions/SimpleCalendar-REL1_38-b7a2f05.tar.gz
tar -xf SimpleCalendar-REL1_38-b7a2f05.tar.gz
mv SimpleCalendar /var/www/mediawiki/extensions/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora añadimos la siguiente línea al final de &lt;code&gt;LocalSettings.php&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;wfLoadExtension( &apos;SimpleCalendar&apos; );&quot; &amp;gt;&amp;gt; /var/www/mediawiki/LocalSettings.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez hecho, podemos comprobar que está correctamente instalado accediendo a &lt;a href=&quot;http://www.roberto.org/index.php/Especial:Versi%C3%B3n&quot;&gt;http://www.roberto.org/index.php/Especial:Versión&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GYSoi3r.png&quot; alt=&quot;p2-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ya instalado, podemos añadir calendarios a las páginas de la wiki con el siguiente bloque:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{#calendar: year=2022 | month=nov | title=&quot;calendario&quot; }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y quedaría de la siguiente forma:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/bNKXNG6.png&quot; alt=&quot;p2-5&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Instalación del CMS PHP NextCloud&lt;/h2&gt;
&lt;p&gt;Primero descargamos la última versión y lo movemos al directorio de apache:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://download.nextcloud.com/server/releases/latest.zip
unzip latest.zip
cp -r nextcloud/ /var/www/
chown -R www-data:www-data /var/www/nextcloud/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora configuramos el vhost en el fichero &lt;code&gt;etc/apache2/sites-available/nextcloud.conf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
        ServerName www.cloud.roberto.org        
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/nextcloud        
        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined     
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;a2ensite nextcloud.conf
systemctl reload apache2.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;E instalamos los módulos de php necesarios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  apt install php-zip php-gd php-curl -y
systemctl reload apache2.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora en la máquina con la base de datos creamos la base de datos para nextcloud:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create database nextcloud;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras este paso, podemos acceder a &lt;code&gt;cloud.roberto.org&lt;/code&gt; para iniciar la instalación, es importante que en la base de datos especifiquemos la ip y el puerto de la máquina con la base de datos:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/dSKyF4C.png&quot; alt=&quot;p2-6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una vez finalizada la instalación, ya podremos utilizar nextcloud:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/LKyMGco.png&quot; alt=&quot;p2-7&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;DNS del dominio&lt;/h2&gt;
&lt;p&gt;Para configurar el dns tenemos que mirar la dirección en la configuración del VPS:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/MZtPIqH.png&quot; alt=&quot;p2-8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;y añadir un registro CNAME en la dirección &lt;a href=&quot;www.admichin.es&quot;&gt;www.admichin.es&lt;/a&gt; que lleve a esa dirección:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/hnTB39X.png&quot; alt=&quot;p2-9&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Configuración del servidor LEMP&lt;/h2&gt;
&lt;p&gt;Para instalar el servidor lemp tenemos que instalar los siguientes paquetes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install 
apt install nginx php php-mysql mariadb-server -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, en la máquina con la base de datos, creamos un fichero con la copia de seguridad y lo enviamos a la vps:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysqldump -u remoto -p -x -A &amp;gt; dbs.sql
scp dbs.sql calcetines@nodriza.admichin.es:/home/calcetines
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creamos en la base de datos un usuario con permisos y restauramos las bases de datos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON *.* TO &apos;admin&apos;@localhost IDENTIFIED BY &apos;contraseña&apos; WITH GRANT OPTION;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;mysql --user admin --password &amp;lt; dbs.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Migración de las aplicaciones&lt;/h2&gt;
&lt;p&gt;Para migrar las aplicaciones, tenemos que moverlas al servidor. En este caso utilizaré rsync ya que permite reanudar la transmisión si se interrumpe, además de ser más rápido que scp:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rsync -avP /var/www/mediawiki/ calcetines@nodriza.admichin.es:/home/calcetines/mediawiki
rsync -avP /var/www/nextcloud/ calcetines@nodriza.admichin.es:/home/calcetines/nextcloud
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez finalizado, en este caso vamos a utilizar un solo virtualhost, por lo que los directorios de ambas aplicaciones deben estar en &lt;code&gt;/var/www/html/nombreaplicacion&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;upstream php-handler {
    #server 127.0.0.1:9000;
    server unix:/var/run/php/php7.4-fpm.sock;
}

# Set the `immutable` cache control options only for assets with a cache busting `v` argument
map $arg_v $asset_immutable {
    &quot;&quot; &quot;&quot;;
    default &quot;immutable&quot;;
}

server {
    listen 80;
    listen [::]:80;
    server_name www.admichin.es;

    # Prevent nginx HTTP Server Detection
    #server_tokens off;
    rewrite ^/$ /portal;

    # Path to the root of the domain
    root /var/www/html;

    location = /robots.txt {
        allow all;
        log_not_found off;
        access_log off;
    }

    location ^~ /.well-known {
        # The rules in this block are an adaptation of the rules
        # in the cloud `.htaccess` that concern `/.well-known`.

        location = /.well-known/carddav { return 301 /cloud/remote.php/dav/; }
        location = /.well-known/caldav  { return 301 /cloud/remote.php/dav/; }

        location /.well-known/acme-challenge    { try_files $uri $uri/ =404; }
        location /.well-known/pki-validation    { try_files $uri $uri/ =404; }

        # Let cloud&apos;s API for `/.well-known` URIs handle all other
        # requests by passing them to the front-end controller.
        return 301 /cloud/index.php$request_uri;
    }
    location ^~ /portal {
        # set max upload size and increase upload timeout:
        client_max_body_size 512M;
        client_body_timeout 300s;
        fastcgi_buffers 64 4K;

        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;


        client_body_buffer_size 512k;

        # HTTP response headers borrowed from cloud `.htaccess`
        add_header Referrer-Policy                      &quot;no-referrer&quot;   always;
        add_header X-Content-Type-Options               &quot;nosniff&quot;       always;
        add_header X-Download-Options                   &quot;noopen&quot;        always;
        add_header X-Frame-Options                      &quot;SAMEORIGIN&quot;    always;
        add_header X-Permitted-Cross-Domain-Policies    &quot;none&quot;          always;
        add_header X-Robots-Tag                         &quot;none&quot;          always;
        add_header X-XSS-Protection                     &quot;1; mode=block&quot; always;

        fastcgi_hide_header X-Powered-By;

        index index.php index.html /portal/index.php$request_uri;

        location ~ \.php(?:$|/) {

            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            set $path_info $fastcgi_path_info;

            try_files $fastcgi_script_name =404;

            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;

            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
            fastcgi_param front_controller_active true;     # Enable pretty urls
            fastcgi_pass php-handler;

            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;

            fastcgi_max_temp_file_size 0;
        }

        location /portal {
            try_files $uri $uri/ /portal/index.php$request_uri;
        }
    }
    location ~ /\.ht {
          deny all;
         }

    location ^~ /cloud {
        # set max upload size and increase upload timeout:
        client_max_body_size 512M;
        client_body_timeout 300s;
        fastcgi_buffers 64 4K;
        # Enable gzip but do not remove ETag headers
        gzip on;
        gzip_vary on;
        gzip_comp_level 4;
        gzip_min_length 256;
        gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
        gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/wasm application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
        # HTTP response headers borrowed from cloud `.htaccess`
        add_header Referrer-Policy                      &quot;no-referrer&quot;   always;
        add_header X-Content-Type-Options               &quot;nosniff&quot;       always;
        add_header X-Download-Options                   &quot;noopen&quot;        always;
        add_header X-Frame-Options                      &quot;SAMEORIGIN&quot;    always;
        add_header X-Permitted-Cross-Domain-Policies    &quot;none&quot;          always;
        add_header X-Robots-Tag                         &quot;none&quot;          always;
        add_header X-XSS-Protection                     &quot;1; mode=block&quot; always;

        # Remove X-Powered-By, which is an information leak
        fastcgi_hide_header X-Powered-By;
        index index.php index.html /cloud/index.php$request_uri;

        # Rule borrowed from `.htaccess` to handle Microsoft DAV clients
        location = /cloud {
            if ( $http_user_agent ~ ^DavClnt ) {
                return 302 /cloud/remote.php/webdav/$is_args$args;
            }
        }

        # Rules borrowed from `.htaccess` to hide certain paths from clients
        location ~ ^/cloud/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/)    { return 404; }
        location ~ ^/cloud/(?:\.|autotest|occ|issue|indie|db_|console)                  { return 404; }
        location ~ \.php(?:$|/) {
            # Required for legacy support
            rewrite ^/cloud/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+|.+\/richdocumentscode\/proxy) /cloud/index.php$request_uri;

            fastcgi_split_path_info ^(.+?\.php)(/.*)$;
            set $path_info $fastcgi_path_info;

            try_files $fastcgi_script_name =404;

            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $path_info;
           # fastcgi_param HTTPS on;

            fastcgi_param modHeadersAvailable true;         # Avoid sending the security headers twice
            fastcgi_param front_controller_active true;     # Enable pretty urls
            fastcgi_pass php-handler;

            fastcgi_intercept_errors on;
            fastcgi_request_buffering off;

            fastcgi_max_temp_file_size 0;
        }
        location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite|map)$ {
            try_files $uri /cloud/index.php$request_uri;
            add_header Cache-Control &quot;public, max-age=15778463, $asset_immutable&quot;;
            access_log off;     # Optional: Don&apos;t log access to assets

            location ~ \.wasm$ {
                default_type application/wasm;
            }
        }
        location ~ \.woff2?$ {
            try_files $uri /cloud/index.php$request_uri;
            expires 7d;         # Cache-Control policy borrowed from `.htaccess`
            access_log off;     # Optional: Don&apos;t log access to assets
        }
        # Rule borrowed from `.htaccess`
        location /cloud/remote {
            return 301 /cloud/remote.php$request_uri;
        }
        location /cloud {
            try_files $uri $uri/ /cloud/index.php$request_uri;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;MediaWiki&lt;/h3&gt;
&lt;p&gt;para migrar la aplicación de mediawiki tenemos que realizar los siguientes pasos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Primero tenemos que transferir los ficheros que se encuentren en el document root de mediawiki a la vps, en este caso con rsync:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;rsync -avP /var/www/mediawiki/ calcetines@nodriza.admichin.es:/home/calcetines/mediawiki
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;dentro de la vps lo movemos a su nueva ubicación:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mv mediawiki /var/www/html/portal
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;El último paso, es configurar el fichero &lt;code&gt;LocalSettings.php&lt;/code&gt; (he quitado los comentarios para que sea menos largo):&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
if ( !defined( &apos;MEDIAWIKI&apos; ) ) { exit;
}
$wgSitename = &quot;adMICHIn.es&quot;; $wgMetaNamespace = &quot;AdMICHIn.es&quot;;
$wgScriptPath = &quot;/portal&quot;;
$wgServer = &quot;http://www.admichin.es&quot;;
$wgResourceBasePath = $wgScriptPath;
$wgLogos = [ &apos;1x&apos; =&amp;gt; &quot;https://i.imgur.com/KqnlgCE.png&quot;,
	
	
	&apos;icon&apos; =&amp;gt; &quot;https://i.imgur.com/KqnlgCE.png&quot;, ];
$wgEnableEmail = true; $wgEnableUserEmail = true; # UPO $wgEmergencyContact = &quot;apache@������.invalid&quot;; $wgPasswordSender = 
&quot;apache@������.invalid&quot;; $wgEnotifUserTalk = false; # UPO $wgEnotifWatchlist = false; # UPO $wgEmailAuthentication = true;
$wgDBtype = &quot;mysql&quot;; $wgDBserver = &quot;localhost&quot;; $wgDBname = &quot;mediawiki&quot;; $wgDBuser = &quot;admin&quot;; $wgDBpassword = 
&quot;contraseña&quot;;
$wgDBprefix = &quot;&quot;;
$wgDBTableOptions = &quot;ENGINE=InnoDB, DEFAULT CHARSET=binary&quot;;
$wgSharedTables[] = &quot;actor&quot;;
$wgMainCacheType = CACHE_NONE; $wgMemCachedServers = [];
$wgEnableUploads = false;
$wgUseInstantCommons = false;
$wgPingback = true;
$wgLanguageCode = &quot;es&quot;;
$wgLocaltimezone = &quot;UTC&quot;;
$wgSecretKey = &quot;61c7675d604b7bd8b0b434dd7c53d6470ff8797636136e4ef7ade0e08cdaba14&quot;;
$wgAuthenticationTokenVersion = &quot;1&quot;;
$wgUpgradeKey = &quot;696787eac8f24d0f&quot;;
$wgRightsPage = &quot;&quot;; # Set to the title of a wiki page that describes your license/copyright $wgRightsUrl = &quot;&quot;; $wgRightsText = 
&quot;&quot;; $wgRightsIcon = &quot;&quot;;
$wgDiff3 = &quot;/usr/bin/diff3&quot;;
$wgDefaultSkin = &quot;MonoBook&quot;;
wfLoadSkin( &apos;MinervaNeue&apos; ); wfLoadSkin( &apos;MonoBook&apos; ); wfLoadSkin( &apos;Timeless&apos; ); wfLoadSkin( &apos;Vector&apos; );
wfLoadExtension( &apos;SimpleCalendar&apos; );
$wgUsePathInfo = TRUE;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso mediaWiki estaría totalmente configurado y podremos acceder con: &lt;a href=&quot;http://www.admichin.es&quot;&gt;http://www.admichin.es&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/3BgNcj6.png&quot; alt=&quot;m1&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;NextCloud&lt;/h3&gt;
&lt;p&gt;Para migrar nextcloud, el método es diferente. Siguiendo &lt;a href=&quot;https://docs.nextcloud.com/server/latest/admin_manual/maintenance/migrating.html&quot;&gt;guía oficial&lt;/a&gt; hay que seguir los siguientes pasos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;En la vps, tenemos que instalar los requisitos previos de nextcloud&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;pt install php-zip php-gd php-curl -y
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;En la máquina original, hay que activar el modo mantenimiento del Nextcloud (desde el document root de nextcloud) y apagar el servidor tras 6-7 minutos:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;sudo -u www-data php occ maintenance:mode --on
systemctl stop apache2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/gFqYaJ9.png&quot; alt=&quot;m2&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cuando termine copiamos los ficheros de nextcloud a la vps:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;rsync -avP /var/www/nextcloud/ calcetines@nodriza.admichin.es:/home/calcetines/nextcloud
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Dentro de la vps lo movemos a su nueva ubicación:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;mv nextcloud /var/www/html/cloud
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;en el fichero config.php tenemos que adaptar las opciones:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$CONFIG = array (
  &apos;instanceid&apos; =&amp;gt; &apos;oct5tjcnoj2h&apos;,
  &apos;passwordsalt&apos; =&amp;gt; &apos;uz7Kh0ZsihYsbbhIhL/HojMYSVc20y&apos;,
  &apos;secret&apos; =&amp;gt; &apos;NxO/97NF1AoG+oCMrY3ryaefvSGO6SHczYjoc5x8NvsLZ1ma&apos;,
  &apos;trusted_domains&apos; =&amp;gt; 
  array (
    0 =&amp;gt; &apos;www.admichin.es&apos;,
  ),
  &apos;datadirectory&apos; =&amp;gt; &apos;/var/www/html/cloud/data&apos;,
  &apos;dbtype&apos; =&amp;gt; &apos;mysql&apos;,
  &apos;version&apos; =&amp;gt; &apos;25.0.1.1&apos;,
  &apos;overwrite.cli.url&apos; =&amp;gt; &apos;http://www.admichin.es/cloud&apos;,
  &apos;dbname&apos; =&amp;gt; &apos;nextcloud&apos;,
  &apos;dbhost&apos; =&amp;gt; &apos;localhost&apos;,
  &apos;dbport&apos; =&amp;gt; &apos;&apos;,
  &apos;dbtableprefix&apos; =&amp;gt; &apos;oc_&apos;,
  &apos;mysql.utf8mb4&apos; =&amp;gt; true,
  &apos;dbuser&apos; =&amp;gt; &apos;oc_roberto&apos;,
  &apos;dbpassword&apos; =&amp;gt; &apos;}QR947P6^,vV%K$ATu]$W~%)AUhj6X&apos;,
  &apos;installed&apos; =&amp;gt; true,
  &apos;maintenance&apos; =&amp;gt; true,
);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Ahora, teniendo en cuenta que el serverblock de nginx está configurado, comprobamos que al acceder a &lt;a href=&quot;http://www.admichin.es/cloud&quot;&gt;http://www.admichin.es/cloud&lt;/a&gt; aparece el modo mantenimiento.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Si aparecece la misma imagen que en el caso anterior, entonces podemos cambiar el valor de configuración de &lt;code&gt;mainteinance&lt;/code&gt; a false, y recargamos la página:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7jYwYOI.png&quot; alt=&quot;m3&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/06VeYQ7.png&quot; alt=&quot;m4&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Práctica PL/SQL individual</title><link>https://www.robertops.com/posts/2022-12-07_plsqlindiv/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-12-07_plsqlindiv/</guid><pubDate>Wed, 07 Dec 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Hacer un procedimiento que muestre el nombre y el salario del empleado cuyo código es 7782&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE mostrarnombresalario
is
    v_nombre emp.ename%type;
    v_salario emp.sal%type;
begin
    select ename,sal into v_nombre,v_salario from emp where empno=7782;
    dbms_output.put_line(&apos;El nombre del empleado es &apos; || v_nombre || &apos; y su salario es: &apos; || v_salario);
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/CscMTfL.png&quot; alt=&quot;p1&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hacer un procedimiento que reciba como parámetro un código de empleado y devuelva su nombre&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE mostrarnombresalario2 (p_empno emp.empno%TYPE)
is
    v_nombre emp.ename%type;
    v_salario emp.sal%type;
begin
    select ename,sal into v_nombre,v_salario from emp where empno=p_empno;
    dbms_output.put_line(&apos;El nombre del empleado es &apos; || v_nombre || &apos; y su salario es: &apos; || v_salario);
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/o2tqMaP.png&quot; alt=&quot;p2&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hacer un procedimiento que devuelva los nombres de los tres empleados más antiguos&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE topantiguos
is
    cursor c_top is
        select ename
        from emp
        order by hiredate asc
        fetch first 3 rows only;
begin
    dbms_output.put_line(&apos;Los 3 empleados mas antiguos son: &apos;);
    for v_empleado in c_top loop
        dbms_output.put_line(v_empleado.ename);
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/FOSjg5a.png&quot; alt=&quot;p3&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hacer un procedimiento que reciba el nombre de un tablespace y muestre los nombres de los usuarios que lo tienen como tablespace por defecto (Vista DBA_USERS)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE verusuarios (p_tablespace dba_users.default_tablespace%type)
is
    cursor c_usuarios is
        SELECT username
        from dba_users
        where default_tablespace=p_tablespace;
begin
    dbms_output.put_line(&apos;El tablespace &apos; || p_tablespace || &apos; es el predeterminado de los siguientes usuarios:&apos;);
    for v_usuario in c_usuarios loop
        dbms_output.put_line(v_usuario.username);
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/U3oRByX.png&quot; alt=&quot;p4&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Modificar el procedimiento anterior para que haga lo mismo pero devolviendo el número de usuarios que tienen ese tablespace como tablespace por defecto. Nota: Hay que convertir el procedimiento en función&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE function f_numusuarios (p_tablespace dba_users.default_tablespace%type)
return number
is
    v_num number;
begin
    select count(username) into v_num from dba_users where default_tablespace=p_tablespace;
    return v_num;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Hacer un procedimiento llamado mostrar_usuarios_por_tablespace que muestre por pantalla un listado de los tablespaces existentes con la lista de usuarios de cada uno y el número de los mismos, así: (Vistas DBA_TABLESPACES y DBA_USERS)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Tablespace xxxx:

	Usr1
	Usr2
	...

Total Usuarios Tablespace xxxx: n1

Tablespace yyyy:

	Usr1
	Usr2
	...

Total Usuarios Tablespace yyyy: n2
....
Total Usuarios BD: nn
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;He modificado el procedimiento del ejercicio 4:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE verusuarios (p_tablespace dba_users.default_tablespace%type)
is
    cursor c_usuarios is
        SELECT username
        from dba_users
        where default_tablespace=p_tablespace;
begin
    for v_usuario in c_usuarios loop
        dbms_output.put_line(CHR(9)|| v_usuario.username);
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y el procedimiento nuevo es:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE mostrar_usuarios_por_tablespace
is
    cursor c_tablespaces is
        SELECT tablespace_name
        from dba_tablespaces;
    v_total_usuario number;
    v_total number:=0;
begin
    for v_tablespace in c_tablespaces loop
        dbms_output.put_line(&apos;Tablespace &apos; || v_tablespace.tablespace_name);
        verusuarios(v_tablespace.tablespace_name);
        v_total_usuario:=f_numusuarios(v_tablespace.tablespace_name);
        v_total:=v_total+v_total_usuario;
        dbms_output.put_line(&apos;Total Usuarios tablespace &apos; || v_tablespace.tablespace_name || &apos;: &apos; || v_total_usuario);
    end loop;
    dbms_output.put_line(&apos;Total Usuarios BD : &apos; || v_total);
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/1sCHBpU.png&quot; alt=&quot;p5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;[...]&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/pbjFtsP.png&quot; alt=&quot;p6&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hacer un procedimiento llamado mostrar_codigo_fuente  que reciba el nombre de otro procedimiento y muestre su código fuente. (DBA_SOURCE)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE mostrar_codigo_fuente (p_nombre dba_source.name%type)
is
    cursor c_codigo is
        SELECT text
        from dba_source
        where name=p_nombre;
begin
    for v_codigo in c_codigo loop
        dbms_output.put_line(v_codigo.text);
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/zx9BQyp.png&quot; alt=&quot;p7&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Hacer un procedimiento llamado mostrar_privilegios_usuario que reciba el nombre de un usuario y muestre sus privilegios de sistema y sus privilegios sobre objetos. (DBA_SYS_PRIVS y DBA_TAB_PRIVS)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE mostrar_privilegios_usuario (p_nombre dba_source.name%type)
is
    cursor c_sistema is
        SELECT privilege
        from dba_sys_privs
        where grantee=p_nombre
        and ADMIN_OPTION=&apos;YES&apos;
        OR INHERITED=&apos;YES&apos;;
    cursor c_objetos is
        SELECT privilege,table_name
        from dba_tab_privs
        where grantee=p_nombre;
begin
    dbms_output.put_line(&apos;Privilegios del usuario &apos;|| p_nombre || &apos; de sistema&apos;);
    for v_sistema in c_sistema loop
        dbms_output.put_line(CHR(9)||v_sistema.privilege);
    end loop;
    dbms_output.put_line(&apos;Privilegios del usuario &apos;|| p_nombre || &apos; sobre objetos&apos;);
    for v_objeto in c_objetos loop
        dbms_output.put_line(CHR(9)||v_objeto.privilege || &apos;---&apos; || v_objeto.table_name);
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/V9bkArM.png&quot; alt=&quot;p8&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Realiza un procedimiento llamado listar_comisiones que nos muestre por pantalla un listado de las comisiones de los empleados agrupados según la localidad donde está ubicado su departamento con el siguiente formato:&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;Localidad NombreLocalidad

    Departamento: NombreDepartamento

        Empleado1 ……. Comisión 1
        Empleado2 ……. Comisión 2
        .
        .
        .
        Empleadon ……. Comision n

    Total Comisiones en el Departamento NombreDepartamento: SumaComisiones

    Departamento: NombreDepartamento

        Empleado1 ……. Comisión 1
        Empleado2 ……. Comisión 2
        .
        .
        .
        Empleadon ……. Comision n

    Total Comisiones en el Departamento NombreDepartamento: SumaComisiones
    .
    .
Total Comisiones en la Localidad NombreLocalidad: SumaComisionesLocalidad

Localidad NombreLocalidad
.
.

Total Comisiones en la Empresa: TotalComisiones
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nota: Los nombres de localidades, departamentos y empleados deben aparecer por orden alfabético.&lt;/p&gt;
&lt;p&gt;Si alguno de los departamentos no tiene ningún empleado con comisiones, aparecerá un mensaje informando de ello en lugar de la lista de empleados.&lt;/p&gt;
&lt;p&gt;El procedimiento debe gestionar adecuadamente las siguientes excepciones:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a) La tabla Empleados está vacía.
b) Alguna comisión es mayor que 10000.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;He creado dos funciones y un procedimiento:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE FUNCTION listar_empleados(p_deptno dept.deptno%type)
return number
is
    cursor c_empleados is
        SELECT ename,comm
        from emp
        where deptno=p_deptno
        order by ename;
    v_vacio number;
    v_suma number:=0;
    v_valor number;
begin
    select sum(comm) into v_vacio from emp where deptno=p_deptno;
    if v_vacio&amp;gt;0 then
        for v_empleado in c_empleados loop
            IF v_empleado.comm is NULL THEN
                v_valor:=0;
            ELSE
                v_suma:=v_suma+v_empleado.comm;
                v_valor:=v_empleado.comm;
            END IF;
            dbms_output.put_line(CHR(9)||CHR(9)|| v_empleado.ename || &apos; ... &apos; || v_valor);
        end loop;
    else
        dbms_output.put_line(CHR(9)||CHR(9)|| &apos;El departamento no tiene comisiones&apos;);
    end if;
    return v_suma;
EXCEPTION
    WHEN NO_DATA_FOUND then
        dbms_output.put_line(&apos;La tabla empleados está vacía&apos;);
        return 0;
end;
/

CREATE OR REPLACE FUNCTION listar_departamentos(p_loc dept.loc%type)
return number
is
    cursor c_departamentos is
        SELECT dname,deptno
        from dept
        where loc=p_loc
        order by dname;
    v_total number;
    v_suma number:=0;
begin
    for v_departamento in c_departamentos loop
        dbms_output.put_line(CHR(9)|| &apos;Departamento: &apos; || v_departamento.dname);
        v_total:=listar_empleados(v_departamento.deptno);
        dbms_output.put_line(CHR(9)|| &apos;Total Comisiones en el Departamento &apos; || v_departamento.dname || &apos;: &apos; || v_total);
        v_suma:=v_suma+v_total;
    end loop;
    return v_suma;
end;
/

CREATE OR REPLACE PROCEDURE listar_comisiones
is
    cursor c_localidades is
        SELECT loc
        from dept
        order by loc;
    v_total number;
    v_suma number:=0;
begin
    for v_localidad in c_localidades loop
        dbms_output.put_line(&apos;Localidad &apos; || v_localidad.loc);
        v_total:=listar_departamentos(v_localidad.loc);
        dbms_output.put_line(&apos;Total Comisiones en la Localidad &apos; || v_localidad.loc || &apos;: &apos; || v_total);
        v_suma:=v_suma+v_total;
    end loop;
    dbms_output.put_line(&apos;Total Comisiones de la Empresa &apos; || v_suma);
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/DGWQS1b.png&quot; alt=&quot;p9&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;. Realiza un procedimiento que reciba el nombre de una tabla y muestre los nombres de las restricciones que tiene, a qué columna afectan y en qué consisten exactamente. (DBA_TABLES, DBA_CONSTRAINTS, DBA_CONS_COLUMNS)&lt;/h2&gt;
&lt;p&gt;He realizado los siguientes procedimientos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE OR REPLACE PROCEDURE listar_restriccion(p_nombre user_constraints.table_name%type,p_tabla user_constraints.table_name%type)
is
    v_tipo user_constraints.constraint_type%type;
    v_nombre user_constraints.constraint_name%type;
    v_referencia user_constraints.r_constraint_name%type;
    v_condicion user_constraints.search_condition%type;
begin
    select constraint_name,constraint_type,r_constraint_name,search_condition into v_nombre,v_tipo,v_referencia,v_condicion
    from user_constraints
    where table_name = p_tabla
    and constraint_name=p_nombre;
    if v_tipo=&apos;P&apos; then
        dbms_output.put_line(p_nombre || &apos; ... es de tipo CLAVE PRIMARIA&apos;);
    elsif v_tipo=&apos;R&apos; then
        dbms_output.put_line(p_nombre || &apos; ... es de tipo CLAVE EXTERNA y hace referencia a: &apos; || v_nombre);
    elsif v_tipo=&apos;C&apos; then
        dbms_output.put_line(p_nombre || &apos; ... es de tipo CHECK y contiene la siguiente comprobacion: &apos; || v_condicion); 
    end if;
end;
/

CREATE OR REPLACE PROCEDURE listar_restricciones(p_tabla user_constraints.table_name%type)
is
    cursor c_columnas is
        SELECT constraint_name,column_name
        from user_cons_columns
        where table_name=p_tabla;
begin
    dbms_output.put_line(&apos;Restricciones de la tabla &apos; || p_tabla);
    for v_columna in c_columnas loop
        listar_restriccion(v_columna.constraint_name,p_tabla);
        dbms_output.put_line(CHR(9)||&apos;Hace referencia a la columna -&amp;gt; &apos; || v_columna.column_name);
    end loop;
end;
/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En la comprobación introduzco una tabla diferente al esquema SCOTT ya que tiene más restricciones:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OMoSRQz.png&quot; alt=&quot;p10&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Realiza al menos dos de los ejercicios anteriores en Postgres usando PL/pgSQL.&lt;/h2&gt;
&lt;h2&gt;Ejercicio 1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE or replace PROCEDURE mostrarnombresalario() AS $$
DECLARE
    v_nombre emp.ename%type;
    v_salario emp.sal%type;
BEGIN
    select ename,sal into v_nombre,v_salario from emp where empno=7782;
    RAISE NOTICE &apos;El nombre del empleado es %, y su salario es %&apos;, v_nombre,v_salario;
END;
$$ LANGUAGE plpgsql;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/GifaOGt.png&quot; alt=&quot;p11&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Ejercicio 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE or replace PROCEDURE mostrarnombresalario2(p_empno emp.empno%type) AS $$
DECLARE
    v_nombre emp.ename%type;
BEGIN
    select ename into v_nombre from emp where empno=7782;
    RAISE NOTICE &apos;El nombre del empleado cuyo codigo es %, es %&apos;, p_empno,v_nombre;
END;
$$ LANGUAGE plpgsql;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/OKfLC2X.png&quot; alt=&quot;p12&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Práctica 2: Interconexión de Servidores de Bases de Datos</title><link>https://www.robertops.com/posts/2022-11-27_practica2_abd/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-11-27_practica2_abd/</guid><pubDate>Sun, 27 Nov 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Las interconexiones de servidores de bases de datos son operaciones que pueden ser muy útiles en
diferentes contextos. Básicamente, se trata de acceder a datos que no están almacenados en nuestra base
de datos, pudiendo combinarlos con los que ya tenemos.
En esta práctica veremos varias formas de crear un enlace entre distintos servidores de bases de datos.
Se pide:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Realizar un enlace entre dos servidores de bases de datos ORACLE, explicando la configuración
necesaria en ambos extremos y demostrando su funcionamiento.&lt;/li&gt;
&lt;li&gt;Realizar un enlace entre dos servidores de bases de datos Postgres, explicando la configuración
necesaria en ambos extremos y demostrando su funcionamiento.&lt;/li&gt;
&lt;li&gt;Realizar un enlace entre un servidor ORACLE y otro Postgres o MySQL empleando Heterogeneus
Services&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Enlace entre dos servidores ORACLE&lt;/h2&gt;
&lt;p&gt;En mi caso, las dos máquinas tienen la configuración siguiente; Tienen instalado Rocky 8 Oracle Database 19c Enterprise Edition, y tienen los siguientes nombres e IPs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;oracle-maquina1: 192.168.122.204&lt;/li&gt;
&lt;li&gt;oracle-maquina2: 192.168.122.80&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En ambas bases de datos existe el usuario roberto y están configuradas para el acceso remoto. Para comprobar que pueden acceder se utiliza el comando tnsping:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/gbnsKsB.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ahora, para que los servidores puedan conectarse entre ellos, tenemos que añadir los datos de las máquinas al fichero &lt;code&gt;/opt/oracle/product/19c/dbhome_1/network/admin/tnsnames.ora&lt;/code&gt;, dejándolos de la siguiente manera:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;En el fichero dentro de la máquina 1:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;MAQUINA2 =
    (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.122.80)(PORT = 1521))
        (CONNECT_DATA =
            (SERVER = DEDICATED)
            (SERVICE_NAME = ORCLCDB)
        )
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;En el fichero dentro de la máquina 2:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;MAQUINA1 =
    (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.122.80)(PORT = 1521))
        (CONNECT_DATA =
            (SERVER = DEDICATED)
            (SERVICE_NAME = ORCLCDB)
        )
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso hay que crear los enlaces haciendo referencia al usuario (roberto). los comandos se ejecutan dentro de oracle:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;En la máquina 1:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;create database link enlace2 connect to roberto identified by roberto using &apos;maquina2&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;En la máquina 2:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;create database link enlace1 connect to roberto identified by roberto using &apos;maquina1&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos comprobar que se puede acceder remotamente a las tablas:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/iYYIaiL.png&quot; alt=&quot;2&quot; /&gt;
&lt;img src=&quot;https://i.imgur.com/2Cu5V0W.png&quot; alt=&quot;3&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Enlace entre dos servidores Postgres&lt;/h2&gt;
&lt;p&gt;En mi caso, las dos máquinas tienen la configuración siguiente; Tienen instalado Debian 11,MariaDB y PostgreSQL, y tienen los siguientes nombres e IPs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mariadb-maquina1: 192.168.122.97&lt;/li&gt;
&lt;li&gt;mariadb-maquina2: 192.168.122.90&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En ambas bases de datos existe el usuario roberto y están configuradas para el acceso remoto.&lt;/p&gt;
&lt;p&gt;Ahora vamos a crear el enlace:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install postgresql-contrib
psql -d scott
create extension dblink;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez hecho eso, tenemos que ejecutar los siguientes comandos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Máquina 1:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT dblink_connect(&apos;dblink2&apos;,&apos;dbname=scott host=192.168.122.90 user=roberto password=roberto&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Máquina 2:&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;SELECT dblink_connect(&apos;dblink1&apos;,&apos;dbname=scott host=192.168.122.97 user=roberto password=roberto&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso, podemos realizar consultas entre máquinas:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/qzn6uGb.png&quot; alt=&quot;4&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Enlace entre un servidor ORACLE y uno Postgres&lt;/h2&gt;
&lt;p&gt;En este caso las máquinas que voy a conectar son oracle-maquina1 y mariadb-maquina2, por lo que las direcciones IP son las siguientes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;oracle-maquina1: 192.168.122.204&lt;/li&gt;
&lt;li&gt;mariadb-maquina2: 192.168.122.90&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Conexión desde Postgres a ORACLE&lt;/h3&gt;
&lt;p&gt;Primero descargamos los siguientes paquetes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install libaio1 postgresql-server-dev-all build-essential git alien -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora tenemos que descargar el instantclient de oracle, se hace con los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://download.oracle.com/otn_software/linux/instantclient/218000/oracle-instantclient-basic-21.8.0.0.0-1.el8.x86_64.rpm
wget https://download.oracle.com/otn_software/linux/instantclient/218000/oracle-instantclient-devel-21.8.0.0.0-1.el8.x86_64.rpm
wget https://download.oracle.com/otn_software/linux/instantclient/218000/oracle-instantclient-sqlplus-21.8.0.0.0-1.el8.x86_64.rpm

sudo alien -i --scripts oracle-instantclient-*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez instalado, con el siguiente comando podemos conectarnos a la base de datos de oracle-maquina1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlplus roberto/roberto@192.168.122.204:1521/ORCLCDB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora tenemos que descargar y compilar &lt;code&gt;oracle_fdw&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget https://github.com/laurenz/oracle_fdw/archive/refs/tags/ORACLE_FDW_2_5_0.zip
unzip ORACLE_FDW_2_5_0.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Renombramos la carpeta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mv oracle_fdw-ORACLE_FDW_2_5_0/ oracle_fdw
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finalmente compilamos el programa:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd oracle_fdw
make
make install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, dentro de psql (y de la base de datos scott) creamos el enlace. Podemos comprobar que se ha creado con \dx:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE EXTENSION oracle_fdw;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Creamos el schema en el que se van a importar las bases de datos de Oracle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create schema oracle;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configuramos un servidor foráneo que haga referencia a la base de datos Oracle en la otra máquina:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create server oracle foreign data wrapper oracle_fdw options (dbserver &apos;//192.168.122.204/ORCLCDB&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora creamos una equivalencia entre un usuario local y el del servidor (aunque en este caso se llaman igual):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create user mapping for roberto server oracle options (user &apos;roberto&apos;, password &apos;roberto&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le damos permisos al usuario local sobre el schema de oracle:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;grant all privileges on schema oracle to roberto;
grant all privileges on foreign server oracle to roberto;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Iniciamos sesión en psql con el usuario que hemos indicado en los pasos anteriores, e importamos el esquema remoto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;psql -U roberto -W -d scott
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;import foreign schema &quot;ROBERTO&quot; from server oracle into oracle;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/EypaWU0.png&quot; alt=&quot;5&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Conexión desde Oracle a Postgres&lt;/h3&gt;
&lt;p&gt;Para realizar la conexión vamos a utilizar ODBC (Open Database Conectivity); y en este caso ya que vamos a realizar la conexión con PostgreSQL, también tenemos que descargar el paquete específico:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dnf install unixODBC postgresql-odbc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora en el fichero de configuración &lt;code&gt;/etc/odbcinst.ini&lt;/code&gt; comentamos todas las líneas menos las referentes a postgresql, quedando el fichero de la siguiente manera:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[PostgreSQL]
Description     = ODBC for PostgreSQL
Driver          = /usr/lib/psqlodbcw.so
Setup           = /usr/lib/libodbcpsqlS.so
Driver64        = /usr/lib64/psqlodbcw.so
Setup64         = /usr/lib64/libodbcpsqlS.so
FileUsage       = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para configurar el acceso a la máquina con postgres tenemos que crear en el fichero &lt;code&gt;/etc/odbc.ini&lt;/code&gt; la siguiente entrada:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[PSQLU]

Debug = 0
CommLog = 0
ReadOnly = 0
Driver = PostgreSQL
Servername = 192.168.122.90
Username = roberto
Password = roberto
Port = 5432
Database = scott
Trace = 0
TraceFile = /tmp/sql.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos comprobar que funciona con isql PSQLU.&lt;/p&gt;
&lt;p&gt;Ahora hay que crear la configuración para que Oracle pueda hacer uso del driver. Para ello tenemos que crear el fichero &lt;code&gt;/opt/oracle/product/19c/dbhome_1/hs/admin/initPSQLU.ora&lt;/code&gt; con el siguiente contenido:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HS_FDS_CONNECT_INFO = PSQLU
HS_FDS_TRACE_LEVEL = DEBUG
HS_FDS_SHAREABLE_NAME = /usr/lib64/psqlodbcw.so
HS_LANGUAGE = AMERICAN_AMERICA.WE8ISO8859P1
set ODBCINI=/etc/odbc.ini
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;También hay que configurar el listener &lt;code&gt;/opt/oracle/product/19c/dbhome_1/network/admin/listener.ora&lt;/code&gt;, añadiendo lo siguiente al final:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SID_LIST_LISTENER =
    (SID_LIST =
        (SID_DESC =
            (SID_NAME = PSQLU)
            (ORACLE_HOME=/opt/oracle/product/19c/dbhome_1)
            (PROGRAM=dg4odbc)
        )
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y añadimos una entrada de conexión a &lt;code&gt;/opt/oracle/product/19c/dbhome_1/network/admin/tnsnames.ora&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PSQLU =
    (DESCRIPTION=
        (ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))
        (CONNECT_DATA=(SID=PSQLU))
        (HS=OK)
    )
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora reiniciamos el listener y creamos el enlace dentro de oracle.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create database link postgreslink connect to &quot;roberto&quot; identified by&quot;roberto&quot; using &apos;PSQLU&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ya está creado el enlace. Ahora para comprobar que funciona, podemos hacer una consulta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT &quot;ename&quot; FROM &quot;emp&quot;@postgreslink;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rSga9bn.png&quot; alt=&quot;6&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Aplicacion web MySQL</title><link>https://www.robertops.com/posts/2022-10-31_practica1_abd/aplicacion_web_mysql/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-31_practica1_abd/aplicacion_web_mysql/</guid><pubDate>Mon, 31 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;instalamos php:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt install apache2 libapache2-mod-php php php-mysql
apt install php-zip php-gd php-mbstring phpmyadmin -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configuración&lt;/h2&gt;
&lt;p&gt;En la instalación de phpmyadmin elegimos como servidor &lt;strong&gt;apache2&lt;/strong&gt;, después, cuando nos pregunta por una contraseña, le damos a Sí, e introducimos una contraseña para phpmyadmin dos veces&lt;/p&gt;
&lt;p&gt;Una vez instalado, para poder acceder con un usuario concreto, tenemos que hacer lo siguiente:&lt;/p&gt;
&lt;p&gt;Entramos en el directorio de phpmyadmin y copiamos un fichero de ejemplo para utilizarlo como configuración:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd /usr/share/phpmyadmin/
cp config.sample.inc.php config.inc.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;en el fichero &lt;code&gt;config.inc.php&lt;/code&gt; tenemos que añadir a &lt;code&gt;blowfish_secret&lt;/code&gt; una cadena de 30 caracteres, y tenemos que descomentar las lineas de &lt;code&gt;controluser&lt;/code&gt;y &lt;code&gt;controlpass&lt;/code&gt; y añadirles las credenciales.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;fichero_conf.png&quot; alt=&quot;fichero_conf&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Una vez realizada la configuración, reiniciamos el servicio:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl reload apache2
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Prueba&lt;/h2&gt;
&lt;p&gt;y podemos acceder a la página de administración en &lt;code&gt;http://IPmaquina/phpmyadmin/&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;login.png&quot; alt=&quot;login&quot; /&gt;
&lt;img src=&quot;home.png&quot; alt=&quot;home&quot; /&gt;
&lt;img src=&quot;tablas.png&quot; alt=&quot;tablas&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Aplicacion web Oracle</title><link>https://www.robertops.com/posts/2022-10-31_practica1_abd/aplicacion_web_oracle/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-31_practica1_abd/aplicacion_web_oracle/</guid><pubDate>Sun, 30 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Aplicación web Oracle programada en python.&lt;/p&gt;
&lt;h2&gt;Preparación&lt;/h2&gt;
&lt;p&gt;Antes de crear el fichero que va a ser la aplicación, vamos a crear un &lt;strong&gt;Entorno virtual&lt;/strong&gt; (Venv) de python para contener ahí los módulos que nos descarguemos&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir aplicacion_web_oracle &amp;amp;&amp;amp; cd aplicacion_web_oracle
python3 -m venv /home/roberto/aplicacion_web_oracle
source bin/activate
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a descargar los módulos necesarios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pip install cx_oracle
pip install flask
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora, usando &lt;strong&gt;flask&lt;/strong&gt;, se ha escrito la siguiente aplicación sencilla, que permite, al introducirle el nombre de un empleado en la dirección &lt;code&gt;/emp/&lt;/code&gt;, mostrar los datos de dicho empleado. Es importante que, cuando se define &lt;strong&gt;pool&lt;/strong&gt; se introduzcan los mismo datos que en el acceso remoto a oracle, ya que esta aplicación se ha creado desde una máquina distina a la de oracle.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import os
import sys
import cx_Oracle
from flask import Flask

def init_session(connection, requestedTag_ignored):
    cursor = connection.cursor()
    cursor.execute(&quot;&quot;&quot;
        ALTER SESSION SET
          TIME_ZONE = &apos;UTC&apos;
          NLS_DATE_FORMAT = &apos;YYYY-MM-DD HH24:MI&apos;&quot;&quot;&quot;)

# start_pool(): starts the connection pool
def start_pool():
    pool_min = 4
    pool_max = 4
    pool_inc = 0
    pool_gmd = cx_Oracle.SPOOL_ATTRVAL_WAIT

    print(&quot;Connecting to&quot;, &quot;192.168.122.105:1521/ORCLCDB&quot;)

    pool = cx_Oracle.SessionPool(user=&quot;roberto&quot;,
                                 password=&quot;roberto&quot;,
                                 dsn=&quot;192.168.122.105:1521/ORCLCDB&quot;,
                                 min=pool_min,
                                 max=pool_max,
                                 increment=pool_inc,
                                 threaded=True,
                                 getmode=pool_gmd,
                                 sessionCallback=init_session)

    return pool

app = Flask(__name__)

@app.route(&apos;/&apos;)
def index():
    return &quot;Entra en /emp/nombreempleado para ver los datos del empleado&quot;


# Show the username for a given id
@app.route(&apos;/emp/&amp;lt;string:name&amp;gt;&apos;)
def show_username(name):
    connection = pool.acquire()
    cursor = connection.cursor()
    cursor.execute(&quot;select * from emp where ename = (:name)&quot;, [name])
    r = cursor.fetchone()
    r = str(r)
    return (r)

################################################################################
#
# Initialization is done once at startup time
#
if __name__ == &apos;__main__&apos;:

    # Start a pool of connections
    pool = start_pool()


    # Start a webserver
    app.run(port=int(os.environ.get(&apos;PORT&apos;, &apos;8080&apos;)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;prueba1.png&quot; alt=&quot;prueba1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;prueba2.png&quot; alt=&quot;prueba2&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Conexiones desde clientes a servidores con bases de datos</title><link>https://www.robertops.com/posts/2022-10-31_practica1_abd/conexiones_bd/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-31_practica1_abd/conexiones_bd/</guid><pubDate>Sun, 30 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Vamos a realizar conexiones desde los clientes de BBDD a sus respectivos servidores.&lt;/p&gt;
&lt;h2&gt;Oracle&lt;/h2&gt;
&lt;p&gt;En el lado del servidor, tiene que estar activo oracle (&lt;code&gt;startup&lt;/code&gt;), también como se ha hecho en la instalación tiene que estar activado el listener&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lsnrctl start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y el firewall tiene que permitir el puerto &lt;code&gt;1521&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;firewall-cmd --permanent --add-port=1521/tcp
firewall-cmd --reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para conectarnos de manera remota a oracle, tenemos que descargar en el lado del cliente &lt;strong&gt;instantclient&lt;/strong&gt;, del siguiente &lt;a href=&quot;https://www.oracle.com/es/database/technologies/instant-client/linux-x86-64-downloads.html&quot;&gt;enlace&lt;/a&gt;. En él hay varios enlaces de descarga. Los que nos interesan son:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://download.oracle.com/otn_software/linux/instantclient/218000/oracle-instantclient-basic-21.8.0.0.0-1.el8.x86_64.rpm&lt;/li&gt;
&lt;li&gt;https://download.oracle.com/otn_software/linux/instantclient/218000/oracle-instantclient-devel-21.8.0.0.0-1.el8.x86_64.rpm&lt;/li&gt;
&lt;li&gt;https://download.oracle.com/otn_software/linux/instantclient/218000/oracle-instantclient-sqlplus-21.8.0.0.0-1.el8.x86_64.rpm&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En mi caso, el cliente es debian, así que tenemos que instalar los paquetes usando alien:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo alien -i --scripts oracle-instantclient-*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez instalados, podemos acceder al servidor con &lt;code&gt;sqlplus&lt;/code&gt;, donde la sintaxis es&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlplus [USUARIO]/[CONTRASEÑA]@[IP:PUERTO]/[NOMBREBD]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;sqlplus roberto/roberto@192.168.122.105:1521/ORCLCDB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;oracle.png&quot; alt=&quot;oracle&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En la captura se puede ver una consulta de prueba para comprobar que se ha accedido a la base de datos correctamente.&lt;/p&gt;
&lt;h2&gt;MariaDB&lt;/h2&gt;
&lt;p&gt;Para conectarnos tenemos que instalar el paquete &lt;code&gt;mariadb-client&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install mariadb-client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez instalado, ya podemos acceder al servidor. El comando tiene la siguiente sintaxis:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mariadb --host FULLY_QUALIFIED_DOMAIN_NAME --port TCP_PORT \
     --user DATABASE_USER --password \
     --ssl-verify-server-cert \
     --ssl-ca PATH_TO_PEM_FILE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Lo utilizamos con las opciones de nuestro servidor:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mariadb --host 192.168.122.78 --port 3306 \
--user remoto --password
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;mariadb.png&quot; alt=&quot;mariadb&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;PostgreSQL&lt;/h2&gt;
&lt;p&gt;Para conectarnos tenemos que instalar el paquete &lt;code&gt;postgresql-client&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install postgresql-client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y nos conectamos al servidor usando el comando &lt;code&gt;psql&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;psql --host 192.168.122.78 --user roberto -d scott
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;postgre.png&quot; alt=&quot;postgre&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Instalación de MariaDB y PostgreSQL en Debian</title><link>https://www.robertops.com/posts/2022-10-31_practica1_abd/instalacion_mariadb_postgresql/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-31_practica1_abd/instalacion_mariadb_postgresql/</guid><pubDate>Wed, 26 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;MariaDB&lt;/h2&gt;
&lt;p&gt;El paquete de MariaDB se encuentra en los repositorios de Debian, por lo que podemos instalarlo directamente con apt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install mariadb-server -y 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Entramos en la base de datos como root:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql -u root -p
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración para acceso remoto&lt;/h3&gt;
&lt;p&gt;Para poder acceder remotamente tenemos que modificar el archivo de configuración &lt;code&gt;/etc/mysql/mariadb.conf.d/50-server.cnf&lt;/code&gt;, buscando la línea de &lt;strong&gt;bind-address&lt;/strong&gt; y poniendo lo siguiente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bind-address            = 0.0.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A continuación reiniciamos el servicio de mariadb:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart mariadb.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dentro de mariadb, tenemos que crear un usuario para el acceso remoto (el &lt;code&gt;%&lt;/code&gt; es un comodín para indicar que se pueda acceder desde cualquier dirección):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON *.* TO &apos;remoto&apos;@&apos;%&apos;
IDENTIFIED BY &apos;remoto&apos; WITH GRANT OPTION;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Creación de usuario&lt;/h3&gt;
&lt;p&gt;Creamos un usuario con todos los privilegios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON *.* TO &apos;roberto&apos;@&apos;localhost&apos;
IDENTIFIED BY &apos;roberto&apos; WITH GRANT OPTION;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora podemos entrar con el usuario roberto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql -u roberto -p
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Creación de tablas&lt;/h3&gt;
&lt;p&gt;Vamos a crear el esquema scott en mysql:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create database scott;
use scott;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE IF NOT EXISTS `dept` (
  `DEPTNO` int(11) DEFAULT NULL,
  `DNAME` varchar(14) DEFAULT NULL,
  `LOC` varchar(13) DEFAULT NULL
);
INSERT INTO `dept` (`DEPTNO`, `DNAME`, `LOC`) VALUES
(10, &apos;ACCOUNTING&apos;, &apos;NEW YORK&apos;),
(20, &apos;RESEARCH&apos;, &apos;DALLAS&apos;),
(30, &apos;SALES&apos;, &apos;CHICAGO&apos;),
(40, &apos;OPERATIONS&apos;, &apos;BOSTON&apos;);
CREATE TABLE IF NOT EXISTS `emp` (
  `EMPNO` int(11) NOT NULL,
  `ENAME` varchar(10) DEFAULT NULL,
  `JOB` varchar(9) DEFAULT NULL,
  `MGR` int(11) DEFAULT NULL,
  `HIREDATE` date DEFAULT NULL,
  `SAL` int(11) DEFAULT NULL,
  `COMM` int(11) DEFAULT NULL,
  `DEPTNO` int(11) DEFAULT NULL
);
INSERT INTO `emp` (`EMPNO`, `ENAME`, `JOB`, `MGR`, `HIREDATE`, `SAL`, `COMM`, `DEPTNO`) VALUES
(7369, &apos;SMITH&apos;, &apos;CLERK&apos;, 7902, &apos;1980-12-17&apos;, 800, NULL, 20),
(7499, &apos;ALLEN&apos;, &apos;SALESMAN&apos;, 7698, &apos;1981-02-20&apos;, 1600, 300, 30),
(7521, &apos;WARD&apos;, &apos;SALESMAN&apos;, 7698, &apos;1981-02-22&apos;, 1250, 500, 30),
(7566, &apos;JONES&apos;, &apos;MANAGER&apos;, 7839, &apos;1981-04-02&apos;, 2975, NULL, 20),
(7654, &apos;MARTIN&apos;, &apos;SALESMAN&apos;, 7698, &apos;1981-09-28&apos;, 1250, 1400, 30),
(7698, &apos;BLAKE&apos;, &apos;MANAGER&apos;, 7839, &apos;1981-05-01&apos;, 2850, NULL, 30),
(7782, &apos;CLARK&apos;, &apos;MANAGER&apos;, 7839, &apos;1981-06-09&apos;, 2450, NULL, 10),
(7788, &apos;SCOTT&apos;, &apos;ANALYST&apos;, 7566, &apos;1982-12-09&apos;, 3000, NULL, 20),
(7839, &apos;KING&apos;, &apos;PRESIDENT&apos;, NULL, &apos;1981-11-17&apos;, 5000, NULL, 10),
(7844, &apos;TURNER&apos;, &apos;SALESMAN&apos;, 7698, &apos;1980-09-08&apos;, 1500, 0, 30),
(7876, &apos;ADAMS&apos;, &apos;CLERK&apos;, 7788, &apos;1983-01-12&apos;, 1100, NULL, 20),
(7900, &apos;JAMES&apos;, &apos;CLERK&apos;, 7698, &apos;1981-12-03&apos;, 950, NULL, 30),
(7902, &apos;FORD&apos;, &apos;ANALYST&apos;, 7566, &apos;1981-12-03&apos;, 3000, NULL, 20),
(7934, &apos;MILLER&apos;, &apos;CLERK&apos;, 7782, &apos;1982-01-23&apos;, 1300, NULL, 10);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;PostgreSQL&lt;/h2&gt;
&lt;p&gt;El paquete de PostgreSQL se encuentra en los repositorios de Debian, por lo que podemos instalarlo directamente con apt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install postgreSQL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Accedemos al usuario postgres:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;su postgres
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración para acceso remoto&lt;/h3&gt;
&lt;p&gt;Para poder acceder remotamente tenemos que modificar el archivo de configuración &lt;code&gt;/etc/postgresql/13/main/postgresql.conf&lt;/code&gt;, buscando la línea de &lt;strong&gt;listen_addresses&lt;/strong&gt; y poniendo lo siguiente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;listen_addresses = &apos;*&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;También, al final del fichero &lt;code&gt;/etc/postgresql/13/main/pg_hba.conf&lt;/code&gt; tenemos que añadir las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;host    all      all              0.0.0.0/0                    md5
host    all      all              ::/0                         md5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reiniciamos el servicio para que los cambios tengan efecto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;systemctl restart postgresql.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora vamos a crear un usuario con contraseña para acceder desde fuera con el siguiente comando dentro de &lt;code&gt;psql&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;create user roberto with superuser password &apos;roberto&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Creación de tablas&lt;/h3&gt;
&lt;p&gt;Creamos la base de datos y accedemos a &lt;code&gt;psql&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;createdb scott
psql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dentro del intérprete de comandos añadimos el esquema scott:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\c scott

create table dept (
  deptno integer,
  dname  text,
  loc    text,
  constraint pk_dept primary key (deptno)
);
create table emp (
  empno    integer,
  ename    text,
  job      text,
  mgr      integer,
  hiredate date,
  sal      integer,
  comm     integer,
  deptno   integer,
  constraint pk_emp primary key (empno),
  constraint fk_mgr foreign key (mgr) references emp (empno),
  constraint fk_deptno foreign key (deptno) references dept (deptno)
);
insert into dept (deptno,  dname,        loc)
       values    (10,     &apos;ACCOUNTING&apos;, &apos;NEW YORK&apos;),
                 (20,     &apos;RESEARCH&apos;,   &apos;DALLAS&apos;),
                 (30,     &apos;SALES&apos;,      &apos;CHICAGO&apos;),
                 (40,     &apos;OPERATIONS&apos;, &apos;BOSTON&apos;);
insert into emp (empno, ename,    job,        mgr,   hiredate,     sal, comm, deptno)
       values   (7369, &apos;SMITH&apos;,  &apos;CLERK&apos;,     7902, &apos;1980-12-17&apos;,  800, NULL,   20),
                (7499, &apos;ALLEN&apos;,  &apos;SALESMAN&apos;,  7698, &apos;1981-02-20&apos;, 1600,  300,   30),
                (7521, &apos;WARD&apos;,   &apos;SALESMAN&apos;,  7698, &apos;1981-02-22&apos;, 1250,  500,   30),
                (7566, &apos;JONES&apos;,  &apos;MANAGER&apos;,   7839, &apos;1981-04-02&apos;, 2975, NULL,   20),
                (7654, &apos;MARTIN&apos;, &apos;SALESMAN&apos;,  7698, &apos;1981-09-28&apos;, 1250, 1400,   30),
                (7698, &apos;BLAKE&apos;,  &apos;MANAGER&apos;,   7839, &apos;1981-05-01&apos;, 2850, NULL,   30),
                (7782, &apos;CLARK&apos;,  &apos;MANAGER&apos;,   7839, &apos;1981-06-09&apos;, 2450, NULL,   10),
                (7788, &apos;SCOTT&apos;,  &apos;ANALYST&apos;,   7566, &apos;1982-12-09&apos;, 3000, NULL,   20),
                (7839, &apos;KING&apos;,   &apos;PRESIDENT&apos;, NULL, &apos;1981-11-17&apos;, 5000, NULL,   10),
                (7844, &apos;TURNER&apos;, &apos;SALESMAN&apos;,  7698, &apos;1981-09-08&apos;, 1500,    0,   30),
                (7876, &apos;ADAMS&apos;,  &apos;CLERK&apos;,     7788, &apos;1983-01-12&apos;, 1100, NULL,   20),
                (7900, &apos;JAMES&apos;,  &apos;CLERK&apos;,     7698, &apos;1981-12-03&apos;,  950, NULL,   30),
                (7902, &apos;FORD&apos;,   &apos;ANALYST&apos;,   7566, &apos;1981-12-03&apos;, 3000, NULL,   20),
                (7934, &apos;MILLER&apos;, &apos;CLERK&apos;,     7782, &apos;1982-01-23&apos;, 1300, NULL,   10);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;MongoDB&lt;/h2&gt;
&lt;p&gt;Como indica la &lt;a href=&quot;https://www.mongodb.com/docs/v6.0/tutorial/install-mongodb-on-debian/&quot;&gt;documentación oficial&lt;/a&gt;, Primero tenemos que añadir la clave a nuestros repositorios:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install gnupg gnupg2 gnupg1
wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ahora añadimos el repositorio de mongo a los repositorios de debian:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;deb http://repo.mongodb.org/apt/debian buster/mongodb-org/6.0 main&quot; | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Y ahora, con los repositorios ya añadidos, instalamos mongodb:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install -y mongodb-org
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez instalado, iniciamos el servicio y activamos el inicio automático:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl start mongod
sudo systemctl enable mongod
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configuración de acceso remoto&lt;/h3&gt;
&lt;p&gt;Tenemos que editar el fichero &lt;code&gt;/etc/mongod.conf&lt;/code&gt; y comentar la línea &lt;code&gt;bindIP&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# bindIp: 127.0.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y reiniciamos el servicio&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart mongod
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Creación de documentos&lt;/h3&gt;
&lt;p&gt;En este caso voy a insertar los datos de mi &lt;a href=&quot;https://github.com/robertorodriguez98/proyectoMongoDB/&quot;&gt;proyecto de MongoDB&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mongoimport --db=yugioh --collection=prankkids --jsonArray --type json --file=prankkids.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Podemos hacer una consulta para comprobar que se ha creado correctamente:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;use yugioh
db.prankkids.find({&quot;card_sets.set_name&quot;:&quot;Hidden Summoners&quot;}).count()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;mongo.png&quot; alt=&quot;mongo&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Instalación Oracle 19c en Rocky linux 8</title><link>https://www.robertops.com/posts/2022-10-31_practica1_abd/instalacion_oracle/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-31_practica1_abd/instalacion_oracle/</guid><pubDate>Wed, 26 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Se va a realizar la instalación de Oracle 19c en Rocky linux 8, debido a su mayor compatibilidad con el programa y menor número de fallos que con Debian 11.&lt;/p&gt;
&lt;h2&gt;Pasos previos&lt;/h2&gt;
&lt;p&gt;Los siguientes pasos se deben ejecutar como usuario &lt;strong&gt;root&lt;/strong&gt;.
Actualizamos el sistema:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dnf makecache
dnf update -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si al ejecutarlo se actualiza el &lt;strong&gt;kernel&lt;/strong&gt;, deberíamos reiniciar la máquina.&lt;/p&gt;
&lt;p&gt;como indica la &lt;a href=&quot;https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/running-rpm-packages-to-install-oracle-database.html#GUID-BB7C11E3-D385-4A2F-9EAF-75F4F0AACF02&quot;&gt;documentación&lt;/a&gt;, instalamos los requisitos previos. Sin embargo, al no estar en centos 7, tenemos que instalar manualmente unos paquetes&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dnf install -y bc binutils compat-openssl10 elfutils-libelf glibc glibc-devel ksh libaio libXrender libX11 libXau libXi libXtst libgcc libnsl libstdc++ libxcb libibverbs make policycoreutils policycoreutils-python-utils smartmontools sysstat libnsl2 net-tools nfs-utils unzip
dnf install -y http://mirror.centos.org/centos/7/os/x86_64/Packages/compat-libcap1-1.10-7.el7.x86_64.rpm
dnf install -y http://mirror.centos.org/centos/7/os/x86_64/Packages/compat-libstdc++-33-3.2.3-72.el7.x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Después descargamos los requisitos previos y los instalamos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -o oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm https://yum.oracle.com/repo/OracleLinux/OL7/latest/x86_64/getPackage/oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm
yum -y localinstall oracle-database-preinstall-19c-1.0-1.el7.x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Configuramos el firewall&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;firewall-cmd --permanent --add-port=1521/tcp
firewall-cmd --reload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;configuramos el &lt;strong&gt;target mode&lt;/strong&gt; de SELinux a permisivo:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sed -i &apos;s/^SELINUX=.*/SELINUX=permissive/g&apos; /etc/selinux/config
setenforce permissive
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;Ahora descargamos el paquete rpm de la &lt;a href=&quot;https://www.oracle.com/es/database/technologies/oracle19c-linux-downloads.html&quot;&gt;página oficial de oracle&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;descarga.png&quot; alt=&quot;descarga&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yum -y localinstall oracle-database-ee-19c-1.0-1.x86_64.rpm 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;instalacion.png&quot; alt=&quot;instalacion&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Como nos indica al final de la instalación, creamos la base de datos de pruebas ejecutando el siguiente script:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/etc/init.d/oracledb_ORCLCDB-19c configure
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;script.png&quot; alt=&quot;script&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Tras la ejecución, tenemos que iniciar sesión con el usuario &lt;strong&gt;oracle&lt;/strong&gt; que se ha creado durante la misma, Y añadirle las siguientes variables al fichero &lt;code&gt;.bash_profile&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight bash &quot;linenos=table&quot; &amp;gt;}}
umask 022
export ORACLE_SID=ORCLCDB
export ORACLE_BASE=/opt/oracle/oradata
export ORACLE_HOME=/opt/oracle/product/19c/dbhome_1
export PATH=$PATH:$ORACLE_HOME/bin
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;p&gt;Recargamos el fichero para que las variables tengan efecto:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.bash_profile
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras este paso ya está instalado &lt;strong&gt;oracle&lt;/strong&gt;, Ahora sigue crear la base de datos. Primero activamos el listener:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lsnrctl start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para facilitar la utilización de &lt;code&gt;sqlplus&lt;/code&gt; vamos a instalar el paquete &lt;code&gt;rlwrap&lt;/code&gt;, que permite que utilicemos el cursor, tanto para desplazarnos por las líneas como para rescatar comandos.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dnf install epel-release
dnf install rlwrap -y
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ahora creamos el siguiente alias en &lt;code&gt;~/.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias sqlplus=&apos;rlwrap sqlplus&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Configuración&lt;/h2&gt;
&lt;p&gt;Primero nos conectamos a la base de datos como &lt;strong&gt;sysdba&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlplus / as sysdba
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;y podemos comprobar la versión de oracle con la siguiente consulta:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT instance_name, host_name, version, startup_time FROM v$instance;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;versionora.png&quot; alt=&quot;versionora&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Creación de usuario con privilegios&lt;/h3&gt;
&lt;p&gt;Vamos a crear un usuario para poder acceder a la base de datos sin utilizar el &lt;strong&gt;sysdba&lt;/strong&gt;, con los siguientes comandos. Antes de crear el usuario, tenemos  que activar &lt;code&gt;_ORACLE_SCRIPT&lt;/code&gt; para que se puedan ejecutar sin errores los siguientes comandos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alter session set &quot;_ORACLE_SCRIPT&quot;=true;
CREATE USER roberto IDENTIFIED BY roberto;
GRANT ALL PRIVILEGES TO roberto;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Una vez creado el usuario podemos conectarnos con él utilizando el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sqlplus roberto/roberto
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Creación de tablas&lt;/h2&gt;
&lt;p&gt;Vamos a introducir a modo de prueba, el esquema &lt;strong&gt;scott&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE DEPT
(
 DEPTNO NUMBER(2),
 DNAME VARCHAR2(14),
 LOC VARCHAR2(13),
 CONSTRAINT PK_DEPT PRIMARY KEY (DEPTNO)
);
CREATE TABLE EMP
(
 EMPNO NUMBER(4),
 ENAME VARCHAR2(10),
 JOB VARCHAR2(9),
 MGR NUMBER(4),
 HIREDATE DATE,
 SAL NUMBER(7, 2),
 COMM NUMBER(7, 2),
 DEPTNO NUMBER(2),
 CONSTRAINT FK_DEPTNO FOREIGN KEY (DEPTNO) REFERENCES DEPT (DEPTNO),
 CONSTRAINT PK_EMP PRIMARY KEY (EMPNO)
);
INSERT INTO DEPT VALUES (10, &apos;ACCOUNTING&apos;, &apos;NEW YORK&apos;);
INSERT INTO DEPT VALUES (20, &apos;RESEARCH&apos;, &apos;DALLAS&apos;);
INSERT INTO DEPT VALUES (30, &apos;SALES&apos;, &apos;CHICAGO&apos;);
INSERT INTO DEPT VALUES (40, &apos;OPERATIONS&apos;, &apos;BOSTON&apos;);
INSERT INTO EMP VALUES(7369, &apos;SMITH&apos;, &apos;CLERK&apos;, 7902,TO_DATE(&apos;17-DIC-1980&apos;, &apos;DD-MON-YYYY&apos;), 800, NULL, 20);
INSERT INTO EMP VALUES(7499, &apos;ALLEN&apos;, &apos;SALESMAN&apos;, 7698,TO_DATE(&apos;20-FEB-1981&apos;, &apos;DD-MON-YYYY&apos;), 1600, 300, 30);
INSERT INTO EMP VALUES(7521, &apos;WARD&apos;, &apos;SALESMAN&apos;, 7698,TO_DATE(&apos;22-FEB-1981&apos;, &apos;DD-MON-YYYY&apos;), 1250, 500, 30);
INSERT INTO EMP VALUES(7566, &apos;JONES&apos;, &apos;MANAGER&apos;, 7839,TO_DATE(&apos;2-ABR-1981&apos;, &apos;DD-MON-YYYY&apos;), 2975, NULL, 20);
INSERT INTO EMP VALUES(7654, &apos;MARTIN&apos;, &apos;SALESMAN&apos;, 7698,TO_DATE(&apos;28-SEP-1981&apos;, &apos;DD-MON-YYYY&apos;), 1250, 1400, 30);
INSERT INTO EMP VALUES(7698, &apos;BLAKE&apos;, &apos;MANAGER&apos;, 7839,TO_DATE(&apos;1-MAY-1981&apos;, &apos;DD-MON-YYYY&apos;), 2850, NULL, 30);
INSERT INTO EMP VALUES(7782, &apos;CLARK&apos;, &apos;MANAGER&apos;, 7839,TO_DATE(&apos;9-JUN-1981&apos;, &apos;DD-MON-YYYY&apos;), 2450, NULL, 10);
INSERT INTO EMP VALUES(7788, &apos;SCOTT&apos;, &apos;ANALYST&apos;, 7566,TO_DATE(&apos;09-DIC-1982&apos;, &apos;DD-MON-YYYY&apos;), 3000, NULL, 20);
INSERT INTO EMP VALUES(7839, &apos;KING&apos;, &apos;PRESIDENT&apos;, NULL,TO_DATE(&apos;17-NOV-1981&apos;, &apos;DD-MON-YYYY&apos;), 5000, NULL, 10);
INSERT INTO EMP VALUES(7844, &apos;TURNER&apos;, &apos;SALESMAN&apos;, 7698,TO_DATE(&apos;8-SEP-1981&apos;, &apos;DD-MON-YYYY&apos;), 1500, 0, 30);
INSERT INTO EMP VALUES(7876, &apos;ADAMS&apos;, &apos;CLERK&apos;, 7788,TO_DATE(&apos;12-ENE-1983&apos;, &apos;DD-MON-YYYY&apos;), 1100, NULL, 20);
INSERT INTO EMP VALUES(7900, &apos;JAMES&apos;, &apos;CLERK&apos;, 7698,TO_DATE(&apos;3-DIC-1981&apos;, &apos;DD-MON-YYYY&apos;), 950, NULL, 30);
INSERT INTO EMP VALUES(7902, &apos;FORD&apos;, &apos;ANALYST&apos;, 7566,TO_DATE(&apos;3-DIC-1981&apos;, &apos;DD-MON-YYYY&apos;), 3000, NULL, 20);
INSERT INTO EMP VALUES(7934, &apos;MILLER&apos;, &apos;CLERK&apos;, 7782,TO_DATE(&apos;23-ENE-1982&apos;, &apos;DD-MON-YYYY&apos;), 1300, NULL, 10);

COMMIT;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Las tablas se crean sin errores y se introducen los valores. Podemos realizar una consulta sencilla:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT ename
FROM emp
WHERE deptno = (SELECT deptno
                FROM dept
                WHERE dname = &apos;SALES&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;consulta.png&quot; alt=&quot;consulta&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Ansible: Escenario router-nat</title><link>https://www.robertops.com/posts/2022-10-18_ansible_router_nat/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-18_ansible_router_nat/</guid><pubDate>Tue, 18 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Creación del siguiente escenario con un cliente y un router-nat utilizando &lt;strong&gt;vagrant&lt;/strong&gt; y &lt;strong&gt;ansible&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;escenario.png&quot; alt=&quot;escenario&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Vagrant&lt;/h2&gt;
&lt;p&gt;Según el esquema de red que tenemos que replicar, vamos a crear una &lt;strong&gt;red muy aislada&lt;/strong&gt; entre las dos máquinas. También, el router tendrá un &lt;strong&gt;bridge&lt;/strong&gt; que será su puerta de enlace predeterminada. En el caso del cliente, esta configuración se hará más adelante. El &lt;code&gt;Vagrantfile&lt;/code&gt; queda de la siguiente manera:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight ruby &quot;linenos=table&quot;&amp;gt;}}
config.vm.define :router do |router|
router.vm.box = &quot;debian/bullseye64&quot;
router.vm.hostname = &quot;router&quot;
router.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true
router.vm.network :public_network,
:dev =&amp;gt; &quot;br0&quot;,
:mode =&amp;gt; &quot;bridge&quot;,
:type =&amp;gt; &quot;bridge&quot;,
use_dhcp_assigned_default_route: true
router.vm.network :private_network,
:libvirt__network_name =&amp;gt; &quot;red-muy-aislada&quot;,
:libvirt__dhcp_enabled =&amp;gt; false,
:ip =&amp;gt; &quot;192.168.0.1&quot;,
:libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
end
config.vm.define :cliente do |cliente|
cliente.vm.box = &quot;debian/bullseye64&quot;
cliente.vm.hostname = &quot;cliente&quot;
cliente.vm.synced_folder &quot;.&quot;, &quot;/vagrant&quot;, disabled: true
cliente.vm.network :private_network,
:libvirt__network_name =&amp;gt; &quot;red-muy-aislada&quot;,
:libvirt__dhcp_enabled =&amp;gt; false,
:ip =&amp;gt; &quot;192.168.0.2&quot;,
:libvirt__forward_mode =&amp;gt; &quot;veryisolated&quot;
end
end
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;p&gt;Creamos las máquinas virtuales:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vagrant up
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Ansible&lt;/h2&gt;
&lt;p&gt;Ahora nos movemos al directorio donde se va a encontrar el &lt;strong&gt;playbook&lt;/strong&gt; (&lt;code&gt;ansible/&lt;/code&gt;). empezamos por el fichero &lt;code&gt;hosts&lt;/code&gt;;&lt;/p&gt;
&lt;h3&gt;hosts&lt;/h3&gt;
&lt;p&gt;El fichero &lt;code&gt;hosts&lt;/code&gt; contiene la información de las máquinas en las que vamos a realizar el despliegue, pero antes de crearlo necesitamos conseguir las direcciones IPs que tienen asignadas en la interfaz &lt;strong&gt;eth0&lt;/strong&gt; (la de Vagrant). Para ello usamos el siguiente comando:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight bash &quot;hl_lines=2&quot; &amp;gt;}}
$ vagrant ssh cliente -c &quot;hostname -I | cut -d&apos; &apos; -f1&quot; 2&amp;gt;/dev/null
192.168.121.77
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight bash &quot;hl_lines=6&quot; &amp;gt;}}
$ vagrant ssh router -c &quot;hostname -I | cut -d&apos; &apos; -f1&quot; 2&amp;gt;/dev/null
==&amp;gt; router: You assigned a static IP ending in &quot;.1&quot; to this machine.
==&amp;gt; router: This is very often used by the router and can cause the
==&amp;gt; router: network to not work properly. If the network doesn&apos;t work
==&amp;gt; router: properly, try changing this IP.
192.168.121.146
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;p&gt;Nos aparece una alerta ya que el router tiene una dirección IP acabada en &quot;.1&quot;, Lo ignoramos ya que la máquina es el &lt;strong&gt;router de la red&lt;/strong&gt;. Con la salida de ambos comandos tenemos las IPs de las máquinas y, añadiendo además las &lt;strong&gt;rutas de las claves privadas&lt;/strong&gt;, podemos crear el fichero:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight yaml &quot;linenos=table,hl_lines=6 10&quot; &amp;gt;}}
all:
children:
maquinas:
hosts:
cliente:
ansible_ssh_host: 192.168.121.77
ansible_ssh_user: vagrant
ansible_ssh_private_key_file: ../.vagrant/machines/cliente/libvirt/private_key
router:
ansible_ssh_host: 192.168.121.146
ansible_ssh_user: vagrant
ansible_ssh_private_key_file: ../.vagrant/machines/router/libvirt/private_key
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;p&gt;Ahora podemos crear las máquinas con &lt;code&gt;vagrant up&lt;/code&gt;:
&lt;img src=&quot;vagrant.png&quot; alt=&quot;vagrant&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;site.yaml&lt;/h3&gt;
&lt;p&gt;Tras eso creamos el fichero que contiene la información de asignación de roles, que deben ser de la siguiente manera:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;common&lt;/strong&gt;: Estas tareas se deben ejecutar en todos los nodos: actualizar los paquetes y añadir tu clave pública a la máquinas para poder acceder a ellas con ssh. ¿Existe algún módulo de ansible que te permita copiar claves públicas?.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;router&lt;/strong&gt;: Todas las tareas necesarias para configurar router cómo router-nat y que salga a internet por eth1. Las configuraciones deben ser permanentes. ¿Existe algún módulo de ansible que te permita ejecutar sysctl?.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cliente&lt;/strong&gt;: Todas las tareas necesarias para que las máquinas conectadas a la red privada salgan a internet por eth1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;web&lt;/strong&gt;: Las tareas necesarias para instalar y configurar un servidor web con una página estática en la máquina cliente.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;{{&amp;lt; highlight yaml &quot;linenos=table&quot; &amp;gt;}}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;hosts: all
become: true
roles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;role: common&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hosts: router
become: true
roles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;role: router&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;hosts: cliente
become: true
roles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;role: web&lt;/li&gt;
&lt;li&gt;role: cliente
{{&amp;lt; / highlight &amp;gt;}}&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Roles&lt;/h3&gt;
&lt;p&gt;Para realizar el despliegue indicado tenemos que crear los roles. Las tareas de los roles, están contenidas en una estructura de carpetas dentro de la carpeta rol, siendo de la siguiente manera:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rolejemplo
├── defaults
├── files
├── handlers
├── library
├── tasks
├── templates
└── vars
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;conteniendo las tareas que realizará. Empezaremos por &lt;strong&gt;common&lt;/strong&gt;:&lt;/p&gt;
&lt;h4&gt;Common&lt;/h4&gt;
&lt;p&gt;Este rol es bastante sencillo ya que solo cuenta con un fichero &lt;code&gt;tasks/main.yaml&lt;/code&gt; que contiene la instrucción para comprobar que el sistema está actualizado:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight yaml &quot;linenos=table&quot; &amp;gt;}}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;name: Comprueba que el sistema esta actualizado
apt: update_cache=yes upgrade=yes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: Introduce la clave publica desde un fichero
authorized_key:
user: vagrant
state: present
key: &quot;{{ lookup(&apos;file&apos;, &apos;/home/roberto/.ssh/id_rsa.pub&apos;) }}&quot;
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Router&lt;/h4&gt;
&lt;p&gt;Todas las tareas necesarias para configurar router cómo router-nat y que salga a internet por eth1. Las configuraciones deben ser permanentes. El contenido de &lt;code&gt;tasks/main.yaml&lt;/code&gt; es el siguiente:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight yaml &quot;linenos=table&quot; &amp;gt;}}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;name: Cambia el bit de forward a 1
ansible.posix.sysctl:
name: net.ipv4.ip_forward
value: &apos;1&apos;
sysctl_set: yes
state: present
reload: yes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: Configuracion de DNAT
iptables:
chain: PREROUTING
in_interface: eth1
destination_port: 80
jump: DNAT
protocol: tcp
table: nat
to_destination: 10.0.0.2:80
become: yes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: Configuracion de SNAT
iptables:
chain: POSTROUTING
destination: 10.0.0.0/8
jump: MASQUERADE
out_interface: eth1
table: nat
become: yes
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Web&lt;/h4&gt;
&lt;p&gt;Las tareas necesarias para instalar y configurar un servidor web con una página estática en la máquina cliente. El contenido de &lt;code&gt;tasks/main.yaml&lt;/code&gt; es el siguiente:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight yaml &quot;linenos=table&quot; &amp;gt;}}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;name: &quot;ensure apache2 is installed&quot;
apt:
pkg: apache2&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: copy template index
template:
src: index.j2
dest: /var/www/html/index.html
owner: www-data
group: www-data
mode: 0644&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: &quot;Copiar fichero al servidor remoto&quot;
copy:
src: fichero.html
dest: /var/www/html/fichero.html
owner: www-data
group: www-data
mode: &apos;0644&apos;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;name: &quot;Copiar fichero de configuración y reiniciar el servicio&quot;
copy:
src: etc/apache2/ports.conf
dest: /etc/apache2/ports.conf
owner: root
group: root
mode: &apos;0644&apos;
notify: restart apache2
{{&amp;lt; / highlight &amp;gt;}}&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;En esta tarea si que se utilizan otros ficheros, pero al no tener una configuración crucial, no vamos a modificarlos. Su contenido se encuentra en github.&lt;/p&gt;
&lt;h4&gt;Cliente&lt;/h4&gt;
&lt;p&gt;Todas las tareas necesarias para que las máquinas conectadas a la red privada salgan a internet por eth1. El contenido de &lt;code&gt;tasks/main.yaml&lt;/code&gt; es el siguiente:&lt;/p&gt;
&lt;p&gt;{{&amp;lt; highlight yaml &quot;linenos=table&quot; &amp;gt;}}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;name: cambiar la ruta por defecto
shell: ip route delete default &amp;amp;&amp;amp; ip route add default via 10.0.0.1 &amp;amp;&amp;amp; echo &quot;post-up route add -net 10.0.0.0 netmask 255.0.0.0 gw 10.0.0.0 dev eth1&quot; &amp;gt;&amp;gt; /etc/network/interfaces
{{&amp;lt; / highlight &amp;gt;}}&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;Finalmente, podemos ejecutar el playbook con &lt;code&gt;ansible-playbook site.yaml&lt;/code&gt;:
&lt;img src=&quot;ansible.png&quot; alt=&quot;ansible&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Compilación de un programa en C utilizando un Makefile</title><link>https://www.robertops.com/posts/2022-10-16_compilacion_makefile/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-16_compilacion_makefile/</guid><pubDate>Sun, 16 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Enunciado:&lt;/strong&gt; Elige el programa escrito en C que prefieras y comprueba en las fuentes que exista un fichero Makefile o Configure. Deberás compilar desde las fuentes.
Realiza los pasos necesarios para compilarlo e instálalo en tu equipo en un directorio que no interfiera con tu sistema de paquetes (/opt, /usr/local, etc.)
La corrección se hará en clase y deberás ser capaz de explicar qué son todos los ficheros que se hayan instalado y realizar una desinstalación limpia.&lt;/p&gt;
&lt;p&gt;El programa elegido es &lt;code&gt;htop&lt;/code&gt;, ya que está escrito en &lt;strong&gt;C&lt;/strong&gt;. De momento no tiene ninguno de los ficheros requeridos, pero ejecutando el script &lt;code&gt;autogen.sh&lt;/code&gt; se crea el fichero configure.&lt;/p&gt;
&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;Para obtener las fuentes ejecutamos el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apt source htop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ó&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget http://deb.debian.org/debian/pool/main/h/htop/htop_3.0.5.orig.tar.gz
tar -xvf htop_3.0.5.orig.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Configure&lt;/h3&gt;
&lt;p&gt;De momento el fichero no existe. Ejecutamos &lt;code&gt;autogen.sh&lt;/code&gt;. Necesitaremos además el paquete &lt;code&gt;autoconf&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install autoconf
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;./autogen.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;autogen.png&quot; alt=&quot;ejecucion script autogen&quot; /&gt;
Podemos comprobar que se ha generado el fichero &lt;strong&gt;configure&lt;/strong&gt;:
&lt;img src=&quot;ls-directorio.png&quot; alt=&quot;ls del directorio de htop&quot; /&gt;
Podemos ejecutar el fichero configure directamente. Sin embargo, de cara a una desinstalación más sencilla y a poder tener más localizados los ficheros instalados, vamos a cambiar la ruta que hay por defecto, al directorio &lt;code&gt;opt&lt;/code&gt;. Se hace añadiendo la siguiente opción:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir /opt/htop
./configure --prefix=/opt/htop/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;configure.png&quot; alt=&quot;salida de la ejecución de configure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;En el caso de que se de algún error en la salida del comando, hay que instalar las &lt;strong&gt;dependencias&lt;/strong&gt; indicadas, y repetir el paso anterior.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install libncurses*
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Makefile&lt;/h3&gt;
&lt;p&gt;Para poder ejecutar &lt;code&gt;make&lt;/code&gt; necesitamos el paquete &lt;code&gt;build-essential&lt;/code&gt;, que contiene las utilidades esenciales para compilar un paquete en Debian.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install build-essential
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para instalar el paquete ejecutamos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo make install
sudo make clean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para generar el fichero &lt;code&gt;.deb&lt;/code&gt; es &lt;strong&gt;necesario&lt;/strong&gt; que el paquete descargado sea con &lt;code&gt;apt source&lt;/code&gt;, o la versión específica de debian.&lt;/p&gt;
&lt;p&gt;o bien, para generar el fichero .deb:&lt;/p&gt;
&lt;p&gt;instalamos las siguientes dependencias:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install libnl-3-dev libnl-genl-3-dev libsensors-dev pkg-config debhelper-compat
# o bien con apt-get build-dep
sudo apt-get build-dep htop
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;make
dpkg-buildpackage -b
make clean
sudo dpkg -i ../htop_3.0.5-7_amd64.deb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;make.png&quot; alt=&quot;ejecucion de comando make&quot; /&gt;
El paquete ya está instalado. Podemos comprobar los ficheros creados en &lt;code&gt;/opt&lt;/code&gt;:
&lt;img src=&quot;tree-htop.png&quot; alt=&quot;tree del directorio htop&quot; /&gt;
Entre los ficheros que aparecen, los más importantes son el propio &lt;strong&gt;binario&lt;/strong&gt; de htop, y el &lt;strong&gt;manual&lt;/strong&gt;.
Para poder utilizar el comando la terminal tenemos que añadirlo al &lt;strong&gt;PATH&lt;/strong&gt;. Para hacerlo, añadimos la siguiente línea al fichero &lt;code&gt;.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=&quot;/opt/htop/bin:$PATH&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Manual&lt;/h3&gt;
&lt;p&gt;Ya que lo hemos instalado en un directorio personal, no se ha creado el manual. Para hacerlo tenemos que crear un enlace simbólico:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo mkdir /usr/local/man/man1
sudo ln -s /opt/htop/share/man/man1/* /usr/local/man/man1/
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Desinstalación&lt;/h2&gt;
&lt;p&gt;Para desinstalar el paquete vamos a utilizar otra vez el comando make, esta vez con la opción &lt;code&gt;uninstall&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo make uninstall
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;desinstalar.png&quot; alt=&quot;ejecucion de make uninstall&quot; /&gt;
Tras eso, comprobamos si queda algo en la ubicación de la instalación:
&lt;img src=&quot;estructura.png&quot; alt=&quot;estructura residual&quot; /&gt;
la estructura de carpetas no se ha borrado, la borramos de manera manual.
Por último, hay que eliminar la línea de &lt;code&gt;.bashrc&lt;/code&gt; que añade la ruta al &lt;code&gt;PATH&lt;/code&gt; y eliminar el enlace simbólico:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo rm /usr/local/man/man1/htop.1 
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Envío de alertas de Snort utilizando postfix</title><link>https://www.robertops.com/posts/2022-10-14_snort/correo_alertas/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-14_snort/correo_alertas/</guid><pubDate>Fri, 14 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;En esta entrada vamos a configurar un servidor de correos postfix para poder monitorizar las alertas que da snort de manera remota. Para ello vamos a utilizar el servidor por medio del comando swatch, que se encargará de comprobar el log de snort.&lt;/p&gt;
&lt;h2&gt;Postfix&lt;/h2&gt;
&lt;h3&gt;Instalación&lt;/h3&gt;
&lt;p&gt;Antes de iniciar la instalación tenemos que comprobar el nombre de nuestra máquina, con el comando&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hostname
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para instalar postfix previamente tenemos que instalar mailutils:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install mailutils
sudo apt install postfix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras la instalación de postfix, Se inciará un setup. En él seleccionamos &lt;strong&gt;Internet site&lt;/strong&gt;, la opción por defecto. En el siguiente paso, si no está ya, introducimos el nombre de la máquina que consultamos antes.&lt;/p&gt;
&lt;h3&gt;Configuración&lt;/h3&gt;
&lt;p&gt;Una vez instalado, tenemos que editar varias líneas en el fichero de configuración: &lt;code&gt;/etc/postfix/main.cf&lt;/code&gt;; tenemos que cambiar las siguientes líneas:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inet_interfaces = loopback-only
mydestination = $myhostname, localhost.$your_domain, $your_domain
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras eso reiniciamos el servicio&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart postfix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Con la configuración actual, no podemos enviar correos a una dirección de correo habitual (gmail, hotmail..) ya que nuestro servidor postfix no tiene &lt;strong&gt;autentificación SASL&lt;/strong&gt; ni &lt;strong&gt;encriptación SLS&lt;/strong&gt;, por lo que los proveedores de correo lo rechazan automáticamente.&lt;/p&gt;
&lt;p&gt;Para comprobar que funciona podemos enviar un correo de prueba. Para el ejemplo, vamos a enviarlo a un correo temporal como &lt;a href=&quot;https://temp-mail.org/es/&quot;&gt;este&lt;/a&gt;, ya que no realiza las mismas comprobaciones que gmail.&lt;/p&gt;
&lt;p&gt;Enviamos el correo con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Cuerpo del mensaje&quot; | mail -s &quot;Asunto del mensaje&quot; correoelectronico
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;prueba_correo.png&quot; alt=&quot;muestra correo temporal&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Para más información:
&lt;a href=&quot;https://netcorecloud.com/tutorials/install-postfix-as-send-only-smtp-server/&quot;&gt;instalación postfix&lt;/a&gt;,
&lt;a href=&quot;https://souptonuts.sourceforge.net/postfix_tutorial.html&quot;&gt;SASL y SLS&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Alertas snort&lt;/h2&gt;
&lt;p&gt;Para que se envien correos al recibir alertas necesitamos el paquete &lt;code&gt;swatchdog&lt;/code&gt;. Tras la instalación, en el fichero &lt;code&gt;.swatchdogrc&lt;/code&gt; del directorio personal tiene que haber el siguiente contenido:&lt;/p&gt;
&lt;p&gt;watchfor /Priority: 0/
exec echo &quot;Mira el log de snort&quot; | mail -s &quot;ALERTA DE SNORT&quot; correoelectronico,
throttle 1:00&lt;/p&gt;
&lt;p&gt;y lo ejecutamos:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;swatchdog  -t /var/log/snort/alert
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Para que &lt;strong&gt;Snort&lt;/strong&gt; Genere las alertas en el fichero alert se ejecuta con el siguiente comando:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;snort -A full -q -u snort -g snort -c /etc/snort/snort.conf -i eth1
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Snort: Instalación y primeros pasos</title><link>https://www.robertops.com/posts/2022-10-14_snort/introducci%C3%B3n/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-14_snort/introducci%C3%B3n/</guid><pubDate>Mon, 10 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Enunciado:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Instalación&lt;/li&gt;
&lt;li&gt;Configuración Red&lt;/li&gt;
&lt;li&gt;Reglas def&lt;/li&gt;
&lt;li&gt;Reglas propias&lt;/li&gt;
&lt;li&gt;Opciones reglas&lt;/li&gt;
&lt;li&gt;Demo uso&lt;/li&gt;
&lt;li&gt;Alertas&lt;/li&gt;
&lt;li&gt;Acciones&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Instalación&lt;/h2&gt;
&lt;p&gt;El paquete de &lt;strong&gt;Snort&lt;/strong&gt; se encuentra en los repositorios de debian, por lo que podemos instalarlo directamente con apt:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt update
sudo apt install snort
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;con &lt;code&gt;snort -V&lt;/code&gt; podemos comprobar que se ha instalado correctamente:
&lt;img src=&quot;pruebainstalacion.png&quot; alt=&quot;pruebainstalacion&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Configuración de la red&lt;/h3&gt;
&lt;p&gt;Tras instalarlo, iniciará la configuración de la red. En la configuración que se abre, podemos seleccionar la ip/rango de ips de la máquina, aunque más adelante modificando el fichero de configuración, podemos elegir que utilice solo una interfaz de red o todas (la opción por defecto).
&lt;img src=&quot;instalacion.png&quot; alt=&quot;instalacion&quot; /&gt;
Para modificar las opciones tras la instalación tenemos que modificar el fichero &lt;code&gt;/etc/snort/snort.debian.conf&lt;/code&gt; (al tratarse de una instalación en debian, se carga el contenido de este fichero antes que la propia configuración de snort). Concretamente nos interesan las siguientes líneas:&lt;/p&gt;
&lt;p&gt;DEBIAN_SNORT_STARTUP=&quot;boot&quot;
DEBIAN_SNORT_HOME_NET=&quot;10.0.0.0/8&quot;
DEBIAN_SNORT_OPTIONS=&quot;&quot;
DEBIAN_SNORT_INTERFACE=&quot;enp7s0&quot;
DEBIAN_SNORT_SEND_STATS=&quot;false&quot;
DEBIAN_SNORT_STATS_RCPT=&quot;root&quot;
DEBIAN_SNORT_STATS_THRESHOLD=&quot;1&quot;&lt;/p&gt;
&lt;p&gt;Como el propio fichero indica, después de modificarlo tenemos que ejecutar el siguiente comando para que la configuración se actualice:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo dpkg-reconfigure snort
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras la ejecución del comando, reconfiguramos Snort, decidiendo cuando se ejecuta, las interfaces e ips, si activamos el modo promiscuo, y por último si queremos que se cree una tarea de cron para mandar correos diariamente con el resultado del log.&lt;/p&gt;
&lt;h2&gt;Reglas&lt;/h2&gt;
&lt;p&gt;Para configurar los grupos de reglas que queremos activar, tenemos que editar el fichero de configuración &lt;code&gt;/etc/snort/snort.conf&lt;/code&gt;:
&lt;img src=&quot;rutasreglas.png&quot; alt=&quot;rutasreglas&quot; /&gt;
En la instalación que se realiza de los repositorios de debian, están incluidas las reglas de la comunidad. En el caso de que queramos utilizar una set de reglas concreta, habría que descomentar el set específico. También se puede observar en la imagen como el fichero de reglas personalizadas está activo (&lt;code&gt;local.rules&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Reglas propias&lt;/h3&gt;
&lt;p&gt;De momento el fichero de reglas propias está vacío. Las reglas se construyen de la siguiente manera:
&lt;img src=&quot;estructuraregla.png&quot; alt=&quot;estructura&quot; /&gt;
Como podemos ver, La regla consta de dos partes principales, la &lt;strong&gt;cabecera&lt;/strong&gt;, que contiene información relacionada con la red, y las &lt;strong&gt;opciones&lt;/strong&gt;, que contienen detalles de invenstigación de paquetes. La regla que se muestra a continuación, sirve para detectar que se está realizando un ping a la máquina:&lt;/p&gt;
&lt;p&gt;alert icmp any any -&amp;gt; $HOME_NET any (msg:&quot;Ping detectado&quot;;sid:1000001;rev:1)&lt;/p&gt;
&lt;p&gt;vamos a analizar la regla:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Estructura&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;th&gt;Descripción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Action&lt;/td&gt;
&lt;td&gt;alert&lt;/td&gt;
&lt;td&gt;le dice a snort que hacer cuando la regla salta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Protocol&lt;/td&gt;
&lt;td&gt;icmp&lt;/td&gt;
&lt;td&gt;Protocolo a ser analizado (TCP, UDP, ICMP, IP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source IP&lt;/td&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;Direcciones IP de origen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source Port&lt;/td&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;Puertos de origen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Direction&lt;/td&gt;
&lt;td&gt;-&amp;gt;&lt;/td&gt;
&lt;td&gt;Operador de dirección. Determina la dirección del tráfico&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destination IP&lt;/td&gt;
&lt;td&gt;$HOME_NET&lt;/td&gt;
&lt;td&gt;Direcciones IP de destino&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Destination Port&lt;/td&gt;
&lt;td&gt;any&lt;/td&gt;
&lt;td&gt;Puertos de destino&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Message&lt;/td&gt;
&lt;td&gt;msg:&quot;Ping detectado&quot;&lt;/td&gt;
&lt;td&gt;Mensaje a mostrar cuando aplique la regla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rule ID&lt;/td&gt;
&lt;td&gt;sid:1000001&lt;/td&gt;
&lt;td&gt;ID único de la regla&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revision info&lt;/td&gt;
&lt;td&gt;rev:1&lt;/td&gt;
&lt;td&gt;Información de revisión&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Demo de la regla&lt;/h3&gt;
&lt;p&gt;Una vez hemos añadido al regla al fichero de &lt;code&gt;local.rules&lt;/code&gt;, ya podemos probar la regla. Vamos a ejecutar snort en modo consola para que muestre el resultado en pantalla (&lt;code&gt;-A console&lt;/code&gt;):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo snort -A console -q -c /etc/snort/snort.conf -i enp1s0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;pruebaping.png&quot; alt=&quot;pruebaping&quot; /&gt;&lt;/p&gt;
&lt;p&gt;MHicxeRVZ6cSE6usNUu2HNRbI&lt;/p&gt;
</content:encoded></item><item><title>Instalación de Debian desatendida</title><link>https://www.robertops.com/posts/2022-10-06_desatendida/</link><guid isPermaLink="true">https://www.robertops.com/posts/2022-10-06_desatendida/</guid><pubDate>Thu, 06 Oct 2022 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Creación de imagen&lt;/h2&gt;
&lt;h3&gt;Descomprimimos la imagen&lt;/h3&gt;
&lt;p&gt;Vamos a utilizar la versión de debian que contiene software privativo, para, por ejemplo, tener disponibles más drivers en caso de que fueran necesarios. Tenemos que seguir los siguientes pasos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Descargamos la imagen de la página de debian:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ wget https://cdimage.debian.org/cdimage/unofficial/non-free/cd-including-firmware/current/amd64/iso-cd/firmware-11.5.0-amd64-netinst.iso
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Descomprimimos la imagen utilizando &lt;code&gt;xorriso&lt;/code&gt; en el directorio &lt;code&gt;isofiles/&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ xorriso -osirrox on -indev firmware-11.5.0-amd64-netinst.iso -extract / isofiles/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Introducimos el preseed&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;copiamos el fichero &lt;code&gt;preseed.cfg&lt;/code&gt; a la raíz de la imagen:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ sudo cp preseed.cfg isofiles/preseed.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Editamos el fichero &lt;code&gt;txt.cfg&lt;/code&gt; (encargado del contenido del menú inicial de instalación) para añadir una opción que utilice el &lt;code&gt;preseed&lt;/code&gt; además de que cargue el idioma español:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ sudo nano isofiles/isolinux/txt.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;label install
        menu label ^Install
        kernel /install.amd/vmlinuz
        append vga=788 initrd=/install.amd/initrd.gz --- quiet
label unattended-gnome
        menu label ^Instalacion Debian Desatendida Preseed
        kernel /install.amd/gtk/vmlinuz
        append vga=788 initrd=/install.amd/gtk/initrd.gz preseed/file=/cdrom/preseed.cfg locale=es_ES console-setup/ask_detect=false keyboard-configuration/xkb-keymap=e&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Volvemos a generar la imagen&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Como hemos alterado los ficheros que contiene la imagen, tenemos que generar un nuevo fichero &lt;code&gt;md5sum.txt&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ cd isofiles/
$ chmod a+w md5sum.txt
$ md5sum `find -follow -type f` &amp;gt; md5sum.txt
$ chmod a-w md5sum.txt
$ cd .
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Por último cambiamos los permisos de &lt;code&gt;isolinux&lt;/code&gt; y creamos la imagen nueva:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ chmod a+w isofiles/isolinux/isolinux.bin
$ genisoimage -r -J -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table -o debian-preseed.iso isofiles
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Carga del fichero preseed.cfg desde red&lt;/h2&gt;
&lt;h3&gt;Configuración del servidor&lt;/h3&gt;
&lt;p&gt;Para la instalación desatendida cargando el &lt;code&gt;preseed&lt;/code&gt; desde red, es necesario una máquina que haga el rol de servidor, teniendo un servidor &lt;code&gt;apache2&lt;/code&gt; instalado. Para preparar dicha máquina seguimos los siguientes pasos:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Instalamos el servidor apache en la máquina:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ apt upgrade &amp;amp;&amp;amp; apt install apache2
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;copiamos el fichero &lt;code&gt;preseed.cfg&lt;/code&gt; previamente configurado al directorio &lt;code&gt;/var/www/html&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Tras este paso, el servidor ya está configurado y ofreciendo la configuración a la red.&lt;/p&gt;
&lt;h3&gt;Utilización desde el cliente&lt;/h3&gt;
&lt;p&gt;Para aplicar la configuración del fichero &lt;code&gt;preseed&lt;/code&gt;, iniciamos la instalación de una imagen de debian sin modificar. Para utilizarla tenemos dos opciones:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Utilizando línea de comandos:
&lt;ol&gt;
&lt;li&gt;Pulsamos la tecla ESC para abrir la línea de comandos&lt;/li&gt;
&lt;li&gt;Introducimos el siguiente comando para acceder al fichero, donde &lt;code&gt;IP servidor&lt;/code&gt; es la ip de la máquina que tiene el servidor apache:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;boot: auto url=[IP servidor]/preseed.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Utilizando las opciones avanzadas:
&lt;ol&gt;
&lt;li&gt;Accedemos a opciones avanzadas en el menú, seguido de instalación automatizada.&lt;/li&gt;
&lt;li&gt;Introducimos la ip del servidor con apache de la siguiente manera:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;http://[IP servidor]/preseed.cfg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras esto, la instalación desatendida comenzará.&lt;/p&gt;
&lt;h2&gt;Instalación basada en preseed/PXE/TFT&lt;/h2&gt;
&lt;p&gt;Para esta instalación, al igual que la anterior, es necesario que una máquina haga el rol de servidor, además en este caso tiene que tener un servidor DHCP. Para configurarla vamos a seguir los siguientes pasos. La máquina tiene que tener una red aislada sin DHCP en la que se va a conectar con los clientes&lt;/p&gt;
&lt;h3&gt;Instalación de dnsmasq&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Instalamos el paquete dnsmasq, encargado tanto del DHCP como del servidor TFTP&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ apt install dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;configuramos el contenido del fichero /etc/dnsmasq.conf/:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;dhcp-range=192.168.100.50,192.168.100.150,255.255.255.0,12h
dhcp-boot=pxelinux.0
enable-tftp
tftp-root=/srv/tftp
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;En el paso anterior especificamos que se utilizara el directorio /srv/tftp/ como raíz para la transmisión por pxe; vamos a crearlo:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ mkdir /srv/tftp/
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Reiniciamos el servicio para que los cambios tengan efecto&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ systemctl restart dnsmasq
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Descarga de la imagen&lt;/h3&gt;
&lt;p&gt;Para instalar utilizando PXE/TFTP tenemos que utilizar una imagen de debian especial llamada netboot. Esta imagen se encuentra en la siguiente dirección: http://ftp.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/netboot/netboot.tar.gz.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Nos desplazamos al directorio /srv/tftp/, descargamos la imagen y la descomprimimos:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;$ wget http://ftp.debian.org/debian/dists/bullseye/main/installer-amd64/current/images/netboot/netboot.tar.gz
$ tar -zxf netboot.tar.gz &amp;amp;&amp;amp; rm netboot.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tras este paso, el servidor ya está ofreciendo la imagen de debian a la red.&lt;/p&gt;
&lt;h3&gt;Reglas nftables&lt;/h3&gt;
&lt;p&gt;dado que el cliente solo está conectado al servidor, no tiene ninguna conexión a internet. Por lo que el servidor, además, tiene que hacer SNAT. Para ello vamos a activar el bit de forwarding y a aplicar las siguientes reglas de nftables:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nft add table nat
$ nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
$ nft add rule ip nat postrouting oifname &quot;eth0&quot; ip saddr 192.168.100.0/24 counter masquerade
$ nft list ruleset &amp;gt; /etc/nftables.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Si la configuración no ha persistido tras un reinicio, podemos recuperarla con:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nft -f /etc/nftables.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Fichero Preseed&lt;/h3&gt;
&lt;p&gt;Para añadir el fichero preseed, tenemos dos opciones. Añadirlo a los ficheros que se están distribuyendo a través de &lt;code&gt;PXE&lt;/code&gt;, o utilizar un &lt;code&gt;servidor apache&lt;/code&gt;, realizándose de la misma manera que en el paso anterior.&lt;/p&gt;
&lt;p&gt;Para utilizar el fichero &lt;code&gt;preseed.cfg&lt;/code&gt; modificamos el fichero &lt;code&gt;txt.cfg&lt;/code&gt; para que utilice el que estamos ofreciendo en el servidor apache:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;label install
	menu label ^Install
	kernel debian-installer/amd64/linux
	append vga=788 initrd=debian-installer/amd64/initrd.gz --- quiet 
label unattended-gnome
        menu label ^Instalacion Debian Desatendida Preseed
        kernel debian-installer/amd64/linux
        append vga=788 initrd=debian-installer/amd64/initrd.gz preseed/url=192.168.100.5/preseed.txt locale=es_ES console-setup/ask_detect=false keyboard-configuration/xkb-keymap=e&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Lado del cliente&lt;/h3&gt;
&lt;p&gt;La instalación desde el lado del cliente es muy similar al paso anterior. Antes de empezar, hay que añadirle una tarjeta de red que esté en la red del DHCP, y hacer que sea una opción de arranque. Una vez hecho esto, el cliente iniciará la imagen en red, y desde ahí, podemos seguir los pasos que ya sabemos para utilizar el fichero preseed.cfg&lt;/p&gt;
</content:encoded></item></channel></rss>