[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-/blog/tutorial/automatizar-ansible":3,"prev-/blog/tutorial/automatizar-ansible":3741,"next-/blog/tutorial/automatizar-ansible":3744,"related-/blog/tutorial/automatizar-ansible":3747},{"id":4,"title":5,"author":6,"authorUrl":7,"body":8,"category":3708,"cta":3709,"date":3712,"dateModified":3712,"description":3713,"draft":3714,"extension":3715,"faq":3716,"featured":3714,"image":3729,"imageAlt":3730,"meta":3731,"navigation":210,"path":3732,"readingTime":248,"seo":3733,"stem":3734,"tags":3735,"__hash__":3740},"blog/blog/tutorial/automatizar-ansible.md","Automatizar tareas de infraestructura con Ansible","Syswork México","/nosotros",{"type":9,"value":10,"toc":3695},"minimark",[11,15,24,29,37,41,49,101,104,140,143,147,150,177,264,267,291,302,305,309,312,709,712,729,747,751,758,2158,2161,2175,2245,2257,2259,2263,2266,2274,2277,2428,2498,2502,2510,2916,2920,2928,3343,3345,3349,3352,3512,3516,3526,3548,3554,3560,3587,3593,3622,3628,3632,3635,3682,3691],[12,13,14],"p",{},"Configurar un servidor manualmente funciona cuando tienes uno. Cuando tienes cinco, es tedioso. Cuando tienes veinte, es insostenible. Y lo peor: cada servidor configurado a mano es ligeramente diferente — alguien olvidó habilitar el firewall en uno, otro tiene una versión vieja de un paquete, un tercero tiene permisos diferentes. Esas diferencias invisibles son las que causan problemas en producción que nadie puede reproducir.",[12,16,17,18,23],{},"Ansible resuelve esto de raíz: defines la configuración deseada de tus ",[19,20,22],"a",{"href":21},"/servicios/servidores","servidores"," como código, la ejecutas y Ansible se encarga de que todos los servidores lleguen al mismo estado — sin importar cuántos sean.",[25,26,28],"h2",{"id":27},"requisitos-previos","Requisitos previos",[12,30,31,32,36],{},"Una máquina de control con Ubuntu 24.04 (puede ser tu laptop o un servidor de administración) y servidores objetivo con acceso SSH configurado con ",[19,33,35],{"href":34},"/blog/hardening-servidores-linux","llaves",".",[25,38,40],{"id":39},"paso-1-instalar-ansible","Paso 1: Instalar Ansible",[12,42,43,44,48],{},"Ansible se instala solo en la ",[45,46,47],"strong",{},"máquina de control"," — no en los servidores objetivo:",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","sudo apt update\nsudo apt install ansible -y\nansible --version\n","bash","",[57,58,59,75,92],"code",{"__ignoreMap":55},[60,61,64,68,72],"span",{"class":62,"line":63},"line",1,[60,65,67],{"class":66},"sbgvK","sudo",[60,69,71],{"class":70},"s_sjI"," apt",[60,73,74],{"class":70}," update\n",[60,76,78,80,82,85,88],{"class":62,"line":77},2,[60,79,67],{"class":66},[60,81,71],{"class":70},[60,83,84],{"class":70}," install",[60,86,87],{"class":70}," ansible",[60,89,91],{"class":90},"stzsN"," -y\n",[60,93,95,98],{"class":62,"line":94},3,[60,96,97],{"class":66},"ansible",[60,99,100],{"class":90}," --version\n",[12,102,103],{},"Verifica la conexión SSH a tus servidores:",[50,105,107],{"className":52,"code":106,"language":54,"meta":55,"style":55},"ssh usuario@10.0.1.10 \"hostname\"\nssh usuario@10.0.1.11 \"hostname\"\n",[57,108,109,127],{"__ignoreMap":55},[60,110,111,114,117,121,124],{"class":62,"line":63},[60,112,113],{"class":66},"ssh",[60,115,116],{"class":70}," usuario@10.0.1.10",[60,118,120],{"class":119},"sjJ54"," \"",[60,122,123],{"class":70},"hostname",[60,125,126],{"class":119},"\"\n",[60,128,129,131,134,136,138],{"class":62,"line":77},[60,130,113],{"class":66},[60,132,133],{"class":70}," usuario@10.0.1.11",[60,135,120],{"class":119},[60,137,123],{"class":70},[60,139,126],{"class":119},[12,141,142],{},"Si puedes conectarte sin contraseña (con llaves SSH), Ansible también puede.",[25,144,146],{"id":145},"paso-2-inventario-de-servidores","Paso 2: Inventario de servidores",[12,148,149],{},"El inventario le dice a Ansible qué servidores administrar. Crea un directorio de proyecto:",[50,151,153],{"className":52,"code":152,"language":54,"meta":55,"style":55},"mkdir -p ~/ansible && cd ~/ansible\n",[57,154,155],{"__ignoreMap":55},[60,156,157,160,163,166,170,174],{"class":62,"line":63},[60,158,159],{"class":66},"mkdir",[60,161,162],{"class":90}," -p",[60,164,165],{"class":70}," ~/ansible",[60,167,169],{"class":168},"sP7_E"," &&",[60,171,173],{"class":172},"sptTA"," cd",[60,175,176],{"class":70}," ~/ansible\n",[50,178,182],{"className":179,"code":180,"language":181,"meta":55,"style":55},"language-ini shiki shiki-themes material-theme-lighter github-light github-dark","# inventory.ini\n[webservers]\nweb1 ansible_host=10.0.1.10\nweb2 ansible_host=10.0.1.11\n\n[dbservers]\ndb1 ansible_host=10.0.1.20\n\n[monitoring]\nmonitor1 ansible_host=10.0.1.30\n\n[all:vars]\nansible_user=deploy\nansible_python_interpreter=/usr/bin/python3\n","ini",[57,183,184,189,194,199,205,212,218,224,229,235,241,246,252,258],{"__ignoreMap":55},[60,185,186],{"class":62,"line":63},[60,187,188],{},"# inventory.ini\n",[60,190,191],{"class":62,"line":77},[60,192,193],{},"[webservers]\n",[60,195,196],{"class":62,"line":94},[60,197,198],{},"web1 ansible_host=10.0.1.10\n",[60,200,202],{"class":62,"line":201},4,[60,203,204],{},"web2 ansible_host=10.0.1.11\n",[60,206,208],{"class":62,"line":207},5,[60,209,211],{"emptyLinePlaceholder":210},true,"\n",[60,213,215],{"class":62,"line":214},6,[60,216,217],{},"[dbservers]\n",[60,219,221],{"class":62,"line":220},7,[60,222,223],{},"db1 ansible_host=10.0.1.20\n",[60,225,227],{"class":62,"line":226},8,[60,228,211],{"emptyLinePlaceholder":210},[60,230,232],{"class":62,"line":231},9,[60,233,234],{},"[monitoring]\n",[60,236,238],{"class":62,"line":237},10,[60,239,240],{},"monitor1 ansible_host=10.0.1.30\n",[60,242,244],{"class":62,"line":243},11,[60,245,211],{"emptyLinePlaceholder":210},[60,247,249],{"class":62,"line":248},12,[60,250,251],{},"[all:vars]\n",[60,253,255],{"class":62,"line":254},13,[60,256,257],{},"ansible_user=deploy\n",[60,259,261],{"class":62,"line":260},14,[60,262,263],{},"ansible_python_interpreter=/usr/bin/python3\n",[12,265,266],{},"Verifica que Ansible puede conectarse:",[50,268,270],{"className":52,"code":269,"language":54,"meta":55,"style":55},"ansible all -i inventory.ini -m ping\n",[57,271,272],{"__ignoreMap":55},[60,273,274,276,279,282,285,288],{"class":62,"line":63},[60,275,97],{"class":66},[60,277,278],{"class":70}," all",[60,280,281],{"class":90}," -i",[60,283,284],{"class":70}," inventory.ini",[60,286,287],{"class":90}," -m",[60,289,290],{"class":70}," ping\n",[12,292,293,294,297,298,301],{},"Deberías ver ",[57,295,296],{},"SUCCESS"," para cada servidor con un ",[57,299,300],{},"\"pong\""," como respuesta.",[303,304],"ad-banner",{},[25,306,308],{"id":307},"paso-3-tu-primer-playbook","Paso 3: Tu primer playbook",[12,310,311],{},"Un playbook es un archivo YAML que describe el estado deseado de tus servidores. Empecemos con uno básico que actualiza paquetes e instala herramientas esenciales:",[50,313,317],{"className":314,"code":315,"language":316,"meta":55,"style":55},"language-yaml shiki shiki-themes material-theme-lighter github-light github-dark","# playbooks/base.yml\n---\n- name: Configuración base de servidores\n  hosts: all\n  become: true  # Ejecutar como root (sudo)\n\n  tasks:\n    - name: Actualizar caché de paquetes\n      apt:\n        update_cache: true\n        cache_valid_time: 3600\n\n    - name: Actualizar todos los paquetes\n      apt:\n        upgrade: safe\n      register: upgrade_result\n\n    - name: Instalar paquetes esenciales\n      apt:\n        name:\n          - curl\n          - wget\n          - git\n          - htop\n          - vim\n          - unzip\n          - net-tools\n          - chrony\n          - jq\n        state: present\n\n    - name: Configurar timezone\n      timezone:\n        name: America/Mexico_City\n\n    - name: Habilitar sincronización NTP\n      systemd:\n        name: chrony\n        state: started\n        enabled: true\n\n    - name: Mostrar resumen de actualización\n      debug:\n        msg: \"Paquetes actualizados: {{ upgrade_result.stdout_lines | default(['Ninguno']) }}\"\n","yaml",[57,318,319,325,330,345,355,369,373,381,393,400,410,421,425,436,442,453,464,469,481,488,496,505,513,521,529,537,545,553,561,569,580,585,597,605,615,620,632,640,649,659,669,674,686,694],{"__ignoreMap":55},[60,320,321],{"class":62,"line":63},[60,322,324],{"class":323},"sutJx","# playbooks/base.yml\n",[60,326,327],{"class":62,"line":77},[60,328,329],{"class":66},"---\n",[60,331,332,335,339,342],{"class":62,"line":94},[60,333,334],{"class":168},"-",[60,336,338],{"class":337},"sQzsp"," name",[60,340,341],{"class":168},":",[60,343,344],{"class":70}," Configuración base de servidores\n",[60,346,347,350,352],{"class":62,"line":201},[60,348,349],{"class":337},"  hosts",[60,351,341],{"class":168},[60,353,354],{"class":70}," all\n",[60,356,357,360,362,366],{"class":62,"line":207},[60,358,359],{"class":337},"  become",[60,361,341],{"class":168},[60,363,365],{"class":364},"syTEX"," true",[60,367,368],{"class":323},"  # Ejecutar como root (sudo)\n",[60,370,371],{"class":62,"line":214},[60,372,211],{"emptyLinePlaceholder":210},[60,374,375,378],{"class":62,"line":220},[60,376,377],{"class":337},"  tasks",[60,379,380],{"class":168},":\n",[60,382,383,386,388,390],{"class":62,"line":226},[60,384,385],{"class":168},"    -",[60,387,338],{"class":337},[60,389,341],{"class":168},[60,391,392],{"class":70}," Actualizar caché de paquetes\n",[60,394,395,398],{"class":62,"line":231},[60,396,397],{"class":337},"      apt",[60,399,380],{"class":168},[60,401,402,405,407],{"class":62,"line":237},[60,403,404],{"class":337},"        update_cache",[60,406,341],{"class":168},[60,408,409],{"class":364}," true\n",[60,411,412,415,417],{"class":62,"line":243},[60,413,414],{"class":337},"        cache_valid_time",[60,416,341],{"class":168},[60,418,420],{"class":419},"srdBf"," 3600\n",[60,422,423],{"class":62,"line":248},[60,424,211],{"emptyLinePlaceholder":210},[60,426,427,429,431,433],{"class":62,"line":254},[60,428,385],{"class":168},[60,430,338],{"class":337},[60,432,341],{"class":168},[60,434,435],{"class":70}," Actualizar todos los paquetes\n",[60,437,438,440],{"class":62,"line":260},[60,439,397],{"class":337},[60,441,380],{"class":168},[60,443,445,448,450],{"class":62,"line":444},15,[60,446,447],{"class":337},"        upgrade",[60,449,341],{"class":168},[60,451,452],{"class":70}," safe\n",[60,454,456,459,461],{"class":62,"line":455},16,[60,457,458],{"class":337},"      register",[60,460,341],{"class":168},[60,462,463],{"class":70}," upgrade_result\n",[60,465,467],{"class":62,"line":466},17,[60,468,211],{"emptyLinePlaceholder":210},[60,470,472,474,476,478],{"class":62,"line":471},18,[60,473,385],{"class":168},[60,475,338],{"class":337},[60,477,341],{"class":168},[60,479,480],{"class":70}," Instalar paquetes esenciales\n",[60,482,484,486],{"class":62,"line":483},19,[60,485,397],{"class":337},[60,487,380],{"class":168},[60,489,491,494],{"class":62,"line":490},20,[60,492,493],{"class":337},"        name",[60,495,380],{"class":168},[60,497,499,502],{"class":62,"line":498},21,[60,500,501],{"class":168},"          -",[60,503,504],{"class":70}," curl\n",[60,506,508,510],{"class":62,"line":507},22,[60,509,501],{"class":168},[60,511,512],{"class":70}," wget\n",[60,514,516,518],{"class":62,"line":515},23,[60,517,501],{"class":168},[60,519,520],{"class":70}," git\n",[60,522,524,526],{"class":62,"line":523},24,[60,525,501],{"class":168},[60,527,528],{"class":70}," htop\n",[60,530,532,534],{"class":62,"line":531},25,[60,533,501],{"class":168},[60,535,536],{"class":70}," vim\n",[60,538,540,542],{"class":62,"line":539},26,[60,541,501],{"class":168},[60,543,544],{"class":70}," unzip\n",[60,546,548,550],{"class":62,"line":547},27,[60,549,501],{"class":168},[60,551,552],{"class":70}," net-tools\n",[60,554,556,558],{"class":62,"line":555},28,[60,557,501],{"class":168},[60,559,560],{"class":70}," chrony\n",[60,562,564,566],{"class":62,"line":563},29,[60,565,501],{"class":168},[60,567,568],{"class":70}," jq\n",[60,570,572,575,577],{"class":62,"line":571},30,[60,573,574],{"class":337},"        state",[60,576,341],{"class":168},[60,578,579],{"class":70}," present\n",[60,581,583],{"class":62,"line":582},31,[60,584,211],{"emptyLinePlaceholder":210},[60,586,588,590,592,594],{"class":62,"line":587},32,[60,589,385],{"class":168},[60,591,338],{"class":337},[60,593,341],{"class":168},[60,595,596],{"class":70}," Configurar timezone\n",[60,598,600,603],{"class":62,"line":599},33,[60,601,602],{"class":337},"      timezone",[60,604,380],{"class":168},[60,606,608,610,612],{"class":62,"line":607},34,[60,609,493],{"class":337},[60,611,341],{"class":168},[60,613,614],{"class":70}," America/Mexico_City\n",[60,616,618],{"class":62,"line":617},35,[60,619,211],{"emptyLinePlaceholder":210},[60,621,623,625,627,629],{"class":62,"line":622},36,[60,624,385],{"class":168},[60,626,338],{"class":337},[60,628,341],{"class":168},[60,630,631],{"class":70}," Habilitar sincronización NTP\n",[60,633,635,638],{"class":62,"line":634},37,[60,636,637],{"class":337},"      systemd",[60,639,380],{"class":168},[60,641,643,645,647],{"class":62,"line":642},38,[60,644,493],{"class":337},[60,646,341],{"class":168},[60,648,560],{"class":70},[60,650,652,654,656],{"class":62,"line":651},39,[60,653,574],{"class":337},[60,655,341],{"class":168},[60,657,658],{"class":70}," started\n",[60,660,662,665,667],{"class":62,"line":661},40,[60,663,664],{"class":337},"        enabled",[60,666,341],{"class":168},[60,668,409],{"class":364},[60,670,672],{"class":62,"line":671},41,[60,673,211],{"emptyLinePlaceholder":210},[60,675,677,679,681,683],{"class":62,"line":676},42,[60,678,385],{"class":168},[60,680,338],{"class":337},[60,682,341],{"class":168},[60,684,685],{"class":70}," Mostrar resumen de actualización\n",[60,687,689,692],{"class":62,"line":688},43,[60,690,691],{"class":337},"      debug",[60,693,380],{"class":168},[60,695,697,700,702,704,707],{"class":62,"line":696},44,[60,698,699],{"class":337},"        msg",[60,701,341],{"class":168},[60,703,120],{"class":119},[60,705,706],{"class":70},"Paquetes actualizados: {{ upgrade_result.stdout_lines | default(['Ninguno']) }}",[60,708,126],{"class":119},[12,710,711],{},"Ejecútalo:",[50,713,715],{"className":52,"code":714,"language":54,"meta":55,"style":55},"ansible-playbook -i inventory.ini playbooks/base.yml\n",[57,716,717],{"__ignoreMap":55},[60,718,719,722,724,726],{"class":62,"line":63},[60,720,721],{"class":66},"ansible-playbook",[60,723,281],{"class":90},[60,725,284],{"class":70},[60,727,728],{"class":70}," playbooks/base.yml\n",[12,730,731,732,735,736,739,740,743,744,746],{},"Ansible muestra cada tarea con su resultado: ",[57,733,734],{},"ok"," (ya estaba como se pidió), ",[57,737,738],{},"changed"," (se aplicó un cambio) o ",[57,741,742],{},"failed",". Eso es idempotencia — si lo ejecutas otra vez, todo saldrá ",[57,745,734],{}," porque ya no hay cambios pendientes.",[25,748,750],{"id":749},"paso-4-playbook-de-hardening","Paso 4: Playbook de hardening",[12,752,753,754,757],{},"Ahora automaticemos los pasos de ",[19,755,756],{"href":34},"hardening de servidores"," que hicimos manualmente:",[50,759,761],{"className":314,"code":760,"language":316,"meta":55,"style":55},"# playbooks/hardening.yml\n---\n- name: Hardening de servidores Linux\n  hosts: all\n  become: true\n\n  vars:\n    ssh_port: 2222\n    allowed_users: \"deploy admin\"\n    fail2ban_maxretry: 3\n    fail2ban_bantime: 3600\n\n  tasks:\n    # --- SSH ---\n    - name: Configurar SSH seguro\n      lineinfile:\n        path: /etc/ssh/sshd_config\n        regexp: \"{{ item.regexp }}\"\n        line: \"{{ item.line }}\"\n        state: present\n      loop:\n        - { regexp: '^#?Port', line: 'Port {{ ssh_port }}' }\n        - { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }\n        - { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }\n        - { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }\n        - { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }\n        - { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }\n        - { regexp: '^#?AllowUsers', line: 'AllowUsers {{ allowed_users }}' }\n      notify: Reiniciar SSH\n\n    # --- Firewall ---\n    - name: Instalar UFW\n      apt:\n        name: ufw\n        state: present\n\n    - name: Configurar política por defecto\n      ufw:\n        direction: \"{{ item.direction }}\"\n        policy: \"{{ item.policy }}\"\n      loop:\n        - { direction: incoming, policy: deny }\n        - { direction: outgoing, policy: allow }\n\n    - name: Permitir SSH\n      ufw:\n        rule: limit\n        port: \"{{ ssh_port }}\"\n        proto: tcp\n        comment: \"SSH con rate limit\"\n\n    - name: Habilitar UFW\n      ufw:\n        state: enabled\n\n    # --- fail2ban ---\n    - name: Instalar fail2ban\n      apt:\n        name: fail2ban\n        state: present\n\n    - name: Configurar fail2ban\n      template:\n        src: templates/jail.local.j2\n        dest: /etc/fail2ban/jail.local\n        mode: '0644'\n      notify: Reiniciar fail2ban\n\n    # --- Actualizaciones automáticas ---\n    - name: Instalar unattended-upgrades\n      apt:\n        name: unattended-upgrades\n        state: present\n\n    - name: Habilitar actualizaciones automáticas\n      copy:\n        content: |\n          APT::Periodic::Update-Package-Lists \"1\";\n          APT::Periodic::Unattended-Upgrade \"1\";\n        dest: /etc/apt/apt.conf.d/20auto-upgrades\n        mode: '0644'\n\n    # --- Kernel hardening ---\n    - name: Aplicar parámetros de kernel seguros\n      sysctl:\n        name: \"{{ item.key }}\"\n        value: \"{{ item.value }}\"\n        sysctl_set: true\n        reload: true\n      loop:\n        - { key: 'net.ipv4.conf.all.accept_redirects', value: '0' }\n        - { key: 'net.ipv4.conf.all.send_redirects', value: '0' }\n        - { key: 'net.ipv4.tcp_syncookies', value: '1' }\n        - { key: 'net.ipv4.icmp_echo_ignore_broadcasts', value: '1' }\n        - { key: 'kernel.dmesg_restrict', value: '1' }\n        - { key: 'kernel.randomize_va_space', value: '2' }\n\n    # --- Auditoría ---\n    - name: Instalar auditd\n      apt:\n        name: auditd\n        state: present\n\n    - name: Habilitar auditd\n      systemd:\n        name: auditd\n        state: started\n        enabled: true\n\n  handlers:\n    - name: Reiniciar SSH\n      systemd:\n        name: sshd\n        state: restarted\n\n    - name: Reiniciar fail2ban\n      systemd:\n        name: fail2ban\n        state: restarted\n",[57,762,763,768,772,783,791,799,803,810,820,834,844,853,857,863,868,879,886,896,910,924,932,939,979,1011,1043,1075,1107,1139,1171,1181,1185,1190,1201,1207,1216,1224,1228,1239,1246,1260,1274,1280,1306,1330,1334,1346,1353,1364,1379,1390,1405,1410,1422,1429,1439,1444,1450,1462,1469,1479,1488,1493,1505,1513,1524,1535,1551,1561,1566,1572,1584,1591,1601,1610,1615,1627,1635,1647,1653,1659,1669,1682,1687,1693,1705,1713,1727,1742,1752,1762,1769,1804,1836,1869,1901,1933,1966,1971,1977,1989,1996,2006,2015,2020,2032,2039,2048,2057,2066,2071,2079,2090,2097,2107,2117,2122,2133,2140,2149],{"__ignoreMap":55},[60,764,765],{"class":62,"line":63},[60,766,767],{"class":323},"# playbooks/hardening.yml\n",[60,769,770],{"class":62,"line":77},[60,771,329],{"class":66},[60,773,774,776,778,780],{"class":62,"line":94},[60,775,334],{"class":168},[60,777,338],{"class":337},[60,779,341],{"class":168},[60,781,782],{"class":70}," Hardening de servidores Linux\n",[60,784,785,787,789],{"class":62,"line":201},[60,786,349],{"class":337},[60,788,341],{"class":168},[60,790,354],{"class":70},[60,792,793,795,797],{"class":62,"line":207},[60,794,359],{"class":337},[60,796,341],{"class":168},[60,798,409],{"class":364},[60,800,801],{"class":62,"line":214},[60,802,211],{"emptyLinePlaceholder":210},[60,804,805,808],{"class":62,"line":220},[60,806,807],{"class":337},"  vars",[60,809,380],{"class":168},[60,811,812,815,817],{"class":62,"line":226},[60,813,814],{"class":337},"    ssh_port",[60,816,341],{"class":168},[60,818,819],{"class":419}," 2222\n",[60,821,822,825,827,829,832],{"class":62,"line":231},[60,823,824],{"class":337},"    allowed_users",[60,826,341],{"class":168},[60,828,120],{"class":119},[60,830,831],{"class":70},"deploy admin",[60,833,126],{"class":119},[60,835,836,839,841],{"class":62,"line":237},[60,837,838],{"class":337},"    fail2ban_maxretry",[60,840,341],{"class":168},[60,842,843],{"class":419}," 3\n",[60,845,846,849,851],{"class":62,"line":243},[60,847,848],{"class":337},"    fail2ban_bantime",[60,850,341],{"class":168},[60,852,420],{"class":419},[60,854,855],{"class":62,"line":248},[60,856,211],{"emptyLinePlaceholder":210},[60,858,859,861],{"class":62,"line":254},[60,860,377],{"class":337},[60,862,380],{"class":168},[60,864,865],{"class":62,"line":260},[60,866,867],{"class":323},"    # --- SSH ---\n",[60,869,870,872,874,876],{"class":62,"line":444},[60,871,385],{"class":168},[60,873,338],{"class":337},[60,875,341],{"class":168},[60,877,878],{"class":70}," Configurar SSH seguro\n",[60,880,881,884],{"class":62,"line":455},[60,882,883],{"class":337},"      lineinfile",[60,885,380],{"class":168},[60,887,888,891,893],{"class":62,"line":466},[60,889,890],{"class":337},"        path",[60,892,341],{"class":168},[60,894,895],{"class":70}," /etc/ssh/sshd_config\n",[60,897,898,901,903,905,908],{"class":62,"line":471},[60,899,900],{"class":337},"        regexp",[60,902,341],{"class":168},[60,904,120],{"class":119},[60,906,907],{"class":70},"{{ item.regexp }}",[60,909,126],{"class":119},[60,911,912,915,917,919,922],{"class":62,"line":483},[60,913,914],{"class":337},"        line",[60,916,341],{"class":168},[60,918,120],{"class":119},[60,920,921],{"class":70},"{{ item.line }}",[60,923,126],{"class":119},[60,925,926,928,930],{"class":62,"line":490},[60,927,574],{"class":337},[60,929,341],{"class":168},[60,931,579],{"class":70},[60,933,934,937],{"class":62,"line":498},[60,935,936],{"class":337},"      loop",[60,938,380],{"class":168},[60,940,941,944,947,950,952,955,958,961,964,967,969,971,974,976],{"class":62,"line":507},[60,942,943],{"class":168},"        -",[60,945,946],{"class":168}," {",[60,948,949],{"class":337}," regexp",[60,951,341],{"class":168},[60,953,954],{"class":119}," '",[60,956,957],{"class":70},"^#?Port",[60,959,960],{"class":119},"'",[60,962,963],{"class":168},",",[60,965,966],{"class":337}," line",[60,968,341],{"class":168},[60,970,954],{"class":119},[60,972,973],{"class":70},"Port {{ ssh_port }}",[60,975,960],{"class":119},[60,977,978],{"class":168}," }\n",[60,980,981,983,985,987,989,991,994,996,998,1000,1002,1004,1007,1009],{"class":62,"line":515},[60,982,943],{"class":168},[60,984,946],{"class":168},[60,986,949],{"class":337},[60,988,341],{"class":168},[60,990,954],{"class":119},[60,992,993],{"class":70},"^#?PermitRootLogin",[60,995,960],{"class":119},[60,997,963],{"class":168},[60,999,966],{"class":337},[60,1001,341],{"class":168},[60,1003,954],{"class":119},[60,1005,1006],{"class":70},"PermitRootLogin no",[60,1008,960],{"class":119},[60,1010,978],{"class":168},[60,1012,1013,1015,1017,1019,1021,1023,1026,1028,1030,1032,1034,1036,1039,1041],{"class":62,"line":523},[60,1014,943],{"class":168},[60,1016,946],{"class":168},[60,1018,949],{"class":337},[60,1020,341],{"class":168},[60,1022,954],{"class":119},[60,1024,1025],{"class":70},"^#?PasswordAuthentication",[60,1027,960],{"class":119},[60,1029,963],{"class":168},[60,1031,966],{"class":337},[60,1033,341],{"class":168},[60,1035,954],{"class":119},[60,1037,1038],{"class":70},"PasswordAuthentication no",[60,1040,960],{"class":119},[60,1042,978],{"class":168},[60,1044,1045,1047,1049,1051,1053,1055,1058,1060,1062,1064,1066,1068,1071,1073],{"class":62,"line":531},[60,1046,943],{"class":168},[60,1048,946],{"class":168},[60,1050,949],{"class":337},[60,1052,341],{"class":168},[60,1054,954],{"class":119},[60,1056,1057],{"class":70},"^#?X11Forwarding",[60,1059,960],{"class":119},[60,1061,963],{"class":168},[60,1063,966],{"class":337},[60,1065,341],{"class":168},[60,1067,954],{"class":119},[60,1069,1070],{"class":70},"X11Forwarding no",[60,1072,960],{"class":119},[60,1074,978],{"class":168},[60,1076,1077,1079,1081,1083,1085,1087,1090,1092,1094,1096,1098,1100,1103,1105],{"class":62,"line":539},[60,1078,943],{"class":168},[60,1080,946],{"class":168},[60,1082,949],{"class":337},[60,1084,341],{"class":168},[60,1086,954],{"class":119},[60,1088,1089],{"class":70},"^#?MaxAuthTries",[60,1091,960],{"class":119},[60,1093,963],{"class":168},[60,1095,966],{"class":337},[60,1097,341],{"class":168},[60,1099,954],{"class":119},[60,1101,1102],{"class":70},"MaxAuthTries 3",[60,1104,960],{"class":119},[60,1106,978],{"class":168},[60,1108,1109,1111,1113,1115,1117,1119,1122,1124,1126,1128,1130,1132,1135,1137],{"class":62,"line":547},[60,1110,943],{"class":168},[60,1112,946],{"class":168},[60,1114,949],{"class":337},[60,1116,341],{"class":168},[60,1118,954],{"class":119},[60,1120,1121],{"class":70},"^#?ClientAliveInterval",[60,1123,960],{"class":119},[60,1125,963],{"class":168},[60,1127,966],{"class":337},[60,1129,341],{"class":168},[60,1131,954],{"class":119},[60,1133,1134],{"class":70},"ClientAliveInterval 300",[60,1136,960],{"class":119},[60,1138,978],{"class":168},[60,1140,1141,1143,1145,1147,1149,1151,1154,1156,1158,1160,1162,1164,1167,1169],{"class":62,"line":555},[60,1142,943],{"class":168},[60,1144,946],{"class":168},[60,1146,949],{"class":337},[60,1148,341],{"class":168},[60,1150,954],{"class":119},[60,1152,1153],{"class":70},"^#?AllowUsers",[60,1155,960],{"class":119},[60,1157,963],{"class":168},[60,1159,966],{"class":337},[60,1161,341],{"class":168},[60,1163,954],{"class":119},[60,1165,1166],{"class":70},"AllowUsers {{ allowed_users }}",[60,1168,960],{"class":119},[60,1170,978],{"class":168},[60,1172,1173,1176,1178],{"class":62,"line":563},[60,1174,1175],{"class":337},"      notify",[60,1177,341],{"class":168},[60,1179,1180],{"class":70}," Reiniciar SSH\n",[60,1182,1183],{"class":62,"line":571},[60,1184,211],{"emptyLinePlaceholder":210},[60,1186,1187],{"class":62,"line":582},[60,1188,1189],{"class":323},"    # --- Firewall ---\n",[60,1191,1192,1194,1196,1198],{"class":62,"line":587},[60,1193,385],{"class":168},[60,1195,338],{"class":337},[60,1197,341],{"class":168},[60,1199,1200],{"class":70}," Instalar UFW\n",[60,1202,1203,1205],{"class":62,"line":599},[60,1204,397],{"class":337},[60,1206,380],{"class":168},[60,1208,1209,1211,1213],{"class":62,"line":607},[60,1210,493],{"class":337},[60,1212,341],{"class":168},[60,1214,1215],{"class":70}," ufw\n",[60,1217,1218,1220,1222],{"class":62,"line":617},[60,1219,574],{"class":337},[60,1221,341],{"class":168},[60,1223,579],{"class":70},[60,1225,1226],{"class":62,"line":622},[60,1227,211],{"emptyLinePlaceholder":210},[60,1229,1230,1232,1234,1236],{"class":62,"line":634},[60,1231,385],{"class":168},[60,1233,338],{"class":337},[60,1235,341],{"class":168},[60,1237,1238],{"class":70}," Configurar política por defecto\n",[60,1240,1241,1244],{"class":62,"line":642},[60,1242,1243],{"class":337},"      ufw",[60,1245,380],{"class":168},[60,1247,1248,1251,1253,1255,1258],{"class":62,"line":651},[60,1249,1250],{"class":337},"        direction",[60,1252,341],{"class":168},[60,1254,120],{"class":119},[60,1256,1257],{"class":70},"{{ item.direction }}",[60,1259,126],{"class":119},[60,1261,1262,1265,1267,1269,1272],{"class":62,"line":661},[60,1263,1264],{"class":337},"        policy",[60,1266,341],{"class":168},[60,1268,120],{"class":119},[60,1270,1271],{"class":70},"{{ item.policy }}",[60,1273,126],{"class":119},[60,1275,1276,1278],{"class":62,"line":671},[60,1277,936],{"class":337},[60,1279,380],{"class":168},[60,1281,1282,1284,1286,1289,1291,1294,1296,1299,1301,1304],{"class":62,"line":676},[60,1283,943],{"class":168},[60,1285,946],{"class":168},[60,1287,1288],{"class":337}," direction",[60,1290,341],{"class":168},[60,1292,1293],{"class":70}," incoming",[60,1295,963],{"class":168},[60,1297,1298],{"class":337}," policy",[60,1300,341],{"class":168},[60,1302,1303],{"class":70}," deny",[60,1305,978],{"class":168},[60,1307,1308,1310,1312,1314,1316,1319,1321,1323,1325,1328],{"class":62,"line":688},[60,1309,943],{"class":168},[60,1311,946],{"class":168},[60,1313,1288],{"class":337},[60,1315,341],{"class":168},[60,1317,1318],{"class":70}," outgoing",[60,1320,963],{"class":168},[60,1322,1298],{"class":337},[60,1324,341],{"class":168},[60,1326,1327],{"class":70}," allow",[60,1329,978],{"class":168},[60,1331,1332],{"class":62,"line":696},[60,1333,211],{"emptyLinePlaceholder":210},[60,1335,1337,1339,1341,1343],{"class":62,"line":1336},45,[60,1338,385],{"class":168},[60,1340,338],{"class":337},[60,1342,341],{"class":168},[60,1344,1345],{"class":70}," Permitir SSH\n",[60,1347,1349,1351],{"class":62,"line":1348},46,[60,1350,1243],{"class":337},[60,1352,380],{"class":168},[60,1354,1356,1359,1361],{"class":62,"line":1355},47,[60,1357,1358],{"class":337},"        rule",[60,1360,341],{"class":168},[60,1362,1363],{"class":70}," limit\n",[60,1365,1367,1370,1372,1374,1377],{"class":62,"line":1366},48,[60,1368,1369],{"class":337},"        port",[60,1371,341],{"class":168},[60,1373,120],{"class":119},[60,1375,1376],{"class":70},"{{ ssh_port }}",[60,1378,126],{"class":119},[60,1380,1382,1385,1387],{"class":62,"line":1381},49,[60,1383,1384],{"class":337},"        proto",[60,1386,341],{"class":168},[60,1388,1389],{"class":70}," tcp\n",[60,1391,1393,1396,1398,1400,1403],{"class":62,"line":1392},50,[60,1394,1395],{"class":337},"        comment",[60,1397,341],{"class":168},[60,1399,120],{"class":119},[60,1401,1402],{"class":70},"SSH con rate limit",[60,1404,126],{"class":119},[60,1406,1408],{"class":62,"line":1407},51,[60,1409,211],{"emptyLinePlaceholder":210},[60,1411,1413,1415,1417,1419],{"class":62,"line":1412},52,[60,1414,385],{"class":168},[60,1416,338],{"class":337},[60,1418,341],{"class":168},[60,1420,1421],{"class":70}," Habilitar UFW\n",[60,1423,1425,1427],{"class":62,"line":1424},53,[60,1426,1243],{"class":337},[60,1428,380],{"class":168},[60,1430,1432,1434,1436],{"class":62,"line":1431},54,[60,1433,574],{"class":337},[60,1435,341],{"class":168},[60,1437,1438],{"class":70}," enabled\n",[60,1440,1442],{"class":62,"line":1441},55,[60,1443,211],{"emptyLinePlaceholder":210},[60,1445,1447],{"class":62,"line":1446},56,[60,1448,1449],{"class":323},"    # --- fail2ban ---\n",[60,1451,1453,1455,1457,1459],{"class":62,"line":1452},57,[60,1454,385],{"class":168},[60,1456,338],{"class":337},[60,1458,341],{"class":168},[60,1460,1461],{"class":70}," Instalar fail2ban\n",[60,1463,1465,1467],{"class":62,"line":1464},58,[60,1466,397],{"class":337},[60,1468,380],{"class":168},[60,1470,1472,1474,1476],{"class":62,"line":1471},59,[60,1473,493],{"class":337},[60,1475,341],{"class":168},[60,1477,1478],{"class":70}," fail2ban\n",[60,1480,1482,1484,1486],{"class":62,"line":1481},60,[60,1483,574],{"class":337},[60,1485,341],{"class":168},[60,1487,579],{"class":70},[60,1489,1491],{"class":62,"line":1490},61,[60,1492,211],{"emptyLinePlaceholder":210},[60,1494,1496,1498,1500,1502],{"class":62,"line":1495},62,[60,1497,385],{"class":168},[60,1499,338],{"class":337},[60,1501,341],{"class":168},[60,1503,1504],{"class":70}," Configurar fail2ban\n",[60,1506,1508,1511],{"class":62,"line":1507},63,[60,1509,1510],{"class":337},"      template",[60,1512,380],{"class":168},[60,1514,1516,1519,1521],{"class":62,"line":1515},64,[60,1517,1518],{"class":337},"        src",[60,1520,341],{"class":168},[60,1522,1523],{"class":70}," templates/jail.local.j2\n",[60,1525,1527,1530,1532],{"class":62,"line":1526},65,[60,1528,1529],{"class":337},"        dest",[60,1531,341],{"class":168},[60,1533,1534],{"class":70}," /etc/fail2ban/jail.local\n",[60,1536,1538,1541,1543,1545,1548],{"class":62,"line":1537},66,[60,1539,1540],{"class":337},"        mode",[60,1542,341],{"class":168},[60,1544,954],{"class":119},[60,1546,1547],{"class":70},"0644",[60,1549,1550],{"class":119},"'\n",[60,1552,1554,1556,1558],{"class":62,"line":1553},67,[60,1555,1175],{"class":337},[60,1557,341],{"class":168},[60,1559,1560],{"class":70}," Reiniciar fail2ban\n",[60,1562,1564],{"class":62,"line":1563},68,[60,1565,211],{"emptyLinePlaceholder":210},[60,1567,1569],{"class":62,"line":1568},69,[60,1570,1571],{"class":323},"    # --- Actualizaciones automáticas ---\n",[60,1573,1575,1577,1579,1581],{"class":62,"line":1574},70,[60,1576,385],{"class":168},[60,1578,338],{"class":337},[60,1580,341],{"class":168},[60,1582,1583],{"class":70}," Instalar unattended-upgrades\n",[60,1585,1587,1589],{"class":62,"line":1586},71,[60,1588,397],{"class":337},[60,1590,380],{"class":168},[60,1592,1594,1596,1598],{"class":62,"line":1593},72,[60,1595,493],{"class":337},[60,1597,341],{"class":168},[60,1599,1600],{"class":70}," unattended-upgrades\n",[60,1602,1604,1606,1608],{"class":62,"line":1603},73,[60,1605,574],{"class":337},[60,1607,341],{"class":168},[60,1609,579],{"class":70},[60,1611,1613],{"class":62,"line":1612},74,[60,1614,211],{"emptyLinePlaceholder":210},[60,1616,1618,1620,1622,1624],{"class":62,"line":1617},75,[60,1619,385],{"class":168},[60,1621,338],{"class":337},[60,1623,341],{"class":168},[60,1625,1626],{"class":70}," Habilitar actualizaciones automáticas\n",[60,1628,1630,1633],{"class":62,"line":1629},76,[60,1631,1632],{"class":337},"      copy",[60,1634,380],{"class":168},[60,1636,1638,1641,1643],{"class":62,"line":1637},77,[60,1639,1640],{"class":337},"        content",[60,1642,341],{"class":168},[60,1644,1646],{"class":1645},"sVHd0"," |\n",[60,1648,1650],{"class":62,"line":1649},78,[60,1651,1652],{"class":70},"          APT::Periodic::Update-Package-Lists \"1\";\n",[60,1654,1656],{"class":62,"line":1655},79,[60,1657,1658],{"class":70},"          APT::Periodic::Unattended-Upgrade \"1\";\n",[60,1660,1662,1664,1666],{"class":62,"line":1661},80,[60,1663,1529],{"class":337},[60,1665,341],{"class":168},[60,1667,1668],{"class":70}," /etc/apt/apt.conf.d/20auto-upgrades\n",[60,1670,1672,1674,1676,1678,1680],{"class":62,"line":1671},81,[60,1673,1540],{"class":337},[60,1675,341],{"class":168},[60,1677,954],{"class":119},[60,1679,1547],{"class":70},[60,1681,1550],{"class":119},[60,1683,1685],{"class":62,"line":1684},82,[60,1686,211],{"emptyLinePlaceholder":210},[60,1688,1690],{"class":62,"line":1689},83,[60,1691,1692],{"class":323},"    # --- Kernel hardening ---\n",[60,1694,1696,1698,1700,1702],{"class":62,"line":1695},84,[60,1697,385],{"class":168},[60,1699,338],{"class":337},[60,1701,341],{"class":168},[60,1703,1704],{"class":70}," Aplicar parámetros de kernel seguros\n",[60,1706,1708,1711],{"class":62,"line":1707},85,[60,1709,1710],{"class":337},"      sysctl",[60,1712,380],{"class":168},[60,1714,1716,1718,1720,1722,1725],{"class":62,"line":1715},86,[60,1717,493],{"class":337},[60,1719,341],{"class":168},[60,1721,120],{"class":119},[60,1723,1724],{"class":70},"{{ item.key }}",[60,1726,126],{"class":119},[60,1728,1730,1733,1735,1737,1740],{"class":62,"line":1729},87,[60,1731,1732],{"class":337},"        value",[60,1734,341],{"class":168},[60,1736,120],{"class":119},[60,1738,1739],{"class":70},"{{ item.value }}",[60,1741,126],{"class":119},[60,1743,1745,1748,1750],{"class":62,"line":1744},88,[60,1746,1747],{"class":337},"        sysctl_set",[60,1749,341],{"class":168},[60,1751,409],{"class":364},[60,1753,1755,1758,1760],{"class":62,"line":1754},89,[60,1756,1757],{"class":337},"        reload",[60,1759,341],{"class":168},[60,1761,409],{"class":364},[60,1763,1765,1767],{"class":62,"line":1764},90,[60,1766,936],{"class":337},[60,1768,380],{"class":168},[60,1770,1772,1774,1776,1779,1781,1783,1786,1788,1790,1793,1795,1797,1800,1802],{"class":62,"line":1771},91,[60,1773,943],{"class":168},[60,1775,946],{"class":168},[60,1777,1778],{"class":337}," key",[60,1780,341],{"class":168},[60,1782,954],{"class":119},[60,1784,1785],{"class":70},"net.ipv4.conf.all.accept_redirects",[60,1787,960],{"class":119},[60,1789,963],{"class":168},[60,1791,1792],{"class":337}," value",[60,1794,341],{"class":168},[60,1796,954],{"class":119},[60,1798,1799],{"class":70},"0",[60,1801,960],{"class":119},[60,1803,978],{"class":168},[60,1805,1807,1809,1811,1813,1815,1817,1820,1822,1824,1826,1828,1830,1832,1834],{"class":62,"line":1806},92,[60,1808,943],{"class":168},[60,1810,946],{"class":168},[60,1812,1778],{"class":337},[60,1814,341],{"class":168},[60,1816,954],{"class":119},[60,1818,1819],{"class":70},"net.ipv4.conf.all.send_redirects",[60,1821,960],{"class":119},[60,1823,963],{"class":168},[60,1825,1792],{"class":337},[60,1827,341],{"class":168},[60,1829,954],{"class":119},[60,1831,1799],{"class":70},[60,1833,960],{"class":119},[60,1835,978],{"class":168},[60,1837,1839,1841,1843,1845,1847,1849,1852,1854,1856,1858,1860,1862,1865,1867],{"class":62,"line":1838},93,[60,1840,943],{"class":168},[60,1842,946],{"class":168},[60,1844,1778],{"class":337},[60,1846,341],{"class":168},[60,1848,954],{"class":119},[60,1850,1851],{"class":70},"net.ipv4.tcp_syncookies",[60,1853,960],{"class":119},[60,1855,963],{"class":168},[60,1857,1792],{"class":337},[60,1859,341],{"class":168},[60,1861,954],{"class":119},[60,1863,1864],{"class":70},"1",[60,1866,960],{"class":119},[60,1868,978],{"class":168},[60,1870,1872,1874,1876,1878,1880,1882,1885,1887,1889,1891,1893,1895,1897,1899],{"class":62,"line":1871},94,[60,1873,943],{"class":168},[60,1875,946],{"class":168},[60,1877,1778],{"class":337},[60,1879,341],{"class":168},[60,1881,954],{"class":119},[60,1883,1884],{"class":70},"net.ipv4.icmp_echo_ignore_broadcasts",[60,1886,960],{"class":119},[60,1888,963],{"class":168},[60,1890,1792],{"class":337},[60,1892,341],{"class":168},[60,1894,954],{"class":119},[60,1896,1864],{"class":70},[60,1898,960],{"class":119},[60,1900,978],{"class":168},[60,1902,1904,1906,1908,1910,1912,1914,1917,1919,1921,1923,1925,1927,1929,1931],{"class":62,"line":1903},95,[60,1905,943],{"class":168},[60,1907,946],{"class":168},[60,1909,1778],{"class":337},[60,1911,341],{"class":168},[60,1913,954],{"class":119},[60,1915,1916],{"class":70},"kernel.dmesg_restrict",[60,1918,960],{"class":119},[60,1920,963],{"class":168},[60,1922,1792],{"class":337},[60,1924,341],{"class":168},[60,1926,954],{"class":119},[60,1928,1864],{"class":70},[60,1930,960],{"class":119},[60,1932,978],{"class":168},[60,1934,1936,1938,1940,1942,1944,1946,1949,1951,1953,1955,1957,1959,1962,1964],{"class":62,"line":1935},96,[60,1937,943],{"class":168},[60,1939,946],{"class":168},[60,1941,1778],{"class":337},[60,1943,341],{"class":168},[60,1945,954],{"class":119},[60,1947,1948],{"class":70},"kernel.randomize_va_space",[60,1950,960],{"class":119},[60,1952,963],{"class":168},[60,1954,1792],{"class":337},[60,1956,341],{"class":168},[60,1958,954],{"class":119},[60,1960,1961],{"class":70},"2",[60,1963,960],{"class":119},[60,1965,978],{"class":168},[60,1967,1969],{"class":62,"line":1968},97,[60,1970,211],{"emptyLinePlaceholder":210},[60,1972,1974],{"class":62,"line":1973},98,[60,1975,1976],{"class":323},"    # --- Auditoría ---\n",[60,1978,1980,1982,1984,1986],{"class":62,"line":1979},99,[60,1981,385],{"class":168},[60,1983,338],{"class":337},[60,1985,341],{"class":168},[60,1987,1988],{"class":70}," Instalar auditd\n",[60,1990,1992,1994],{"class":62,"line":1991},100,[60,1993,397],{"class":337},[60,1995,380],{"class":168},[60,1997,1999,2001,2003],{"class":62,"line":1998},101,[60,2000,493],{"class":337},[60,2002,341],{"class":168},[60,2004,2005],{"class":70}," auditd\n",[60,2007,2009,2011,2013],{"class":62,"line":2008},102,[60,2010,574],{"class":337},[60,2012,341],{"class":168},[60,2014,579],{"class":70},[60,2016,2018],{"class":62,"line":2017},103,[60,2019,211],{"emptyLinePlaceholder":210},[60,2021,2023,2025,2027,2029],{"class":62,"line":2022},104,[60,2024,385],{"class":168},[60,2026,338],{"class":337},[60,2028,341],{"class":168},[60,2030,2031],{"class":70}," Habilitar auditd\n",[60,2033,2035,2037],{"class":62,"line":2034},105,[60,2036,637],{"class":337},[60,2038,380],{"class":168},[60,2040,2042,2044,2046],{"class":62,"line":2041},106,[60,2043,493],{"class":337},[60,2045,341],{"class":168},[60,2047,2005],{"class":70},[60,2049,2051,2053,2055],{"class":62,"line":2050},107,[60,2052,574],{"class":337},[60,2054,341],{"class":168},[60,2056,658],{"class":70},[60,2058,2060,2062,2064],{"class":62,"line":2059},108,[60,2061,664],{"class":337},[60,2063,341],{"class":168},[60,2065,409],{"class":364},[60,2067,2069],{"class":62,"line":2068},109,[60,2070,211],{"emptyLinePlaceholder":210},[60,2072,2074,2077],{"class":62,"line":2073},110,[60,2075,2076],{"class":337},"  handlers",[60,2078,380],{"class":168},[60,2080,2082,2084,2086,2088],{"class":62,"line":2081},111,[60,2083,385],{"class":168},[60,2085,338],{"class":337},[60,2087,341],{"class":168},[60,2089,1180],{"class":70},[60,2091,2093,2095],{"class":62,"line":2092},112,[60,2094,637],{"class":337},[60,2096,380],{"class":168},[60,2098,2100,2102,2104],{"class":62,"line":2099},113,[60,2101,493],{"class":337},[60,2103,341],{"class":168},[60,2105,2106],{"class":70}," sshd\n",[60,2108,2110,2112,2114],{"class":62,"line":2109},114,[60,2111,574],{"class":337},[60,2113,341],{"class":168},[60,2115,2116],{"class":70}," restarted\n",[60,2118,2120],{"class":62,"line":2119},115,[60,2121,211],{"emptyLinePlaceholder":210},[60,2123,2125,2127,2129,2131],{"class":62,"line":2124},116,[60,2126,385],{"class":168},[60,2128,338],{"class":337},[60,2130,341],{"class":168},[60,2132,1560],{"class":70},[60,2134,2136,2138],{"class":62,"line":2135},117,[60,2137,637],{"class":337},[60,2139,380],{"class":168},[60,2141,2143,2145,2147],{"class":62,"line":2142},118,[60,2144,493],{"class":337},[60,2146,341],{"class":168},[60,2148,1478],{"class":70},[60,2150,2152,2154,2156],{"class":62,"line":2151},119,[60,2153,574],{"class":337},[60,2155,341],{"class":168},[60,2157,2116],{"class":70},[12,2159,2160],{},"Crea el template de fail2ban:",[50,2162,2164],{"className":52,"code":2163,"language":54,"meta":55,"style":55},"mkdir -p templates\n",[57,2165,2166],{"__ignoreMap":55},[60,2167,2168,2170,2172],{"class":62,"line":63},[60,2169,159],{"class":66},[60,2171,162],{"class":90},[60,2173,2174],{"class":70}," templates\n",[50,2176,2180],{"className":2177,"code":2178,"language":2179,"meta":55,"style":55},"language-jinja2 shiki shiki-themes material-theme-lighter github-light github-dark","# templates/jail.local.j2\n[DEFAULT]\nbantime = {{ fail2ban_bantime }}\nfindtime = 600\nmaxretry = {{ fail2ban_maxretry }}\nbanaction = ufw\n\n[sshd]\nenabled = true\nport = {{ ssh_port }}\nfilter = sshd\nlogpath = /var/log/auth.log\nmaxretry = {{ fail2ban_maxretry }}\n","jinja2",[57,2181,2182,2187,2192,2197,2202,2207,2212,2216,2221,2226,2231,2236,2241],{"__ignoreMap":55},[60,2183,2184],{"class":62,"line":63},[60,2185,2186],{},"# templates/jail.local.j2\n",[60,2188,2189],{"class":62,"line":77},[60,2190,2191],{},"[DEFAULT]\n",[60,2193,2194],{"class":62,"line":94},[60,2195,2196],{},"bantime = {{ fail2ban_bantime }}\n",[60,2198,2199],{"class":62,"line":201},[60,2200,2201],{},"findtime = 600\n",[60,2203,2204],{"class":62,"line":207},[60,2205,2206],{},"maxretry = {{ fail2ban_maxretry }}\n",[60,2208,2209],{"class":62,"line":214},[60,2210,2211],{},"banaction = ufw\n",[60,2213,2214],{"class":62,"line":220},[60,2215,211],{"emptyLinePlaceholder":210},[60,2217,2218],{"class":62,"line":226},[60,2219,2220],{},"[sshd]\n",[60,2222,2223],{"class":62,"line":231},[60,2224,2225],{},"enabled = true\n",[60,2227,2228],{"class":62,"line":237},[60,2229,2230],{},"port = {{ ssh_port }}\n",[60,2232,2233],{"class":62,"line":243},[60,2234,2235],{},"filter = sshd\n",[60,2237,2238],{"class":62,"line":248},[60,2239,2240],{},"logpath = /var/log/auth.log\n",[60,2242,2243],{"class":62,"line":254},[60,2244,2206],{},[2246,2247,2250],"alert",{"title":2248,"type":2249},"Handlers","info",[12,2251,2252,2253,2256],{},"Los handlers solo se ejecutan si la tarea que los notifica hizo un cambio. Si SSH ya estaba configurado correctamente, el handler ",[57,2254,2255],{},"Reiniciar SSH"," no se ejecuta — evitando reinicios innecesarios.",[303,2258],{},[25,2260,2262],{"id":2261},"paso-5-roles-para-organización","Paso 5: Roles para organización",[12,2264,2265],{},"Cuando tus playbooks crecen, organízalos en roles — módulos reutilizables para cada función:",[50,2267,2272],{"className":2268,"code":2270,"language":2271},[2269],"language-text","~/ansible/\n├── inventory.ini\n├── playbooks/\n│   └── site.yml              # Playbook principal que llama roles\n├── roles/\n│   ├── base/\n│   │   └── tasks/\n│   │       └── main.yml\n│   ├── hardening/\n│   │   ├── tasks/\n│   │   │   └── main.yml\n│   │   ├── templates/\n│   │   │   └── jail.local.j2\n│   │   └── defaults/\n│   │       └── main.yml      # Variables por defecto del rol\n│   ├── docker/\n│   │   └── tasks/\n│   │       └── main.yml\n│   └── monitoring/\n│       └── tasks/\n│           └── main.yml\n","text",[57,2273,2270],{"__ignoreMap":55},[12,2275,2276],{},"El playbook principal aplica los roles según el grupo del inventario:",[50,2278,2280],{"className":314,"code":2279,"language":316,"meta":55,"style":55},"# playbooks/site.yml\n---\n- name: Configuración base para todos los servidores\n  hosts: all\n  become: true\n  roles:\n    - base\n    - hardening\n\n- name: Servidores web con Docker\n  hosts: webservers\n  become: true\n  roles:\n    - docker\n\n- name: Servidores de monitoreo\n  hosts: monitoring\n  become: true\n  roles:\n    - monitoring\n",[57,2281,2282,2287,2291,2302,2310,2318,2325,2332,2339,2343,2354,2363,2371,2377,2384,2388,2399,2408,2416,2422],{"__ignoreMap":55},[60,2283,2284],{"class":62,"line":63},[60,2285,2286],{"class":323},"# playbooks/site.yml\n",[60,2288,2289],{"class":62,"line":77},[60,2290,329],{"class":66},[60,2292,2293,2295,2297,2299],{"class":62,"line":94},[60,2294,334],{"class":168},[60,2296,338],{"class":337},[60,2298,341],{"class":168},[60,2300,2301],{"class":70}," Configuración base para todos los servidores\n",[60,2303,2304,2306,2308],{"class":62,"line":201},[60,2305,349],{"class":337},[60,2307,341],{"class":168},[60,2309,354],{"class":70},[60,2311,2312,2314,2316],{"class":62,"line":207},[60,2313,359],{"class":337},[60,2315,341],{"class":168},[60,2317,409],{"class":364},[60,2319,2320,2323],{"class":62,"line":214},[60,2321,2322],{"class":337},"  roles",[60,2324,380],{"class":168},[60,2326,2327,2329],{"class":62,"line":220},[60,2328,385],{"class":168},[60,2330,2331],{"class":70}," base\n",[60,2333,2334,2336],{"class":62,"line":226},[60,2335,385],{"class":168},[60,2337,2338],{"class":70}," hardening\n",[60,2340,2341],{"class":62,"line":231},[60,2342,211],{"emptyLinePlaceholder":210},[60,2344,2345,2347,2349,2351],{"class":62,"line":237},[60,2346,334],{"class":168},[60,2348,338],{"class":337},[60,2350,341],{"class":168},[60,2352,2353],{"class":70}," Servidores web con Docker\n",[60,2355,2356,2358,2360],{"class":62,"line":243},[60,2357,349],{"class":337},[60,2359,341],{"class":168},[60,2361,2362],{"class":70}," webservers\n",[60,2364,2365,2367,2369],{"class":62,"line":248},[60,2366,359],{"class":337},[60,2368,341],{"class":168},[60,2370,409],{"class":364},[60,2372,2373,2375],{"class":62,"line":254},[60,2374,2322],{"class":337},[60,2376,380],{"class":168},[60,2378,2379,2381],{"class":62,"line":260},[60,2380,385],{"class":168},[60,2382,2383],{"class":70}," docker\n",[60,2385,2386],{"class":62,"line":444},[60,2387,211],{"emptyLinePlaceholder":210},[60,2389,2390,2392,2394,2396],{"class":62,"line":455},[60,2391,334],{"class":168},[60,2393,338],{"class":337},[60,2395,341],{"class":168},[60,2397,2398],{"class":70}," Servidores de monitoreo\n",[60,2400,2401,2403,2405],{"class":62,"line":466},[60,2402,349],{"class":337},[60,2404,341],{"class":168},[60,2406,2407],{"class":70}," monitoring\n",[60,2409,2410,2412,2414],{"class":62,"line":471},[60,2411,359],{"class":337},[60,2413,341],{"class":168},[60,2415,409],{"class":364},[60,2417,2418,2420],{"class":62,"line":483},[60,2419,2322],{"class":337},[60,2421,380],{"class":168},[60,2423,2424,2426],{"class":62,"line":490},[60,2425,385],{"class":168},[60,2427,2407],{"class":70},[50,2429,2431],{"className":52,"code":2430,"language":54,"meta":55,"style":55},"# Ejecutar todo\nansible-playbook -i inventory.ini playbooks/site.yml\n\n# Ejecutar solo en webservers\nansible-playbook -i inventory.ini playbooks/site.yml --limit webservers\n\n# Ejecutar solo el rol de hardening\nansible-playbook -i inventory.ini playbooks/site.yml --tags hardening\n",[57,2432,2433,2438,2449,2453,2458,2474,2478,2483],{"__ignoreMap":55},[60,2434,2435],{"class":62,"line":63},[60,2436,2437],{"class":323},"# Ejecutar todo\n",[60,2439,2440,2442,2444,2446],{"class":62,"line":77},[60,2441,721],{"class":66},[60,2443,281],{"class":90},[60,2445,284],{"class":70},[60,2447,2448],{"class":70}," playbooks/site.yml\n",[60,2450,2451],{"class":62,"line":94},[60,2452,211],{"emptyLinePlaceholder":210},[60,2454,2455],{"class":62,"line":201},[60,2456,2457],{"class":323},"# Ejecutar solo en webservers\n",[60,2459,2460,2462,2464,2466,2469,2472],{"class":62,"line":207},[60,2461,721],{"class":66},[60,2463,281],{"class":90},[60,2465,284],{"class":70},[60,2467,2468],{"class":70}," playbooks/site.yml",[60,2470,2471],{"class":90}," --limit",[60,2473,2362],{"class":70},[60,2475,2476],{"class":62,"line":214},[60,2477,211],{"emptyLinePlaceholder":210},[60,2479,2480],{"class":62,"line":220},[60,2481,2482],{"class":323},"# Ejecutar solo el rol de hardening\n",[60,2484,2485,2487,2489,2491,2493,2496],{"class":62,"line":226},[60,2486,721],{"class":66},[60,2488,281],{"class":90},[60,2490,284],{"class":70},[60,2492,2468],{"class":70},[60,2494,2495],{"class":90}," --tags",[60,2497,2338],{"class":70},[25,2499,2501],{"id":2500},"paso-6-playbook-de-docker","Paso 6: Playbook de Docker",[12,2503,2504,2505,2509],{},"Un ejemplo práctico — instalar ",[19,2506,2508],{"href":2507},"/tecnologias/docker","Docker"," en todos los webservers:",[50,2511,2513],{"className":314,"code":2512,"language":316,"meta":55,"style":55},"# roles/docker/tasks/main.yml\n---\n- name: Instalar dependencias\n  apt:\n    name:\n      - ca-certificates\n      - curl\n      - gnupg\n    state: present\n\n- name: Agregar GPG key de Docker\n  apt_key:\n    url: https://download.docker.com/linux/ubuntu/gpg\n    state: present\n\n- name: Agregar repositorio de Docker\n  apt_repository:\n    repo: \"deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable\"\n    state: present\n\n- name: Instalar Docker\n  apt:\n    name:\n      - docker-ce\n      - docker-ce-cli\n      - containerd.io\n      - docker-compose-plugin\n    state: present\n    update_cache: true\n\n- name: Agregar usuario al grupo docker\n  user:\n    name: \"{{ ansible_user }}\"\n    groups: docker\n    append: true\n\n- name: Habilitar Docker\n  systemd:\n    name: docker\n    state: started\n    enabled: true\n\n- name: Verificar instalación\n  command: docker --version\n  register: docker_version\n  changed_when: false\n\n- name: Mostrar versión\n  debug:\n    msg: \"Docker instalado: {{ docker_version.stdout }}\"\n",[57,2514,2515,2520,2524,2535,2542,2549,2557,2563,2570,2579,2583,2594,2601,2611,2619,2623,2634,2641,2655,2663,2667,2678,2684,2690,2697,2704,2711,2718,2726,2735,2739,2750,2757,2770,2779,2788,2792,2803,2810,2818,2826,2835,2839,2850,2860,2870,2880,2884,2895,2902],{"__ignoreMap":55},[60,2516,2517],{"class":62,"line":63},[60,2518,2519],{"class":323},"# roles/docker/tasks/main.yml\n",[60,2521,2522],{"class":62,"line":77},[60,2523,329],{"class":66},[60,2525,2526,2528,2530,2532],{"class":62,"line":94},[60,2527,334],{"class":168},[60,2529,338],{"class":337},[60,2531,341],{"class":168},[60,2533,2534],{"class":70}," Instalar dependencias\n",[60,2536,2537,2540],{"class":62,"line":201},[60,2538,2539],{"class":337},"  apt",[60,2541,380],{"class":168},[60,2543,2544,2547],{"class":62,"line":207},[60,2545,2546],{"class":337},"    name",[60,2548,380],{"class":168},[60,2550,2551,2554],{"class":62,"line":214},[60,2552,2553],{"class":168},"      -",[60,2555,2556],{"class":70}," ca-certificates\n",[60,2558,2559,2561],{"class":62,"line":220},[60,2560,2553],{"class":168},[60,2562,504],{"class":70},[60,2564,2565,2567],{"class":62,"line":226},[60,2566,2553],{"class":168},[60,2568,2569],{"class":70}," gnupg\n",[60,2571,2572,2575,2577],{"class":62,"line":231},[60,2573,2574],{"class":337},"    state",[60,2576,341],{"class":168},[60,2578,579],{"class":70},[60,2580,2581],{"class":62,"line":237},[60,2582,211],{"emptyLinePlaceholder":210},[60,2584,2585,2587,2589,2591],{"class":62,"line":243},[60,2586,334],{"class":168},[60,2588,338],{"class":337},[60,2590,341],{"class":168},[60,2592,2593],{"class":70}," Agregar GPG key de Docker\n",[60,2595,2596,2599],{"class":62,"line":248},[60,2597,2598],{"class":337},"  apt_key",[60,2600,380],{"class":168},[60,2602,2603,2606,2608],{"class":62,"line":254},[60,2604,2605],{"class":337},"    url",[60,2607,341],{"class":168},[60,2609,2610],{"class":70}," https://download.docker.com/linux/ubuntu/gpg\n",[60,2612,2613,2615,2617],{"class":62,"line":260},[60,2614,2574],{"class":337},[60,2616,341],{"class":168},[60,2618,579],{"class":70},[60,2620,2621],{"class":62,"line":444},[60,2622,211],{"emptyLinePlaceholder":210},[60,2624,2625,2627,2629,2631],{"class":62,"line":455},[60,2626,334],{"class":168},[60,2628,338],{"class":337},[60,2630,341],{"class":168},[60,2632,2633],{"class":70}," Agregar repositorio de Docker\n",[60,2635,2636,2639],{"class":62,"line":466},[60,2637,2638],{"class":337},"  apt_repository",[60,2640,380],{"class":168},[60,2642,2643,2646,2648,2650,2653],{"class":62,"line":471},[60,2644,2645],{"class":337},"    repo",[60,2647,341],{"class":168},[60,2649,120],{"class":119},[60,2651,2652],{"class":70},"deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable",[60,2654,126],{"class":119},[60,2656,2657,2659,2661],{"class":62,"line":483},[60,2658,2574],{"class":337},[60,2660,341],{"class":168},[60,2662,579],{"class":70},[60,2664,2665],{"class":62,"line":490},[60,2666,211],{"emptyLinePlaceholder":210},[60,2668,2669,2671,2673,2675],{"class":62,"line":498},[60,2670,334],{"class":168},[60,2672,338],{"class":337},[60,2674,341],{"class":168},[60,2676,2677],{"class":70}," Instalar Docker\n",[60,2679,2680,2682],{"class":62,"line":507},[60,2681,2539],{"class":337},[60,2683,380],{"class":168},[60,2685,2686,2688],{"class":62,"line":515},[60,2687,2546],{"class":337},[60,2689,380],{"class":168},[60,2691,2692,2694],{"class":62,"line":523},[60,2693,2553],{"class":168},[60,2695,2696],{"class":70}," docker-ce\n",[60,2698,2699,2701],{"class":62,"line":531},[60,2700,2553],{"class":168},[60,2702,2703],{"class":70}," docker-ce-cli\n",[60,2705,2706,2708],{"class":62,"line":539},[60,2707,2553],{"class":168},[60,2709,2710],{"class":70}," containerd.io\n",[60,2712,2713,2715],{"class":62,"line":547},[60,2714,2553],{"class":168},[60,2716,2717],{"class":70}," docker-compose-plugin\n",[60,2719,2720,2722,2724],{"class":62,"line":555},[60,2721,2574],{"class":337},[60,2723,341],{"class":168},[60,2725,579],{"class":70},[60,2727,2728,2731,2733],{"class":62,"line":563},[60,2729,2730],{"class":337},"    update_cache",[60,2732,341],{"class":168},[60,2734,409],{"class":364},[60,2736,2737],{"class":62,"line":571},[60,2738,211],{"emptyLinePlaceholder":210},[60,2740,2741,2743,2745,2747],{"class":62,"line":582},[60,2742,334],{"class":168},[60,2744,338],{"class":337},[60,2746,341],{"class":168},[60,2748,2749],{"class":70}," Agregar usuario al grupo docker\n",[60,2751,2752,2755],{"class":62,"line":587},[60,2753,2754],{"class":337},"  user",[60,2756,380],{"class":168},[60,2758,2759,2761,2763,2765,2768],{"class":62,"line":599},[60,2760,2546],{"class":337},[60,2762,341],{"class":168},[60,2764,120],{"class":119},[60,2766,2767],{"class":70},"{{ ansible_user }}",[60,2769,126],{"class":119},[60,2771,2772,2775,2777],{"class":62,"line":607},[60,2773,2774],{"class":337},"    groups",[60,2776,341],{"class":168},[60,2778,2383],{"class":70},[60,2780,2781,2784,2786],{"class":62,"line":617},[60,2782,2783],{"class":337},"    append",[60,2785,341],{"class":168},[60,2787,409],{"class":364},[60,2789,2790],{"class":62,"line":622},[60,2791,211],{"emptyLinePlaceholder":210},[60,2793,2794,2796,2798,2800],{"class":62,"line":634},[60,2795,334],{"class":168},[60,2797,338],{"class":337},[60,2799,341],{"class":168},[60,2801,2802],{"class":70}," Habilitar Docker\n",[60,2804,2805,2808],{"class":62,"line":642},[60,2806,2807],{"class":337},"  systemd",[60,2809,380],{"class":168},[60,2811,2812,2814,2816],{"class":62,"line":651},[60,2813,2546],{"class":337},[60,2815,341],{"class":168},[60,2817,2383],{"class":70},[60,2819,2820,2822,2824],{"class":62,"line":661},[60,2821,2574],{"class":337},[60,2823,341],{"class":168},[60,2825,658],{"class":70},[60,2827,2828,2831,2833],{"class":62,"line":671},[60,2829,2830],{"class":337},"    enabled",[60,2832,341],{"class":168},[60,2834,409],{"class":364},[60,2836,2837],{"class":62,"line":676},[60,2838,211],{"emptyLinePlaceholder":210},[60,2840,2841,2843,2845,2847],{"class":62,"line":688},[60,2842,334],{"class":168},[60,2844,338],{"class":337},[60,2846,341],{"class":168},[60,2848,2849],{"class":70}," Verificar instalación\n",[60,2851,2852,2855,2857],{"class":62,"line":696},[60,2853,2854],{"class":337},"  command",[60,2856,341],{"class":168},[60,2858,2859],{"class":70}," docker --version\n",[60,2861,2862,2865,2867],{"class":62,"line":1336},[60,2863,2864],{"class":337},"  register",[60,2866,341],{"class":168},[60,2868,2869],{"class":70}," docker_version\n",[60,2871,2872,2875,2877],{"class":62,"line":1348},[60,2873,2874],{"class":337},"  changed_when",[60,2876,341],{"class":168},[60,2878,2879],{"class":364}," false\n",[60,2881,2882],{"class":62,"line":1355},[60,2883,211],{"emptyLinePlaceholder":210},[60,2885,2886,2888,2890,2892],{"class":62,"line":1366},[60,2887,334],{"class":168},[60,2889,338],{"class":337},[60,2891,341],{"class":168},[60,2893,2894],{"class":70}," Mostrar versión\n",[60,2896,2897,2900],{"class":62,"line":1381},[60,2898,2899],{"class":337},"  debug",[60,2901,380],{"class":168},[60,2903,2904,2907,2909,2911,2914],{"class":62,"line":1392},[60,2905,2906],{"class":337},"    msg",[60,2908,341],{"class":168},[60,2910,120],{"class":119},[60,2912,2913],{"class":70},"Docker instalado: {{ docker_version.stdout }}",[60,2915,126],{"class":119},[25,2917,2919],{"id":2918},"paso-7-playbook-de-monitoreo","Paso 7: Playbook de monitoreo",[12,2921,2922,2923,2927],{},"Instalar ",[19,2924,2926],{"href":2925},"/blog/monitoreo-grafana-prometheus","node_exporter"," en todos los servidores:",[50,2929,2931],{"className":314,"code":2930,"language":316,"meta":55,"style":55},"# roles/monitoring/tasks/main.yml\n---\n- name: Crear usuario node_exporter\n  user:\n    name: node_exporter\n    system: true\n    shell: /bin/false\n    create_home: false\n\n- name: Descargar node_exporter\n  get_url:\n    url: \"https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz\"\n    dest: /tmp/node_exporter.tar.gz\n\n- name: Extraer binario\n  unarchive:\n    src: /tmp/node_exporter.tar.gz\n    dest: /tmp/\n    remote_src: true\n\n- name: Instalar binario\n  copy:\n    src: \"/tmp/node_exporter-{{ node_exporter_version }}.linux-amd64/node_exporter\"\n    dest: /usr/local/bin/node_exporter\n    mode: '0755'\n    remote_src: true\n\n- name: Crear servicio systemd\n  template:\n    src: node_exporter.service.j2\n    dest: /etc/systemd/system/node_exporter.service\n  notify: Reiniciar node_exporter\n\n- name: Habilitar y arrancar\n  systemd:\n    name: node_exporter\n    state: started\n    enabled: true\n    daemon_reload: true\n\n- name: Abrir puerto en firewall\n  ufw:\n    rule: allow\n    from_ip: \"{{ prometheus_server_ip }}\"\n    port: '9100'\n    proto: tcp\n    comment: \"Prometheus scraping\"\n",[57,2932,2933,2938,2942,2953,2959,2968,2977,2987,2996,3000,3011,3018,3031,3041,3045,3056,3063,3072,3081,3090,3094,3105,3112,3125,3134,3148,3156,3160,3171,3178,3187,3196,3206,3210,3221,3227,3235,3243,3251,3260,3264,3275,3282,3292,3306,3320,3329],{"__ignoreMap":55},[60,2934,2935],{"class":62,"line":63},[60,2936,2937],{"class":323},"# roles/monitoring/tasks/main.yml\n",[60,2939,2940],{"class":62,"line":77},[60,2941,329],{"class":66},[60,2943,2944,2946,2948,2950],{"class":62,"line":94},[60,2945,334],{"class":168},[60,2947,338],{"class":337},[60,2949,341],{"class":168},[60,2951,2952],{"class":70}," Crear usuario node_exporter\n",[60,2954,2955,2957],{"class":62,"line":201},[60,2956,2754],{"class":337},[60,2958,380],{"class":168},[60,2960,2961,2963,2965],{"class":62,"line":207},[60,2962,2546],{"class":337},[60,2964,341],{"class":168},[60,2966,2967],{"class":70}," node_exporter\n",[60,2969,2970,2973,2975],{"class":62,"line":214},[60,2971,2972],{"class":337},"    system",[60,2974,341],{"class":168},[60,2976,409],{"class":364},[60,2978,2979,2982,2984],{"class":62,"line":220},[60,2980,2981],{"class":337},"    shell",[60,2983,341],{"class":168},[60,2985,2986],{"class":70}," /bin/false\n",[60,2988,2989,2992,2994],{"class":62,"line":226},[60,2990,2991],{"class":337},"    create_home",[60,2993,341],{"class":168},[60,2995,2879],{"class":364},[60,2997,2998],{"class":62,"line":231},[60,2999,211],{"emptyLinePlaceholder":210},[60,3001,3002,3004,3006,3008],{"class":62,"line":237},[60,3003,334],{"class":168},[60,3005,338],{"class":337},[60,3007,341],{"class":168},[60,3009,3010],{"class":70}," Descargar node_exporter\n",[60,3012,3013,3016],{"class":62,"line":243},[60,3014,3015],{"class":337},"  get_url",[60,3017,380],{"class":168},[60,3019,3020,3022,3024,3026,3029],{"class":62,"line":248},[60,3021,2605],{"class":337},[60,3023,341],{"class":168},[60,3025,120],{"class":119},[60,3027,3028],{"class":70},"https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/node_exporter-{{ node_exporter_version }}.linux-amd64.tar.gz",[60,3030,126],{"class":119},[60,3032,3033,3036,3038],{"class":62,"line":254},[60,3034,3035],{"class":337},"    dest",[60,3037,341],{"class":168},[60,3039,3040],{"class":70}," /tmp/node_exporter.tar.gz\n",[60,3042,3043],{"class":62,"line":260},[60,3044,211],{"emptyLinePlaceholder":210},[60,3046,3047,3049,3051,3053],{"class":62,"line":444},[60,3048,334],{"class":168},[60,3050,338],{"class":337},[60,3052,341],{"class":168},[60,3054,3055],{"class":70}," Extraer binario\n",[60,3057,3058,3061],{"class":62,"line":455},[60,3059,3060],{"class":337},"  unarchive",[60,3062,380],{"class":168},[60,3064,3065,3068,3070],{"class":62,"line":466},[60,3066,3067],{"class":337},"    src",[60,3069,341],{"class":168},[60,3071,3040],{"class":70},[60,3073,3074,3076,3078],{"class":62,"line":471},[60,3075,3035],{"class":337},[60,3077,341],{"class":168},[60,3079,3080],{"class":70}," /tmp/\n",[60,3082,3083,3086,3088],{"class":62,"line":483},[60,3084,3085],{"class":337},"    remote_src",[60,3087,341],{"class":168},[60,3089,409],{"class":364},[60,3091,3092],{"class":62,"line":490},[60,3093,211],{"emptyLinePlaceholder":210},[60,3095,3096,3098,3100,3102],{"class":62,"line":498},[60,3097,334],{"class":168},[60,3099,338],{"class":337},[60,3101,341],{"class":168},[60,3103,3104],{"class":70}," Instalar binario\n",[60,3106,3107,3110],{"class":62,"line":507},[60,3108,3109],{"class":337},"  copy",[60,3111,380],{"class":168},[60,3113,3114,3116,3118,3120,3123],{"class":62,"line":515},[60,3115,3067],{"class":337},[60,3117,341],{"class":168},[60,3119,120],{"class":119},[60,3121,3122],{"class":70},"/tmp/node_exporter-{{ node_exporter_version }}.linux-amd64/node_exporter",[60,3124,126],{"class":119},[60,3126,3127,3129,3131],{"class":62,"line":523},[60,3128,3035],{"class":337},[60,3130,341],{"class":168},[60,3132,3133],{"class":70}," /usr/local/bin/node_exporter\n",[60,3135,3136,3139,3141,3143,3146],{"class":62,"line":531},[60,3137,3138],{"class":337},"    mode",[60,3140,341],{"class":168},[60,3142,954],{"class":119},[60,3144,3145],{"class":70},"0755",[60,3147,1550],{"class":119},[60,3149,3150,3152,3154],{"class":62,"line":539},[60,3151,3085],{"class":337},[60,3153,341],{"class":168},[60,3155,409],{"class":364},[60,3157,3158],{"class":62,"line":547},[60,3159,211],{"emptyLinePlaceholder":210},[60,3161,3162,3164,3166,3168],{"class":62,"line":555},[60,3163,334],{"class":168},[60,3165,338],{"class":337},[60,3167,341],{"class":168},[60,3169,3170],{"class":70}," Crear servicio systemd\n",[60,3172,3173,3176],{"class":62,"line":563},[60,3174,3175],{"class":337},"  template",[60,3177,380],{"class":168},[60,3179,3180,3182,3184],{"class":62,"line":571},[60,3181,3067],{"class":337},[60,3183,341],{"class":168},[60,3185,3186],{"class":70}," node_exporter.service.j2\n",[60,3188,3189,3191,3193],{"class":62,"line":582},[60,3190,3035],{"class":337},[60,3192,341],{"class":168},[60,3194,3195],{"class":70}," /etc/systemd/system/node_exporter.service\n",[60,3197,3198,3201,3203],{"class":62,"line":587},[60,3199,3200],{"class":337},"  notify",[60,3202,341],{"class":168},[60,3204,3205],{"class":70}," Reiniciar node_exporter\n",[60,3207,3208],{"class":62,"line":599},[60,3209,211],{"emptyLinePlaceholder":210},[60,3211,3212,3214,3216,3218],{"class":62,"line":607},[60,3213,334],{"class":168},[60,3215,338],{"class":337},[60,3217,341],{"class":168},[60,3219,3220],{"class":70}," Habilitar y arrancar\n",[60,3222,3223,3225],{"class":62,"line":617},[60,3224,2807],{"class":337},[60,3226,380],{"class":168},[60,3228,3229,3231,3233],{"class":62,"line":622},[60,3230,2546],{"class":337},[60,3232,341],{"class":168},[60,3234,2967],{"class":70},[60,3236,3237,3239,3241],{"class":62,"line":634},[60,3238,2574],{"class":337},[60,3240,341],{"class":168},[60,3242,658],{"class":70},[60,3244,3245,3247,3249],{"class":62,"line":642},[60,3246,2830],{"class":337},[60,3248,341],{"class":168},[60,3250,409],{"class":364},[60,3252,3253,3256,3258],{"class":62,"line":651},[60,3254,3255],{"class":337},"    daemon_reload",[60,3257,341],{"class":168},[60,3259,409],{"class":364},[60,3261,3262],{"class":62,"line":661},[60,3263,211],{"emptyLinePlaceholder":210},[60,3265,3266,3268,3270,3272],{"class":62,"line":671},[60,3267,334],{"class":168},[60,3269,338],{"class":337},[60,3271,341],{"class":168},[60,3273,3274],{"class":70}," Abrir puerto en firewall\n",[60,3276,3277,3280],{"class":62,"line":676},[60,3278,3279],{"class":337},"  ufw",[60,3281,380],{"class":168},[60,3283,3284,3287,3289],{"class":62,"line":688},[60,3285,3286],{"class":337},"    rule",[60,3288,341],{"class":168},[60,3290,3291],{"class":70}," allow\n",[60,3293,3294,3297,3299,3301,3304],{"class":62,"line":696},[60,3295,3296],{"class":337},"    from_ip",[60,3298,341],{"class":168},[60,3300,120],{"class":119},[60,3302,3303],{"class":70},"{{ prometheus_server_ip }}",[60,3305,126],{"class":119},[60,3307,3308,3311,3313,3315,3318],{"class":62,"line":1336},[60,3309,3310],{"class":337},"    port",[60,3312,341],{"class":168},[60,3314,954],{"class":119},[60,3316,3317],{"class":70},"9100",[60,3319,1550],{"class":119},[60,3321,3322,3325,3327],{"class":62,"line":1348},[60,3323,3324],{"class":337},"    proto",[60,3326,341],{"class":168},[60,3328,1389],{"class":70},[60,3330,3331,3334,3336,3338,3341],{"class":62,"line":1355},[60,3332,3333],{"class":337},"    comment",[60,3335,341],{"class":168},[60,3337,120],{"class":119},[60,3339,3340],{"class":70},"Prometheus scraping",[60,3342,126],{"class":119},[303,3344],{},[25,3346,3348],{"id":3347},"comandos-ad-hoc","Comandos ad-hoc",[12,3350,3351],{},"No todo necesita un playbook. Ansible puede ejecutar comandos rápidos en todos tus servidores:",[50,3353,3355],{"className":52,"code":3354,"language":54,"meta":55,"style":55},"# Ver espacio en disco de todos los servidores\nansible all -i inventory.ini -a \"df -h /\"\n\n# Reiniciar un servicio en todos los webservers\nansible webservers -i inventory.ini -b -m systemd -a \"name=nginx state=restarted\"\n\n# Ver uptime de todos\nansible all -i inventory.ini -a \"uptime\"\n\n# Copiar un archivo a todos los servidores\nansible all -i inventory.ini -b -m copy -a \"src=./mi-config.conf dest=/etc/mi-config.conf mode=0644\"\n\n# Verificar qué paquetes se pueden actualizar\nansible all -i inventory.ini -b -a \"apt list --upgradable\"\n",[57,3356,3357,3362,3382,3386,3391,3419,3423,3428,3447,3451,3456,3482,3486,3491],{"__ignoreMap":55},[60,3358,3359],{"class":62,"line":63},[60,3360,3361],{"class":323},"# Ver espacio en disco de todos los servidores\n",[60,3363,3364,3366,3368,3370,3372,3375,3377,3380],{"class":62,"line":77},[60,3365,97],{"class":66},[60,3367,278],{"class":70},[60,3369,281],{"class":90},[60,3371,284],{"class":70},[60,3373,3374],{"class":90}," -a",[60,3376,120],{"class":119},[60,3378,3379],{"class":70},"df -h /",[60,3381,126],{"class":119},[60,3383,3384],{"class":62,"line":94},[60,3385,211],{"emptyLinePlaceholder":210},[60,3387,3388],{"class":62,"line":201},[60,3389,3390],{"class":323},"# Reiniciar un servicio en todos los webservers\n",[60,3392,3393,3395,3398,3400,3402,3405,3407,3410,3412,3414,3417],{"class":62,"line":207},[60,3394,97],{"class":66},[60,3396,3397],{"class":70}," webservers",[60,3399,281],{"class":90},[60,3401,284],{"class":70},[60,3403,3404],{"class":90}," -b",[60,3406,287],{"class":90},[60,3408,3409],{"class":70}," systemd",[60,3411,3374],{"class":90},[60,3413,120],{"class":119},[60,3415,3416],{"class":70},"name=nginx state=restarted",[60,3418,126],{"class":119},[60,3420,3421],{"class":62,"line":214},[60,3422,211],{"emptyLinePlaceholder":210},[60,3424,3425],{"class":62,"line":220},[60,3426,3427],{"class":323},"# Ver uptime de todos\n",[60,3429,3430,3432,3434,3436,3438,3440,3442,3445],{"class":62,"line":226},[60,3431,97],{"class":66},[60,3433,278],{"class":70},[60,3435,281],{"class":90},[60,3437,284],{"class":70},[60,3439,3374],{"class":90},[60,3441,120],{"class":119},[60,3443,3444],{"class":70},"uptime",[60,3446,126],{"class":119},[60,3448,3449],{"class":62,"line":231},[60,3450,211],{"emptyLinePlaceholder":210},[60,3452,3453],{"class":62,"line":237},[60,3454,3455],{"class":323},"# Copiar un archivo a todos los servidores\n",[60,3457,3458,3460,3462,3464,3466,3468,3470,3473,3475,3477,3480],{"class":62,"line":243},[60,3459,97],{"class":66},[60,3461,278],{"class":70},[60,3463,281],{"class":90},[60,3465,284],{"class":70},[60,3467,3404],{"class":90},[60,3469,287],{"class":90},[60,3471,3472],{"class":70}," copy",[60,3474,3374],{"class":90},[60,3476,120],{"class":119},[60,3478,3479],{"class":70},"src=./mi-config.conf dest=/etc/mi-config.conf mode=0644",[60,3481,126],{"class":119},[60,3483,3484],{"class":62,"line":248},[60,3485,211],{"emptyLinePlaceholder":210},[60,3487,3488],{"class":62,"line":254},[60,3489,3490],{"class":323},"# Verificar qué paquetes se pueden actualizar\n",[60,3492,3493,3495,3497,3499,3501,3503,3505,3507,3510],{"class":62,"line":260},[60,3494,97],{"class":66},[60,3496,278],{"class":70},[60,3498,281],{"class":90},[60,3500,284],{"class":70},[60,3502,3404],{"class":90},[60,3504,3374],{"class":90},[60,3506,120],{"class":119},[60,3508,3509],{"class":70},"apt list --upgradable",[60,3511,126],{"class":119},[25,3513,3515],{"id":3514},"buenas-prácticas","Buenas prácticas",[12,3517,3518,3525],{},[45,3519,3520,3521,3524],{},"Usa ",[57,3522,3523],{},"--check"," antes de ejecutar"," — el modo dry-run muestra qué cambiaría sin hacer nada:",[50,3527,3529],{"className":52,"code":3528,"language":54,"meta":55,"style":55},"ansible-playbook -i inventory.ini playbooks/hardening.yml --check --diff\n",[57,3530,3531],{"__ignoreMap":55},[60,3532,3533,3535,3537,3539,3542,3545],{"class":62,"line":63},[60,3534,721],{"class":66},[60,3536,281],{"class":90},[60,3538,284],{"class":70},[60,3540,3541],{"class":70}," playbooks/hardening.yml",[60,3543,3544],{"class":90}," --check",[60,3546,3547],{"class":90}," --diff\n",[12,3549,3550,3553],{},[45,3551,3552],{},"Versiona tus playbooks en git"," — tus configuraciones de infraestructura son código y merecen control de versiones, code review y historial.",[12,3555,3556,3559],{},[45,3557,3558],{},"Usa variables por entorno"," — diferentes configuraciones para desarrollo, staging y producción:",[50,3561,3563],{"className":52,"code":3562,"language":54,"meta":55,"style":55},"ansible-playbook -i inventory_produccion.ini playbooks/site.yml\nansible-playbook -i inventory_staging.ini playbooks/site.yml\n",[57,3564,3565,3576],{"__ignoreMap":55},[60,3566,3567,3569,3571,3574],{"class":62,"line":63},[60,3568,721],{"class":66},[60,3570,281],{"class":90},[60,3572,3573],{"class":70}," inventory_produccion.ini",[60,3575,2448],{"class":70},[60,3577,3578,3580,3582,3585],{"class":62,"line":77},[60,3579,721],{"class":66},[60,3581,281],{"class":90},[60,3583,3584],{"class":70}," inventory_staging.ini",[60,3586,2448],{"class":70},[12,3588,3589,3592],{},[45,3590,3591],{},"Cifra secretos con Ansible Vault"," — contraseñas, llaves y tokens cifrados dentro del repositorio:",[50,3594,3596],{"className":52,"code":3595,"language":54,"meta":55,"style":55},"ansible-vault encrypt vars/secretos.yml\nansible-playbook -i inventory.ini playbooks/site.yml --ask-vault-pass\n",[57,3597,3598,3609],{"__ignoreMap":55},[60,3599,3600,3603,3606],{"class":62,"line":63},[60,3601,3602],{"class":66},"ansible-vault",[60,3604,3605],{"class":70}," encrypt",[60,3607,3608],{"class":70}," vars/secretos.yml\n",[60,3610,3611,3613,3615,3617,3619],{"class":62,"line":77},[60,3612,721],{"class":66},[60,3614,281],{"class":90},[60,3616,284],{"class":70},[60,3618,2468],{"class":70},[60,3620,3621],{"class":90}," --ask-vault-pass\n",[12,3623,3624,3627],{},[45,3625,3626],{},"Prueba en staging primero"," — siempre ejecuta playbooks nuevos en un ambiente de pruebas antes de producción.",[25,3629,3631],{"id":3630},"siguientes-pasos","Siguientes pasos",[12,3633,3634],{},"Con Ansible dominado, puedes escalar tu automatización:",[3636,3637,3638,3645,3658,3667,3673],"ul",{},[3639,3640,3641,3644],"li",{},[45,3642,3643],{},"AWX / Semaphore"," — interfaz web para ejecutar playbooks con historial, permisos y programación",[3639,3646,3647,3654,3655],{},[45,3648,3649,3650],{},"Ansible + ",[19,3651,3653],{"href":3652},"/tecnologias/proxmox","Proxmox"," — aprovisionar VMs automáticamente con el módulo ",[57,3656,3657],{},"community.general.proxmox_kvm",[3639,3659,3660,3666],{},[45,3661,3649,3662],{},[19,3663,3665],{"href":3664},"/blog/cicd-gitlab-guia-practica","CI/CD"," — ejecutar playbooks desde pipelines de GitLab",[3639,3668,3669,3672],{},[45,3670,3671],{},"Molecule"," — testing automatizado de roles de Ansible antes de aplicarlos en producción",[3639,3674,3675,3681],{},[45,3676,3677],{},[19,3678,3680],{"href":3679},"/servicios/consultoria","Consultoría de infraestructura"," — diseñamos tu estrategia de automatización con Ansible adaptada a tu operación",[3683,3684],"call-to-action",{"description":3685,"eyebrow":3686,"icon":3687,"label":3688,"title":3689,"to":3690},"Implementamos Ansible para que cada servidor nuevo de tu infraestructura nazca configurado, seguro y monitoreado — en minutos, no en horas.","Infraestructura como código","i-lucide-workflow","Solicitar evaluación","¿Quieres que tus servidores se configuren solos?","/contacto",[3692,3693,3694],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":55,"searchDepth":77,"depth":94,"links":3696},[3697,3698,3699,3700,3701,3702,3703,3704,3705,3706,3707],{"id":27,"depth":77,"text":28},{"id":39,"depth":77,"text":40},{"id":145,"depth":77,"text":146},{"id":307,"depth":77,"text":308},{"id":749,"depth":77,"text":750},{"id":2261,"depth":77,"text":2262},{"id":2500,"depth":77,"text":2501},{"id":2918,"depth":77,"text":2919},{"id":3347,"depth":77,"text":3348},{"id":3514,"depth":77,"text":3515},{"id":3630,"depth":77,"text":3631},"tutorial",{"title":3710,"description":3711,"label":3688,"to":3690,"icon":3687},"¿Necesitas automatizar la gestión de tu infraestructura?","Implementamos Ansible para que la configuración de tus servidores sea reproducible, documentada y ejecutable en minutos.","2026-02-08","Guía paso a paso para instalar Ansible, escribir tu primer playbook y automatizar la configuración de servidores Linux — desde actualización de paquetes hasta hardening completo.",false,"md",[3717,3720,3723,3726],{"question":3718,"answer":3719},"¿Qué diferencia hay entre Ansible y un script bash?","Un script bash ejecuta comandos en secuencia y si algo falla, se detiene o sigue sin control. Ansible es idempotente — puedes ejecutar el mismo playbook 10 veces y el resultado es el mismo; solo aplica los cambios que faltan. Además, Ansible maneja múltiples servidores en paralelo, tiene módulos para casi todo (paquetes, archivos, servicios, usuarios, firewall) y reporta exactamente qué cambió en cada ejecución.",{"question":3721,"answer":3722},"¿Ansible necesita un agente instalado en los servidores?","No. Ansible funciona sin agentes (agentless). Se conecta por SSH a los servidores objetivo y ejecuta los módulos directamente. Solo necesitas Python instalado en los servidores destino (viene preinstalado en Ubuntu y la mayoría de distribuciones). Esto simplifica enormemente la adopción — no hay nada que instalar o mantener en tus servidores.",{"question":3724,"answer":3725},"¿Puedo usar Ansible para configurar servidores Windows?","Sí. Ansible se conecta a Windows por WinRM (no SSH) y tiene módulos específicos para Windows — instalar software, configurar IIS, manejar servicios de Windows, Active Directory, registro y más. La máquina de control (donde corre Ansible) debe ser Linux, pero los targets pueden ser Windows.",{"question":3727,"answer":3728},"¿Ansible sirve para 2-3 servidores o solo para infraestructura grande?","Ansible aporta valor desde el primer servidor. Con 2-3 servidores te ahorra repetir los mismos pasos de configuración manualmente. Con 10+ servidores, el ahorro es enorme. Y lo más importante: tus configuraciones quedan documentadas como código — si un servidor muere, lo recreas con un comando en lugar de recordar todo lo que le hiciste a mano.","/images/blog/ansible-automatizacion.jpg","Terminal mostrando ejecución de playbook Ansible configurando múltiples servidores simultáneamente",{},"/blog/tutorial/automatizar-ansible",{"title":5,"description":3713},"blog/tutorial/automatizar-ansible",[97,3736,3737,3738,3739,3708],"automatizacion","devops","linux","infraestructura","Ixam8Xjs0l9wk6fGGr2qc8eFUi_Pt1s18oRTt7ETDKM",{"path":3742,"title":3743},"/blog/tutorial/monitoreo-grafana-prometheus","Monitoreo de servidores con Grafana y Prometheus desde cero",{"path":3745,"title":3746},"/blog/tutorial/backup-automatizado-python-cron","Backup automatizado con Python y cron en servidores Linux",[3748,3755,3762],{"path":3749,"title":3750,"description":3751,"date":3752,"category":3708,"image":3753,"imageAlt":3754,"readingTime":248},"/blog/tutorial/apis-rest-python-fastapi","Crear APIs REST con Python y FastAPI para integraciones empresariales","Guía paso a paso para construir una API REST profesional con Python y FastAPI que conecte tu ERP, CRM o cualquier sistema con validación, autenticación y documentación automática.","2026-03-04","/images/blog/fastapi-api-rest.jpg","Editor de código mostrando una API FastAPI con documentación Swagger generada automáticamente",{"path":3756,"title":3757,"description":3758,"date":3759,"category":3708,"image":3760,"imageAlt":3761,"readingTime":226},"/blog/tutorial/configurar-firewall-ufw-linux","Configurar firewall en Linux con UFW — reglas esenciales","Guía paso a paso para configurar UFW (Uncomplicated Firewall) en Ubuntu y Debian con las reglas esenciales para proteger servidores de producción.","2026-03-01","/images/blog/ufw-firewall-linux.jpg","Terminal de Linux mostrando reglas de firewall UFW activas protegiendo un servidor de producción",{"path":3763,"title":3764,"description":3765,"date":3766,"category":3708,"image":3767,"imageAlt":3768,"readingTime":237},"/blog/tutorial/traefik-reverse-proxy-docker","Configurar Traefik como reverse proxy para contenedores Docker","Guía paso a paso para instalar Traefik como reverse proxy con descubrimiento automático de contenedores Docker, SSL con Let's Encrypt y dashboard de monitoreo.","2026-02-28","/images/blog/traefik-docker.jpg","Dashboard de Traefik mostrando rutas automáticas hacia múltiples contenedores Docker con SSL activo"]