[{"data":1,"prerenderedAt":2391},["ShallowReactive",2],{"blog-post-/practical-ansible":3},{"id":4,"title":5,"body":6,"date":2378,"description":2379,"draft":2380,"extension":2381,"image":2382,"meta":2383,"navigation":174,"path":2384,"seo":2385,"stem":2386,"tags":2387,"__hash__":2390},"content_en/17.practical-ansible.md","Practical ansible",{"type":7,"value":8,"toc":2369},"minimark",[9,13,32,35,40,43,305,313,320,415,426,433,436,440,443,454,513,538,545,549,556,562,569,628,635,640,644,655,664,667,686,699,702,709,788,806,814,824,869,873,882,885,892,895,898,903,1505,1516,1521,1525,1528,1541,1544,1547,1550,1599,1606,1611,1622,2312,2321,2324,2327,2353,2358,2362,2365],[10,11,12],"p",{},"For getting the taste of Ansible, Here i try to write some Ansible playbooks for common security tasks. Here are the things that i think achievable:",[14,15,16,20,23,26,29],"ol",{},[17,18,19],"li",{},"Copy our ssh public key to all the remote servers, so we can connect to all servers without using password login",[17,21,22],{},"security updates",[17,24,25],{},"disabling password login",[17,27,28],{},"Firewall configuration",[17,30,31],{},"Changing the ssh port from 22 to something else. humans are smart enough to find that, we're doing this only for getting rid of some robots that are wandering the internet for finding insecure servers.(I know, some robots are also not that dumb but anyway)",[10,33,34],{},"Let's Go!",[36,37,39],"h2",{"id":38},"base-templete","Base templete",[10,41,42],{},"Our playbook can be a single file like this:",[44,45,50],"pre",{"className":46,"code":47,"language":48,"meta":49,"style":49},"language-yaml shiki shiki-themes github-dark","- name: General Linux security hardening\n  hosts: all\n  become: true\n  gather_facts: true\n  remote_user: userblahblah\n  tasks:\n  - name: Ensure apache is at the latest version\n    ansible.builtin.yum:\n      name: httpd\n      state: latest\n\n  - name: Write the apache config file\n    ansible.builtin.template:\n      src: /srv/httpd.j2\n      dest: /etc/httpd.conf\n\n  - name: Ensure postgresql is at the latest version\n    ansible.builtin.yum:\n      name: postgresql\n      state: latest\n\n  - name: Ensure that postgresql is started\n    ansible.builtin.service:\n      name: postgresql\n      state: started\n","yaml","",[51,52,53,73,84,96,106,117,126,139,147,158,169,176,188,196,207,218,223,235,242,252,261,266,278,286,295],"code",{"__ignoreMap":49},[54,55,58,62,66,69],"span",{"class":56,"line":57},"line",1,[54,59,61],{"class":60},"s95oV","- ",[54,63,65],{"class":64},"s4JwU","name",[54,67,68],{"class":60},": ",[54,70,72],{"class":71},"sU2Wk","General Linux security hardening\n",[54,74,76,79,81],{"class":56,"line":75},2,[54,77,78],{"class":64},"  hosts",[54,80,68],{"class":60},[54,82,83],{"class":71},"all\n",[54,85,87,90,92],{"class":56,"line":86},3,[54,88,89],{"class":64},"  become",[54,91,68],{"class":60},[54,93,95],{"class":94},"sDLfK","true\n",[54,97,99,102,104],{"class":56,"line":98},4,[54,100,101],{"class":64},"  gather_facts",[54,103,68],{"class":60},[54,105,95],{"class":94},[54,107,109,112,114],{"class":56,"line":108},5,[54,110,111],{"class":64},"  remote_user",[54,113,68],{"class":60},[54,115,116],{"class":71},"userblahblah\n",[54,118,120,123],{"class":56,"line":119},6,[54,121,122],{"class":64},"  tasks",[54,124,125],{"class":60},":\n",[54,127,129,132,134,136],{"class":56,"line":128},7,[54,130,131],{"class":60},"  - ",[54,133,65],{"class":64},[54,135,68],{"class":60},[54,137,138],{"class":71},"Ensure apache is at the latest version\n",[54,140,142,145],{"class":56,"line":141},8,[54,143,144],{"class":64},"    ansible.builtin.yum",[54,146,125],{"class":60},[54,148,150,153,155],{"class":56,"line":149},9,[54,151,152],{"class":64},"      name",[54,154,68],{"class":60},[54,156,157],{"class":71},"httpd\n",[54,159,161,164,166],{"class":56,"line":160},10,[54,162,163],{"class":64},"      state",[54,165,68],{"class":60},[54,167,168],{"class":71},"latest\n",[54,170,172],{"class":56,"line":171},11,[54,173,175],{"emptyLinePlaceholder":174},true,"\n",[54,177,179,181,183,185],{"class":56,"line":178},12,[54,180,131],{"class":60},[54,182,65],{"class":64},[54,184,68],{"class":60},[54,186,187],{"class":71},"Write the apache config file\n",[54,189,191,194],{"class":56,"line":190},13,[54,192,193],{"class":64},"    ansible.builtin.template",[54,195,125],{"class":60},[54,197,199,202,204],{"class":56,"line":198},14,[54,200,201],{"class":64},"      src",[54,203,68],{"class":60},[54,205,206],{"class":71},"/srv/httpd.j2\n",[54,208,210,213,215],{"class":56,"line":209},15,[54,211,212],{"class":64},"      dest",[54,214,68],{"class":60},[54,216,217],{"class":71},"/etc/httpd.conf\n",[54,219,221],{"class":56,"line":220},16,[54,222,175],{"emptyLinePlaceholder":174},[54,224,226,228,230,232],{"class":56,"line":225},17,[54,227,131],{"class":60},[54,229,65],{"class":64},[54,231,68],{"class":60},[54,233,234],{"class":71},"Ensure postgresql is at the latest version\n",[54,236,238,240],{"class":56,"line":237},18,[54,239,144],{"class":64},[54,241,125],{"class":60},[54,243,245,247,249],{"class":56,"line":244},19,[54,246,152],{"class":64},[54,248,68],{"class":60},[54,250,251],{"class":71},"postgresql\n",[54,253,255,257,259],{"class":56,"line":254},20,[54,256,163],{"class":64},[54,258,68],{"class":60},[54,260,168],{"class":71},[54,262,264],{"class":56,"line":263},21,[54,265,175],{"emptyLinePlaceholder":174},[54,267,269,271,273,275],{"class":56,"line":268},22,[54,270,131],{"class":60},[54,272,65],{"class":64},[54,274,68],{"class":60},[54,276,277],{"class":71},"Ensure that postgresql is started\n",[54,279,281,284],{"class":56,"line":280},23,[54,282,283],{"class":64},"    ansible.builtin.service",[54,285,125],{"class":60},[54,287,289,291,293],{"class":56,"line":288},24,[54,290,152],{"class":64},[54,292,68],{"class":60},[54,294,251],{"class":71},[54,296,298,300,302],{"class":56,"line":297},25,[54,299,163],{"class":64},[54,301,68],{"class":60},[54,303,304],{"class":71},"started\n",[10,306,307,308,312],{},"But, without structuring your ",[309,310,311],"strong",{},"ansible tasks"," into seperate files, you will end up with long playbooks which is hard to maintain.",[10,314,315,316,319],{},"So you will be better off using ",[51,317,318],{},"import_tasks"," in tasks section:",[44,321,323],{"className":46,"code":322,"language":48,"meta":49,"style":49},"- name: General Linux security hardening\n  hosts: all\n  become: true\n  gather_facts: true\n  remote_user: userblahblah\n  tasks:\n    -  import_tasks: tasks/authorized_keys.yml\n    -  import_tasks: tasks/disable_root_login.yml\n  handlers:\n    - import_tasks: handler/restart_ssh.yml\n",[51,324,325,335,343,351,359,367,373,385,396,403],{"__ignoreMap":49},[54,326,327,329,331,333],{"class":56,"line":57},[54,328,61],{"class":60},[54,330,65],{"class":64},[54,332,68],{"class":60},[54,334,72],{"class":71},[54,336,337,339,341],{"class":56,"line":75},[54,338,78],{"class":64},[54,340,68],{"class":60},[54,342,83],{"class":71},[54,344,345,347,349],{"class":56,"line":86},[54,346,89],{"class":64},[54,348,68],{"class":60},[54,350,95],{"class":94},[54,352,353,355,357],{"class":56,"line":98},[54,354,101],{"class":64},[54,356,68],{"class":60},[54,358,95],{"class":94},[54,360,361,363,365],{"class":56,"line":108},[54,362,111],{"class":64},[54,364,68],{"class":60},[54,366,116],{"class":71},[54,368,369,371],{"class":56,"line":119},[54,370,122],{"class":64},[54,372,125],{"class":60},[54,374,375,378,380,382],{"class":56,"line":128},[54,376,377],{"class":60},"    -  ",[54,379,318],{"class":64},[54,381,68],{"class":60},[54,383,384],{"class":71},"tasks/authorized_keys.yml\n",[54,386,387,389,391,393],{"class":56,"line":141},[54,388,377],{"class":60},[54,390,318],{"class":64},[54,392,68],{"class":60},[54,394,395],{"class":71},"tasks/disable_root_login.yml\n",[54,397,398,401],{"class":56,"line":149},[54,399,400],{"class":64},"  handlers",[54,402,125],{"class":60},[54,404,405,408,410,412],{"class":56,"line":160},[54,406,407],{"class":60},"    - ",[54,409,318],{"class":64},[54,411,68],{"class":60},[54,413,414],{"class":71},"handler/restart_ssh.yml\n",[10,416,417,418,421,422,425],{},"the address ",[51,419,420],{},"tasks/**"," is relative the playbook folder. ",[51,423,424],{},"tasks"," directory is where you define your tasks. (And they can be used in another playbooks as well)",[10,427,428,429,432],{},"You may wonder what is the field ",[51,430,431],{},"handlers"," doing here. Sometimes you want a task to run only when a change is made on a machine. For example, you may want to restart a service if a task updates the configuration of that service, but not if the configuration is unchanged. Ansible uses handlers to address this use case. Handlers are tasks that only run when notified.",[10,434,435],{},"So using this setup, we are going define our tasks in next sections:",[36,437,439],{"id":438},"ssh-public-key","SSH public key",[10,441,442],{},"Before disabling password login and doing anything like this, you need to put your public key in the remote servers so you can login using ssh key authentication method. Otherwise you will be locked out and something bad happens",[10,444,445,446,449,450,453],{},"Because this is so common, it has it's own dedicated simple module named ",[51,447,448],{},"ansible.posix.authorized_key",". Let's create the file ",[51,451,452],{},"tasks/authorized_key",":",[44,455,457],{"className":46,"code":456,"language":48,"meta":49,"style":49},"---\n- name: Copy local public key to destination server\n  ansible.posix.authorized_key:\n    user: tommy\n    state: present\n    key: \"{{ lookup('file', lookup('env', 'HOME') + '/.ssh/id_ed25519.pub') }}\"\n\n",[51,458,459,465,476,483,493,503],{"__ignoreMap":49},[54,460,461],{"class":56,"line":57},[54,462,464],{"class":463},"svObZ","---\n",[54,466,467,469,471,473],{"class":56,"line":75},[54,468,61],{"class":60},[54,470,65],{"class":64},[54,472,68],{"class":60},[54,474,475],{"class":71},"Copy local public key to destination server\n",[54,477,478,481],{"class":56,"line":86},[54,479,480],{"class":64},"  ansible.posix.authorized_key",[54,482,125],{"class":60},[54,484,485,488,490],{"class":56,"line":98},[54,486,487],{"class":64},"    user",[54,489,68],{"class":60},[54,491,492],{"class":71},"tommy\n",[54,494,495,498,500],{"class":56,"line":108},[54,496,497],{"class":64},"    state",[54,499,68],{"class":60},[54,501,502],{"class":71},"present\n",[54,504,505,508,510],{"class":56,"line":119},[54,506,507],{"class":64},"    key",[54,509,68],{"class":60},[54,511,512],{"class":71},"\"{{ lookup('file', lookup('env', 'HOME') + '/.ssh/id_ed25519.pub') }}\"\n",[10,514,515,516,518,519,522,523,526,527,529,530,533,534,537],{},"we use the module ",[51,517,448],{}," to move our public key to remote server. fields are quite self explanatory. in the ",[51,520,521],{},"key"," field, you need to put your public key string. but instead you usually use the ",[51,524,525],{},"lookup"," plugin to use external text files as a value. in the ",[51,528,525],{}," function call, we are trying to access our host's home directory using ",[51,531,532],{},"$HOME"," environment variable. and after that, we just concatenate that with ",[51,535,536],{},"/.ssh/id_ed25519.pub"," .",[10,539,540,541,544],{},"Define this task to your main playbook and run the playbook using the command ",[51,542,543],{},"ansible-playbook"," make sure it's doing it's job currectly. (Normally if the key is present in remote server, nothing should happen)",[36,546,548],{"id":547},"security-update","Security update",[10,550,551,552,555],{},"Writing a task for automatically updating the security patches through ",[51,553,554],{},"aptitude"," would be really helpful and time saving.",[557,558,559],"info",{},[10,560,561],{},"Writing ansible tasks for this purpose is fine, but there is also another option for applying security patches which is using the apt package named unattended upgrade. It fits better to the aptitude package manager system. And also you don't need to do anything manually.",[10,563,564,565,568],{},"Create the file ",[51,566,567],{},"tasks/apt_security.yml"," :",[44,570,572],{"className":46,"code":571,"language":48,"meta":49,"style":49},"- name: Update all packages to their latest version\n  ansible.builtin.apt:\n    name: \"*\"\n    state: latest\n    update_cache: true\n    only_upgrade: true\n\n",[51,573,574,585,592,602,610,619],{"__ignoreMap":49},[54,575,576,578,580,582],{"class":56,"line":57},[54,577,61],{"class":60},[54,579,65],{"class":64},[54,581,68],{"class":60},[54,583,584],{"class":71},"Update all packages to their latest version\n",[54,586,587,590],{"class":56,"line":75},[54,588,589],{"class":64},"  ansible.builtin.apt",[54,591,125],{"class":60},[54,593,594,597,599],{"class":56,"line":86},[54,595,596],{"class":64},"    name",[54,598,68],{"class":60},[54,600,601],{"class":71},"\"*\"\n",[54,603,604,606,608],{"class":56,"line":98},[54,605,497],{"class":64},[54,607,68],{"class":60},[54,609,168],{"class":71},[54,611,612,615,617],{"class":56,"line":108},[54,613,614],{"class":64},"    update_cache",[54,616,68],{"class":60},[54,618,95],{"class":94},[54,620,621,624,626],{"class":56,"line":119},[54,622,623],{"class":64},"    only_upgrade",[54,625,68],{"class":60},[54,627,95],{"class":94},[10,629,630,631,634],{},"We don't need to be too specific here. We just put the star as a package name and all packages will be upgraded to the latest patch version. The ",[51,632,633],{},"update_cache"," field will make sure the cache is updated before upgrade process.",[557,636,637],{},[10,638,639],{},"It's needless to say, we are using Ubuntu LTS and the package major versions are frozen in the entire support time-span (for 5 years).",[36,641,643],{"id":642},"disable-root-login","Disable root login",[10,645,646,647,650,651,654],{},"Servers that are reachable from the public internet, needs to be reinforced for dealing with bad actors such as ",[309,648,649],{},"hackers"," and ",[309,652,653],{},"automated robots",".",[557,656,657],{},[10,658,659,660,663],{},"Check your ",[51,661,662],{},"auth.log"," file for any suspicious activity.",[10,665,666],{},"You have some common options to configure:",[668,669,670,677,680],"ul",{},[17,671,672,673,676],{},"Disabling the ssh ",[51,674,675],{},"root"," Login",[17,678,679],{},"Disabling the ssh password-login completely",[17,681,682,683],{},"Configuring something like ",[51,684,685],{},"fail2ban",[557,687,688],{},[10,689,690,691,694,695,698],{},"All approaches for human authentication rely on: ",[309,692,693],{},"Something You Know, Have, or Are."," But the thing is, knowing something good enough like complex password is not feasible for most of us. Especially when you are dealing with multiple machines. So you better to leverage something that you can ",[309,696,697],{},"have",". Something like ssh-keys. Even better, combine it with optional ssh password that is easy to remember.",[10,700,701],{},"Here we just define the first one. It's probably the easiest and most straight-forward things to do.",[10,703,704,705,708],{},"The built-in module ",[51,706,707],{},"ansible.builtin.lineinfile"," can help us here:",[44,710,712],{"className":46,"code":711,"language":48,"meta":49,"style":49},"---\n- name: Disabling the root login of ssh daemon\n  ansible.builtin.lineinfile:\n    path: /etc/ssh/sshd_config\n    state: present\n    regex: '^#?PermitRootLogin\\s+.*'\n    line: 'PermitRootLogin no'\n  notify:\n    - ssh daemon reload\n",[51,713,714,718,729,736,746,754,764,774,781],{"__ignoreMap":49},[54,715,716],{"class":56,"line":57},[54,717,464],{"class":463},[54,719,720,722,724,726],{"class":56,"line":75},[54,721,61],{"class":60},[54,723,65],{"class":64},[54,725,68],{"class":60},[54,727,728],{"class":71},"Disabling the root login of ssh daemon\n",[54,730,731,734],{"class":56,"line":86},[54,732,733],{"class":64},"  ansible.builtin.lineinfile",[54,735,125],{"class":60},[54,737,738,741,743],{"class":56,"line":98},[54,739,740],{"class":64},"    path",[54,742,68],{"class":60},[54,744,745],{"class":71},"/etc/ssh/sshd_config\n",[54,747,748,750,752],{"class":56,"line":108},[54,749,497],{"class":64},[54,751,68],{"class":60},[54,753,502],{"class":71},[54,755,756,759,761],{"class":56,"line":119},[54,757,758],{"class":64},"    regex",[54,760,68],{"class":60},[54,762,763],{"class":71},"'^#?PermitRootLogin\\s+.*'\n",[54,765,766,769,771],{"class":56,"line":128},[54,767,768],{"class":64},"    line",[54,770,68],{"class":60},[54,772,773],{"class":71},"'PermitRootLogin no'\n",[54,775,776,779],{"class":56,"line":141},[54,777,778],{"class":64},"  notify",[54,780,125],{"class":60},[54,782,783,785],{"class":56,"line":149},[54,784,407],{"class":60},[54,786,787],{"class":71},"ssh daemon reload\n",[10,789,790,791,794,795,798,799,802,803,805],{},"The module ",[51,792,793],{},"lineinfile"," need the ",[51,796,797],{},"path"," of the file, ",[51,800,801],{},"regex"," to match and the ",[51,804,56],{}," to replace into the matched regex.",[557,807,808],{},[10,809,810,811,813],{},"When modifying a line, the regex should typically match both the initial state of the line as well as its state after replacement by ",[51,812,56],{}," to ensure idempotence.",[10,815,816,817,820,821,568],{},"At the end, we notify the ",[51,818,819],{},"ssh daemon reload"," to apply the new configuration. Here is the handler in ",[51,822,823],{},"handler/restart_ssh.yml",[44,825,827],{"className":46,"code":826,"language":48,"meta":49,"style":49},"---\n- name: restart ssh service\n  ansible.builtin.service:\n    name: ssh.socket\n    state: restarted\n",[51,828,829,833,844,851,860],{"__ignoreMap":49},[54,830,831],{"class":56,"line":57},[54,832,464],{"class":463},[54,834,835,837,839,841],{"class":56,"line":75},[54,836,61],{"class":60},[54,838,65],{"class":64},[54,840,68],{"class":60},[54,842,843],{"class":71},"restart ssh service\n",[54,845,846,849],{"class":56,"line":86},[54,847,848],{"class":64},"  ansible.builtin.service",[54,850,125],{"class":60},[54,852,853,855,857],{"class":56,"line":98},[54,854,596],{"class":64},[54,856,68],{"class":60},[54,858,859],{"class":71},"ssh.socket\n",[54,861,862,864,866],{"class":56,"line":108},[54,863,497],{"class":64},[54,865,68],{"class":60},[54,867,868],{"class":71},"restarted\n",[36,870,872],{"id":871},"firewall","Firewall",[557,874,875],{},[10,876,877,878,881],{},"Don't do anything rush about iptables otherwise you will lock-out yourself from connecting to your server. Try to test these using tools like ",[51,879,880],{},"iptables-apply"," to see if they work as expected.",[10,883,884],{},"You better to be strict about what packets are free to enter you server.",[10,886,887,888,891],{},"The common sense about the firewall strategies states that, ",[309,889,890],{},"you should allow only the packets that you are need and trust",". You can’t have serious security if you use a default policy of ACCEPT and continuously insert rules to drop packets from sources that start to send bad stuff. You must allow only the packets that you trust, and deny everything else.",[10,893,894],{},"So, the firewalls are little more specific because it depends on the applications that you are running and hosting on your server.",[10,896,897],{},"Here I write some general ones in the playbook:",[557,899,900],{},[10,901,902],{},"Iptable is still the dominant firewall in most Linux distros and because of that, We will focus on that here.",[44,904,906],{"className":46,"code":905,"language":48,"meta":49,"style":49},"---\n- name: flush all INPUT chain rules\n  ansible.builtin.iptables:\n    chain: INPUT\n    flush: yes\n\n- name: Iptable accept icmp packets\n  ansible.builtin.iptables:\n    chain: INPUT\n    protocol: icmp\n    jump: ACCEPT\n\n- name: Iptable accept tcp packet that are initiated from server\n  ansible.builtin.iptables:\n    chain: INPUT\n    protocol: tcp\n    syn: negate\n    jump: ACCEPT\n\n- name: Iptable accept packets from localhost\n  ansible.builtin.iptables:\n    chain: INPUT\n    source: 127.0.0.1\n    jump: ACCEPT\n\n- name: Iptable accept packets from local network\n  ansible.builtin.iptables:\n    chain: INPUT\n    source: 10.0.0.0/24\n    jump: ACCEPT\n\n- name: Iptable accept tcp packets on port 22\n  ansible.builtin.iptables:\n    chain: INPUT\n    protocol: tcp\n    destination_ports:\n      - \"22\"\n    jump: ACCEPT\n\n- name: Iptable accept dns packets\n  ansible.builtin.iptables:\n    chain: INPUT\n    protocol: udp\n    source_port: 53\n    jump: ACCEPT\n\n- name: Iptable accept on http port\n  ansible.builtin.iptables:\n    chain: INPUT\n    protocol: tcp\n    destination_port: 80\n    jump: ACCEPT\n\n- name: Iptable accept requests to k3s control node, Only from local network.\n  ansible.builtin.iptables:\n    chain: INPUT\n    source: 10.0.0.0/24\n    protocol: tcp\n    destination_port: 6443\n    jump: ACCEPT\n\n- name: INPUT CHAIN Default Policy Drop\n  ansible.builtin.iptables:\n    chain: INPUT\n    policy: DROP\n\n- name: Save current state of the firewall in file system\n  community.general.iptables_state:\n    state: saved\n    path: /etc/iptables/rules.v4\n",[51,907,908,912,923,930,940,950,954,965,971,979,989,999,1003,1014,1020,1028,1037,1047,1055,1059,1070,1076,1084,1094,1102,1106,1118,1125,1134,1144,1153,1158,1170,1177,1186,1195,1203,1212,1221,1226,1238,1245,1254,1264,1275,1284,1289,1301,1308,1317,1326,1337,1346,1351,1363,1370,1379,1388,1397,1407,1416,1421,1433,1440,1449,1460,1465,1477,1485,1495],{"__ignoreMap":49},[54,909,910],{"class":56,"line":57},[54,911,464],{"class":463},[54,913,914,916,918,920],{"class":56,"line":75},[54,915,61],{"class":60},[54,917,65],{"class":64},[54,919,68],{"class":60},[54,921,922],{"class":71},"flush all INPUT chain rules\n",[54,924,925,928],{"class":56,"line":86},[54,926,927],{"class":64},"  ansible.builtin.iptables",[54,929,125],{"class":60},[54,931,932,935,937],{"class":56,"line":98},[54,933,934],{"class":64},"    chain",[54,936,68],{"class":60},[54,938,939],{"class":71},"INPUT\n",[54,941,942,945,947],{"class":56,"line":108},[54,943,944],{"class":64},"    flush",[54,946,68],{"class":60},[54,948,949],{"class":94},"yes\n",[54,951,952],{"class":56,"line":119},[54,953,175],{"emptyLinePlaceholder":174},[54,955,956,958,960,962],{"class":56,"line":128},[54,957,61],{"class":60},[54,959,65],{"class":64},[54,961,68],{"class":60},[54,963,964],{"class":71},"Iptable accept icmp packets\n",[54,966,967,969],{"class":56,"line":141},[54,968,927],{"class":64},[54,970,125],{"class":60},[54,972,973,975,977],{"class":56,"line":149},[54,974,934],{"class":64},[54,976,68],{"class":60},[54,978,939],{"class":71},[54,980,981,984,986],{"class":56,"line":160},[54,982,983],{"class":64},"    protocol",[54,985,68],{"class":60},[54,987,988],{"class":71},"icmp\n",[54,990,991,994,996],{"class":56,"line":171},[54,992,993],{"class":64},"    jump",[54,995,68],{"class":60},[54,997,998],{"class":71},"ACCEPT\n",[54,1000,1001],{"class":56,"line":178},[54,1002,175],{"emptyLinePlaceholder":174},[54,1004,1005,1007,1009,1011],{"class":56,"line":190},[54,1006,61],{"class":60},[54,1008,65],{"class":64},[54,1010,68],{"class":60},[54,1012,1013],{"class":71},"Iptable accept tcp packet that are initiated from server\n",[54,1015,1016,1018],{"class":56,"line":198},[54,1017,927],{"class":64},[54,1019,125],{"class":60},[54,1021,1022,1024,1026],{"class":56,"line":209},[54,1023,934],{"class":64},[54,1025,68],{"class":60},[54,1027,939],{"class":71},[54,1029,1030,1032,1034],{"class":56,"line":220},[54,1031,983],{"class":64},[54,1033,68],{"class":60},[54,1035,1036],{"class":71},"tcp\n",[54,1038,1039,1042,1044],{"class":56,"line":225},[54,1040,1041],{"class":64},"    syn",[54,1043,68],{"class":60},[54,1045,1046],{"class":71},"negate\n",[54,1048,1049,1051,1053],{"class":56,"line":237},[54,1050,993],{"class":64},[54,1052,68],{"class":60},[54,1054,998],{"class":71},[54,1056,1057],{"class":56,"line":244},[54,1058,175],{"emptyLinePlaceholder":174},[54,1060,1061,1063,1065,1067],{"class":56,"line":254},[54,1062,61],{"class":60},[54,1064,65],{"class":64},[54,1066,68],{"class":60},[54,1068,1069],{"class":71},"Iptable accept packets from localhost\n",[54,1071,1072,1074],{"class":56,"line":263},[54,1073,927],{"class":64},[54,1075,125],{"class":60},[54,1077,1078,1080,1082],{"class":56,"line":268},[54,1079,934],{"class":64},[54,1081,68],{"class":60},[54,1083,939],{"class":71},[54,1085,1086,1089,1091],{"class":56,"line":280},[54,1087,1088],{"class":64},"    source",[54,1090,68],{"class":60},[54,1092,1093],{"class":94},"127.0.0.1\n",[54,1095,1096,1098,1100],{"class":56,"line":288},[54,1097,993],{"class":64},[54,1099,68],{"class":60},[54,1101,998],{"class":71},[54,1103,1104],{"class":56,"line":297},[54,1105,175],{"emptyLinePlaceholder":174},[54,1107,1109,1111,1113,1115],{"class":56,"line":1108},26,[54,1110,61],{"class":60},[54,1112,65],{"class":64},[54,1114,68],{"class":60},[54,1116,1117],{"class":71},"Iptable accept packets from local network\n",[54,1119,1121,1123],{"class":56,"line":1120},27,[54,1122,927],{"class":64},[54,1124,125],{"class":60},[54,1126,1128,1130,1132],{"class":56,"line":1127},28,[54,1129,934],{"class":64},[54,1131,68],{"class":60},[54,1133,939],{"class":71},[54,1135,1137,1139,1141],{"class":56,"line":1136},29,[54,1138,1088],{"class":64},[54,1140,68],{"class":60},[54,1142,1143],{"class":71},"10.0.0.0/24\n",[54,1145,1147,1149,1151],{"class":56,"line":1146},30,[54,1148,993],{"class":64},[54,1150,68],{"class":60},[54,1152,998],{"class":71},[54,1154,1156],{"class":56,"line":1155},31,[54,1157,175],{"emptyLinePlaceholder":174},[54,1159,1161,1163,1165,1167],{"class":56,"line":1160},32,[54,1162,61],{"class":60},[54,1164,65],{"class":64},[54,1166,68],{"class":60},[54,1168,1169],{"class":71},"Iptable accept tcp packets on port 22\n",[54,1171,1173,1175],{"class":56,"line":1172},33,[54,1174,927],{"class":64},[54,1176,125],{"class":60},[54,1178,1180,1182,1184],{"class":56,"line":1179},34,[54,1181,934],{"class":64},[54,1183,68],{"class":60},[54,1185,939],{"class":71},[54,1187,1189,1191,1193],{"class":56,"line":1188},35,[54,1190,983],{"class":64},[54,1192,68],{"class":60},[54,1194,1036],{"class":71},[54,1196,1198,1201],{"class":56,"line":1197},36,[54,1199,1200],{"class":64},"    destination_ports",[54,1202,125],{"class":60},[54,1204,1206,1209],{"class":56,"line":1205},37,[54,1207,1208],{"class":60},"      - ",[54,1210,1211],{"class":71},"\"22\"\n",[54,1213,1215,1217,1219],{"class":56,"line":1214},38,[54,1216,993],{"class":64},[54,1218,68],{"class":60},[54,1220,998],{"class":71},[54,1222,1224],{"class":56,"line":1223},39,[54,1225,175],{"emptyLinePlaceholder":174},[54,1227,1229,1231,1233,1235],{"class":56,"line":1228},40,[54,1230,61],{"class":60},[54,1232,65],{"class":64},[54,1234,68],{"class":60},[54,1236,1237],{"class":71},"Iptable accept dns packets\n",[54,1239,1241,1243],{"class":56,"line":1240},41,[54,1242,927],{"class":64},[54,1244,125],{"class":60},[54,1246,1248,1250,1252],{"class":56,"line":1247},42,[54,1249,934],{"class":64},[54,1251,68],{"class":60},[54,1253,939],{"class":71},[54,1255,1257,1259,1261],{"class":56,"line":1256},43,[54,1258,983],{"class":64},[54,1260,68],{"class":60},[54,1262,1263],{"class":71},"udp\n",[54,1265,1267,1270,1272],{"class":56,"line":1266},44,[54,1268,1269],{"class":64},"    source_port",[54,1271,68],{"class":60},[54,1273,1274],{"class":94},"53\n",[54,1276,1278,1280,1282],{"class":56,"line":1277},45,[54,1279,993],{"class":64},[54,1281,68],{"class":60},[54,1283,998],{"class":71},[54,1285,1287],{"class":56,"line":1286},46,[54,1288,175],{"emptyLinePlaceholder":174},[54,1290,1292,1294,1296,1298],{"class":56,"line":1291},47,[54,1293,61],{"class":60},[54,1295,65],{"class":64},[54,1297,68],{"class":60},[54,1299,1300],{"class":71},"Iptable accept on http port\n",[54,1302,1304,1306],{"class":56,"line":1303},48,[54,1305,927],{"class":64},[54,1307,125],{"class":60},[54,1309,1311,1313,1315],{"class":56,"line":1310},49,[54,1312,934],{"class":64},[54,1314,68],{"class":60},[54,1316,939],{"class":71},[54,1318,1320,1322,1324],{"class":56,"line":1319},50,[54,1321,983],{"class":64},[54,1323,68],{"class":60},[54,1325,1036],{"class":71},[54,1327,1329,1332,1334],{"class":56,"line":1328},51,[54,1330,1331],{"class":64},"    destination_port",[54,1333,68],{"class":60},[54,1335,1336],{"class":94},"80\n",[54,1338,1340,1342,1344],{"class":56,"line":1339},52,[54,1341,993],{"class":64},[54,1343,68],{"class":60},[54,1345,998],{"class":71},[54,1347,1349],{"class":56,"line":1348},53,[54,1350,175],{"emptyLinePlaceholder":174},[54,1352,1354,1356,1358,1360],{"class":56,"line":1353},54,[54,1355,61],{"class":60},[54,1357,65],{"class":64},[54,1359,68],{"class":60},[54,1361,1362],{"class":71},"Iptable accept requests to k3s control node, Only from local network.\n",[54,1364,1366,1368],{"class":56,"line":1365},55,[54,1367,927],{"class":64},[54,1369,125],{"class":60},[54,1371,1373,1375,1377],{"class":56,"line":1372},56,[54,1374,934],{"class":64},[54,1376,68],{"class":60},[54,1378,939],{"class":71},[54,1380,1382,1384,1386],{"class":56,"line":1381},57,[54,1383,1088],{"class":64},[54,1385,68],{"class":60},[54,1387,1143],{"class":71},[54,1389,1391,1393,1395],{"class":56,"line":1390},58,[54,1392,983],{"class":64},[54,1394,68],{"class":60},[54,1396,1036],{"class":71},[54,1398,1400,1402,1404],{"class":56,"line":1399},59,[54,1401,1331],{"class":64},[54,1403,68],{"class":60},[54,1405,1406],{"class":94},"6443\n",[54,1408,1410,1412,1414],{"class":56,"line":1409},60,[54,1411,993],{"class":64},[54,1413,68],{"class":60},[54,1415,998],{"class":71},[54,1417,1419],{"class":56,"line":1418},61,[54,1420,175],{"emptyLinePlaceholder":174},[54,1422,1424,1426,1428,1430],{"class":56,"line":1423},62,[54,1425,61],{"class":60},[54,1427,65],{"class":64},[54,1429,68],{"class":60},[54,1431,1432],{"class":71},"INPUT CHAIN Default Policy Drop\n",[54,1434,1436,1438],{"class":56,"line":1435},63,[54,1437,927],{"class":64},[54,1439,125],{"class":60},[54,1441,1443,1445,1447],{"class":56,"line":1442},64,[54,1444,934],{"class":64},[54,1446,68],{"class":60},[54,1448,939],{"class":71},[54,1450,1452,1455,1457],{"class":56,"line":1451},65,[54,1453,1454],{"class":64},"    policy",[54,1456,68],{"class":60},[54,1458,1459],{"class":71},"DROP\n",[54,1461,1463],{"class":56,"line":1462},66,[54,1464,175],{"emptyLinePlaceholder":174},[54,1466,1468,1470,1472,1474],{"class":56,"line":1467},67,[54,1469,61],{"class":60},[54,1471,65],{"class":64},[54,1473,68],{"class":60},[54,1475,1476],{"class":71},"Save current state of the firewall in file system\n",[54,1478,1480,1483],{"class":56,"line":1479},68,[54,1481,1482],{"class":64},"  community.general.iptables_state",[54,1484,125],{"class":60},[54,1486,1488,1490,1492],{"class":56,"line":1487},69,[54,1489,497],{"class":64},[54,1491,68],{"class":60},[54,1493,1494],{"class":71},"saved\n",[54,1496,1498,1500,1502],{"class":56,"line":1497},70,[54,1499,740],{"class":64},[54,1501,68],{"class":60},[54,1503,1504],{"class":71},"/etc/iptables/rules.v4\n",[10,1506,1507,1508,1511,1512,1515],{},"Things are quite self-explanatory. But the important thing is, you need to make sure the iptable is saved and preserved after reboot. This can be done via the package ",[51,1509,1510],{},"iptable-persistant"," . You just install the package and after that, the file ",[51,1513,1514],{},"/etc/iptabels/rules.v4"," will be loaded every-time at the boot.",[557,1517,1518],{},[10,1519,1520],{},"UFW can also be used as a more ergonomic front-end to iptables here. Its roles also does not need configuring to be persistent after reboot. It just works!",[36,1522,1524],{"id":1523},"ssh-port","SSH port",[10,1526,1527],{},"This is where Ansible gets complicated.",[10,1529,1530,1531,1533,1534,1537,1538,537],{},"Changing the ssh default port is easy enough. You just use the ",[51,1532,793],{}," and change the ",[51,1535,1536],{},"Port"," parameter of ",[51,1539,1540],{},"/etc/sshd/sshd.config",[10,1542,1543],{},"The issue is that Ansible uses SSH for its connections. How can we change the default SSH port and still maintain Ansible's SSH connection?",[10,1545,1546],{},"The problem is about making the role idempotent without having to fiddle with your inventory file too much.",[10,1548,1549],{},"Consider the following inventory:",[44,1551,1553],{"className":46,"code":1552,"language":48,"meta":49,"style":49},"[homelab]\n10.0.0.30 ansible_port=2222\n10.0.0.31 ansible_port=2222\n10.0.0.32 ansible_port=2222\n\n[cloud]\n188.65.100.54 ansible_port=22\n",[51,1554,1555,1566,1571,1576,1581,1585,1594],{"__ignoreMap":49},[54,1556,1557,1560,1563],{"class":56,"line":57},[54,1558,1559],{"class":60},"[",[54,1561,1562],{"class":71},"homelab",[54,1564,1565],{"class":60},"]\n",[54,1567,1568],{"class":56,"line":75},[54,1569,1570],{"class":71},"10.0.0.30 ansible_port=2222\n",[54,1572,1573],{"class":56,"line":86},[54,1574,1575],{"class":71},"10.0.0.31 ansible_port=2222\n",[54,1577,1578],{"class":56,"line":98},[54,1579,1580],{"class":71},"10.0.0.32 ansible_port=2222\n",[54,1582,1583],{"class":56,"line":108},[54,1584,175],{"emptyLinePlaceholder":174},[54,1586,1587,1589,1592],{"class":56,"line":119},[54,1588,1559],{"class":60},[54,1590,1591],{"class":71},"cloud",[54,1593,1565],{"class":60},[54,1595,1596],{"class":56,"line":128},[54,1597,1598],{"class":71},"188.65.100.54 ansible_port=22\n",[10,1600,1601,1602,1605],{},"Let's say we want to change the ssh port of ",[51,1603,1604],{},"[cloud]"," hosts to 2222. When the playbook done its job, ansible ssh connection will be terminated and we need to change our inventory file manually.",[10,1607,1608],{},[309,1609,1610],{},"Is that even possible to make this idempotent? Or i am just too much perfectionist?",[10,1612,1613,1614,1621],{},"It turn out ",[1615,1616,1620],"a",{"href":1617,"rel":1618},"https://dmsimard.com/2016/03/15/changing-the-ssh-port-with-ansible/",[1619],"nofollow","there is a way"," to handle that:",[44,1623,1625],{"className":46,"code":1624,"language":48,"meta":49,"style":49},"---\n# When we change the ssh port, ansible can not connect to the host anymore.\n# So here we configure something to figure out what is the current port numeber\n# and make changes based on that without manual modification to inventory.\n\n\n# The variable \"configured_port\" is the port that needs to be configured!\n# We make a copy of the variable \"ansible_port\". because changing that directly\n# will mess with the ansible ssh connection.\n\n- name: Set configured port fact\n  set_fact:\n    configured_port: \"{{ ansible_port }}\"\n\n\n\n- name: Check if server is using the default SSH port\n  ansible.builtin.wait_for:\n    port: 22 \n    state: started \n    host: \"{{inventory_hostname}}\"\n    timeout: 4\n    msg: The host is Not reachable on the default 22 port!\n    delay: 0\n  delegate_to: localhost \n  ignore_errors: \"yes\"\n  register: default_ssh\n\n# If the \"default_ssh\" is set from above, continue the tasks with the default port set. \n- name: Set inventory ansible_port to default\n  set_fact:\n    ansible_port: \"22\"\n  when: default_ssh is defined and\n        default_ssh.failed is false \n  register: ssh_port_set\n\n\n\n# Only runs if the \"default_ssh\" is not defined.\n- name: Check if we're using the inventory-provided SSH port\n  ansible.builtin.wait_for:\n    port: \"{{ configured_port }}\"\n    state: \"started\"\n    host: \"{{ inventory_hostname }}\"\n    msg: \"The host is not reachable on the inventory specified port {{ansible_port}}\"\n    timeout: 4\n    delay: 0\n  delegate_to: \"localhost\"\n  ignore_errors: \"yes\"\n  register: configured_ssh\n  when: default_ssh is defined and\n        default_ssh.state is undefined\n\n# If {{ ansible_port }} is reachable, we don't need to do anything special. \n- name: SSH port is configured properly\n  debug:\n    msg: \"SSH port is configured properly\"\n  when: configured_ssh is defined and\n        configured_ssh.state is defined and\n        configured_ssh.state == \"started\"\n  register: ssh_port_set\n\n# At this point, the variable \"ssh_port_set\" should be defined and have some value.\n# if not, it means it's not reachable with neither 22 or \"ansible_port\".\n- name: Fail if SSH port was not auto-detected (unknown)\n  fail:\n    msg: \"The SSH port is neither 22 or {{ ansible_port }}. Check the {{inventory_hostname}} manually!\"\n  when: ssh_port_set is undefined\n\n\n- name: Change SSH listen port to target port\n  ansible.builtin.lineinfile:\n    dest: \"/etc/ssh/sshd_config\"\n    regex: \"^#?Port.*\"\n    line: \"Port {{configured_port}}\"\n  notify:\n    - restart daemon\n    - restart ssh service\n\n\n# You probably need this. otherwise the ssh restart handlers are not executed.\n- name: Flush handlers to apply SSH changes\n  meta: flush_handlers\n\n- name: Ensure we use the configured SSH port for the remainder of the role\n  set_fact:\n    ansible_port: \"{{ configured_port }}\"\n\n# gather facts is better to be disabled during this playbook. because it's needs a ssh connection to the remote server. \n- name: gather facts now, as now the server is ready in the configured port \n  ansible.builtin.setup:\n\n",[51,1626,1627,1631,1637,1642,1647,1651,1655,1660,1665,1670,1674,1685,1692,1702,1706,1710,1714,1725,1732,1745,1756,1766,1776,1786,1796,1808,1818,1828,1832,1837,1848,1854,1863,1873,1880,1889,1893,1897,1901,1906,1917,1923,1932,1941,1950,1959,1967,1975,1984,1992,2001,2009,2014,2018,2023,2034,2041,2050,2059,2064,2069,2077,2081,2086,2091,2102,2109,2118,2127,2131,2135,2147,2154,2165,2175,2185,2192,2200,2207,2212,2217,2223,2235,2246,2251,2263,2270,2279,2284,2290,2304],{"__ignoreMap":49},[54,1628,1629],{"class":56,"line":57},[54,1630,464],{"class":463},[54,1632,1633],{"class":56,"line":75},[54,1634,1636],{"class":1635},"sAwPA","# When we change the ssh port, ansible can not connect to the host anymore.\n",[54,1638,1639],{"class":56,"line":86},[54,1640,1641],{"class":1635},"# So here we configure something to figure out what is the current port numeber\n",[54,1643,1644],{"class":56,"line":98},[54,1645,1646],{"class":1635},"# and make changes based on that without manual modification to inventory.\n",[54,1648,1649],{"class":56,"line":108},[54,1650,175],{"emptyLinePlaceholder":174},[54,1652,1653],{"class":56,"line":119},[54,1654,175],{"emptyLinePlaceholder":174},[54,1656,1657],{"class":56,"line":128},[54,1658,1659],{"class":1635},"# The variable \"configured_port\" is the port that needs to be configured!\n",[54,1661,1662],{"class":56,"line":141},[54,1663,1664],{"class":1635},"# We make a copy of the variable \"ansible_port\". because changing that directly\n",[54,1666,1667],{"class":56,"line":149},[54,1668,1669],{"class":1635},"# will mess with the ansible ssh connection.\n",[54,1671,1672],{"class":56,"line":160},[54,1673,175],{"emptyLinePlaceholder":174},[54,1675,1676,1678,1680,1682],{"class":56,"line":171},[54,1677,61],{"class":60},[54,1679,65],{"class":64},[54,1681,68],{"class":60},[54,1683,1684],{"class":71},"Set configured port fact\n",[54,1686,1687,1690],{"class":56,"line":178},[54,1688,1689],{"class":64},"  set_fact",[54,1691,125],{"class":60},[54,1693,1694,1697,1699],{"class":56,"line":190},[54,1695,1696],{"class":64},"    configured_port",[54,1698,68],{"class":60},[54,1700,1701],{"class":71},"\"{{ ansible_port }}\"\n",[54,1703,1704],{"class":56,"line":198},[54,1705,175],{"emptyLinePlaceholder":174},[54,1707,1708],{"class":56,"line":209},[54,1709,175],{"emptyLinePlaceholder":174},[54,1711,1712],{"class":56,"line":220},[54,1713,175],{"emptyLinePlaceholder":174},[54,1715,1716,1718,1720,1722],{"class":56,"line":225},[54,1717,61],{"class":60},[54,1719,65],{"class":64},[54,1721,68],{"class":60},[54,1723,1724],{"class":71},"Check if server is using the default SSH port\n",[54,1726,1727,1730],{"class":56,"line":237},[54,1728,1729],{"class":64},"  ansible.builtin.wait_for",[54,1731,125],{"class":60},[54,1733,1734,1737,1739,1742],{"class":56,"line":244},[54,1735,1736],{"class":64},"    port",[54,1738,68],{"class":60},[54,1740,1741],{"class":94},"22",[54,1743,1744],{"class":60}," \n",[54,1746,1747,1749,1751,1754],{"class":56,"line":254},[54,1748,497],{"class":64},[54,1750,68],{"class":60},[54,1752,1753],{"class":71},"started",[54,1755,1744],{"class":60},[54,1757,1758,1761,1763],{"class":56,"line":263},[54,1759,1760],{"class":64},"    host",[54,1762,68],{"class":60},[54,1764,1765],{"class":71},"\"{{inventory_hostname}}\"\n",[54,1767,1768,1771,1773],{"class":56,"line":268},[54,1769,1770],{"class":64},"    timeout",[54,1772,68],{"class":60},[54,1774,1775],{"class":94},"4\n",[54,1777,1778,1781,1783],{"class":56,"line":280},[54,1779,1780],{"class":64},"    msg",[54,1782,68],{"class":60},[54,1784,1785],{"class":71},"The host is Not reachable on the default 22 port!\n",[54,1787,1788,1791,1793],{"class":56,"line":288},[54,1789,1790],{"class":64},"    delay",[54,1792,68],{"class":60},[54,1794,1795],{"class":94},"0\n",[54,1797,1798,1801,1803,1806],{"class":56,"line":297},[54,1799,1800],{"class":64},"  delegate_to",[54,1802,68],{"class":60},[54,1804,1805],{"class":71},"localhost",[54,1807,1744],{"class":60},[54,1809,1810,1813,1815],{"class":56,"line":1108},[54,1811,1812],{"class":64},"  ignore_errors",[54,1814,68],{"class":60},[54,1816,1817],{"class":71},"\"yes\"\n",[54,1819,1820,1823,1825],{"class":56,"line":1120},[54,1821,1822],{"class":64},"  register",[54,1824,68],{"class":60},[54,1826,1827],{"class":71},"default_ssh\n",[54,1829,1830],{"class":56,"line":1127},[54,1831,175],{"emptyLinePlaceholder":174},[54,1833,1834],{"class":56,"line":1136},[54,1835,1836],{"class":1635},"# If the \"default_ssh\" is set from above, continue the tasks with the default port set. \n",[54,1838,1839,1841,1843,1845],{"class":56,"line":1146},[54,1840,61],{"class":60},[54,1842,65],{"class":64},[54,1844,68],{"class":60},[54,1846,1847],{"class":71},"Set inventory ansible_port to default\n",[54,1849,1850,1852],{"class":56,"line":1155},[54,1851,1689],{"class":64},[54,1853,125],{"class":60},[54,1855,1856,1859,1861],{"class":56,"line":1160},[54,1857,1858],{"class":64},"    ansible_port",[54,1860,68],{"class":60},[54,1862,1211],{"class":71},[54,1864,1865,1868,1870],{"class":56,"line":1172},[54,1866,1867],{"class":64},"  when",[54,1869,68],{"class":60},[54,1871,1872],{"class":71},"default_ssh is defined and\n",[54,1874,1875,1878],{"class":56,"line":1179},[54,1876,1877],{"class":71},"        default_ssh.failed is false",[54,1879,1744],{"class":60},[54,1881,1882,1884,1886],{"class":56,"line":1188},[54,1883,1822],{"class":64},[54,1885,68],{"class":60},[54,1887,1888],{"class":71},"ssh_port_set\n",[54,1890,1891],{"class":56,"line":1197},[54,1892,175],{"emptyLinePlaceholder":174},[54,1894,1895],{"class":56,"line":1205},[54,1896,175],{"emptyLinePlaceholder":174},[54,1898,1899],{"class":56,"line":1214},[54,1900,175],{"emptyLinePlaceholder":174},[54,1902,1903],{"class":56,"line":1223},[54,1904,1905],{"class":1635},"# Only runs if the \"default_ssh\" is not defined.\n",[54,1907,1908,1910,1912,1914],{"class":56,"line":1228},[54,1909,61],{"class":60},[54,1911,65],{"class":64},[54,1913,68],{"class":60},[54,1915,1916],{"class":71},"Check if we're using the inventory-provided SSH port\n",[54,1918,1919,1921],{"class":56,"line":1240},[54,1920,1729],{"class":64},[54,1922,125],{"class":60},[54,1924,1925,1927,1929],{"class":56,"line":1247},[54,1926,1736],{"class":64},[54,1928,68],{"class":60},[54,1930,1931],{"class":71},"\"{{ configured_port }}\"\n",[54,1933,1934,1936,1938],{"class":56,"line":1256},[54,1935,497],{"class":64},[54,1937,68],{"class":60},[54,1939,1940],{"class":71},"\"started\"\n",[54,1942,1943,1945,1947],{"class":56,"line":1266},[54,1944,1760],{"class":64},[54,1946,68],{"class":60},[54,1948,1949],{"class":71},"\"{{ inventory_hostname }}\"\n",[54,1951,1952,1954,1956],{"class":56,"line":1277},[54,1953,1780],{"class":64},[54,1955,68],{"class":60},[54,1957,1958],{"class":71},"\"The host is not reachable on the inventory specified port {{ansible_port}}\"\n",[54,1960,1961,1963,1965],{"class":56,"line":1286},[54,1962,1770],{"class":64},[54,1964,68],{"class":60},[54,1966,1775],{"class":94},[54,1968,1969,1971,1973],{"class":56,"line":1291},[54,1970,1790],{"class":64},[54,1972,68],{"class":60},[54,1974,1795],{"class":94},[54,1976,1977,1979,1981],{"class":56,"line":1303},[54,1978,1800],{"class":64},[54,1980,68],{"class":60},[54,1982,1983],{"class":71},"\"localhost\"\n",[54,1985,1986,1988,1990],{"class":56,"line":1310},[54,1987,1812],{"class":64},[54,1989,68],{"class":60},[54,1991,1817],{"class":71},[54,1993,1994,1996,1998],{"class":56,"line":1319},[54,1995,1822],{"class":64},[54,1997,68],{"class":60},[54,1999,2000],{"class":71},"configured_ssh\n",[54,2002,2003,2005,2007],{"class":56,"line":1328},[54,2004,1867],{"class":64},[54,2006,68],{"class":60},[54,2008,1872],{"class":71},[54,2010,2011],{"class":56,"line":1339},[54,2012,2013],{"class":71},"        default_ssh.state is undefined\n",[54,2015,2016],{"class":56,"line":1348},[54,2017,175],{"emptyLinePlaceholder":174},[54,2019,2020],{"class":56,"line":1353},[54,2021,2022],{"class":1635},"# If {{ ansible_port }} is reachable, we don't need to do anything special. \n",[54,2024,2025,2027,2029,2031],{"class":56,"line":1365},[54,2026,61],{"class":60},[54,2028,65],{"class":64},[54,2030,68],{"class":60},[54,2032,2033],{"class":71},"SSH port is configured properly\n",[54,2035,2036,2039],{"class":56,"line":1372},[54,2037,2038],{"class":64},"  debug",[54,2040,125],{"class":60},[54,2042,2043,2045,2047],{"class":56,"line":1381},[54,2044,1780],{"class":64},[54,2046,68],{"class":60},[54,2048,2049],{"class":71},"\"SSH port is configured properly\"\n",[54,2051,2052,2054,2056],{"class":56,"line":1390},[54,2053,1867],{"class":64},[54,2055,68],{"class":60},[54,2057,2058],{"class":71},"configured_ssh is defined and\n",[54,2060,2061],{"class":56,"line":1399},[54,2062,2063],{"class":71},"        configured_ssh.state is defined and\n",[54,2065,2066],{"class":56,"line":1409},[54,2067,2068],{"class":71},"        configured_ssh.state == \"started\"\n",[54,2070,2071,2073,2075],{"class":56,"line":1418},[54,2072,1822],{"class":64},[54,2074,68],{"class":60},[54,2076,1888],{"class":71},[54,2078,2079],{"class":56,"line":1423},[54,2080,175],{"emptyLinePlaceholder":174},[54,2082,2083],{"class":56,"line":1435},[54,2084,2085],{"class":1635},"# At this point, the variable \"ssh_port_set\" should be defined and have some value.\n",[54,2087,2088],{"class":56,"line":1442},[54,2089,2090],{"class":1635},"# if not, it means it's not reachable with neither 22 or \"ansible_port\".\n",[54,2092,2093,2095,2097,2099],{"class":56,"line":1451},[54,2094,61],{"class":60},[54,2096,65],{"class":64},[54,2098,68],{"class":60},[54,2100,2101],{"class":71},"Fail if SSH port was not auto-detected (unknown)\n",[54,2103,2104,2107],{"class":56,"line":1462},[54,2105,2106],{"class":64},"  fail",[54,2108,125],{"class":60},[54,2110,2111,2113,2115],{"class":56,"line":1467},[54,2112,1780],{"class":64},[54,2114,68],{"class":60},[54,2116,2117],{"class":71},"\"The SSH port is neither 22 or {{ ansible_port }}. Check the {{inventory_hostname}} manually!\"\n",[54,2119,2120,2122,2124],{"class":56,"line":1479},[54,2121,1867],{"class":64},[54,2123,68],{"class":60},[54,2125,2126],{"class":71},"ssh_port_set is undefined\n",[54,2128,2129],{"class":56,"line":1487},[54,2130,175],{"emptyLinePlaceholder":174},[54,2132,2133],{"class":56,"line":1497},[54,2134,175],{"emptyLinePlaceholder":174},[54,2136,2138,2140,2142,2144],{"class":56,"line":2137},71,[54,2139,61],{"class":60},[54,2141,65],{"class":64},[54,2143,68],{"class":60},[54,2145,2146],{"class":71},"Change SSH listen port to target port\n",[54,2148,2150,2152],{"class":56,"line":2149},72,[54,2151,733],{"class":64},[54,2153,125],{"class":60},[54,2155,2157,2160,2162],{"class":56,"line":2156},73,[54,2158,2159],{"class":64},"    dest",[54,2161,68],{"class":60},[54,2163,2164],{"class":71},"\"/etc/ssh/sshd_config\"\n",[54,2166,2168,2170,2172],{"class":56,"line":2167},74,[54,2169,758],{"class":64},[54,2171,68],{"class":60},[54,2173,2174],{"class":71},"\"^#?Port.*\"\n",[54,2176,2178,2180,2182],{"class":56,"line":2177},75,[54,2179,768],{"class":64},[54,2181,68],{"class":60},[54,2183,2184],{"class":71},"\"Port {{configured_port}}\"\n",[54,2186,2188,2190],{"class":56,"line":2187},76,[54,2189,778],{"class":64},[54,2191,125],{"class":60},[54,2193,2195,2197],{"class":56,"line":2194},77,[54,2196,407],{"class":60},[54,2198,2199],{"class":71},"restart daemon\n",[54,2201,2203,2205],{"class":56,"line":2202},78,[54,2204,407],{"class":60},[54,2206,843],{"class":71},[54,2208,2210],{"class":56,"line":2209},79,[54,2211,175],{"emptyLinePlaceholder":174},[54,2213,2215],{"class":56,"line":2214},80,[54,2216,175],{"emptyLinePlaceholder":174},[54,2218,2220],{"class":56,"line":2219},81,[54,2221,2222],{"class":1635},"# You probably need this. otherwise the ssh restart handlers are not executed.\n",[54,2224,2226,2228,2230,2232],{"class":56,"line":2225},82,[54,2227,61],{"class":60},[54,2229,65],{"class":64},[54,2231,68],{"class":60},[54,2233,2234],{"class":71},"Flush handlers to apply SSH changes\n",[54,2236,2238,2241,2243],{"class":56,"line":2237},83,[54,2239,2240],{"class":64},"  meta",[54,2242,68],{"class":60},[54,2244,2245],{"class":71},"flush_handlers\n",[54,2247,2249],{"class":56,"line":2248},84,[54,2250,175],{"emptyLinePlaceholder":174},[54,2252,2254,2256,2258,2260],{"class":56,"line":2253},85,[54,2255,61],{"class":60},[54,2257,65],{"class":64},[54,2259,68],{"class":60},[54,2261,2262],{"class":71},"Ensure we use the configured SSH port for the remainder of the role\n",[54,2264,2266,2268],{"class":56,"line":2265},86,[54,2267,1689],{"class":64},[54,2269,125],{"class":60},[54,2271,2273,2275,2277],{"class":56,"line":2272},87,[54,2274,1858],{"class":64},[54,2276,68],{"class":60},[54,2278,1931],{"class":71},[54,2280,2282],{"class":56,"line":2281},88,[54,2283,175],{"emptyLinePlaceholder":174},[54,2285,2287],{"class":56,"line":2286},89,[54,2288,2289],{"class":1635},"# gather facts is better to be disabled during this playbook. because it's needs a ssh connection to the remote server. \n",[54,2291,2293,2295,2297,2299,2302],{"class":56,"line":2292},90,[54,2294,61],{"class":60},[54,2296,65],{"class":64},[54,2298,68],{"class":60},[54,2300,2301],{"class":71},"gather facts now, as now the server is ready in the configured port",[54,2303,1744],{"class":60},[54,2305,2307,2310],{"class":56,"line":2306},91,[54,2308,2309],{"class":64},"  ansible.builtin.setup",[54,2311,125],{"class":60},[557,2313,2314],{},[10,2315,2316,2317,2320],{},"First, change the variable ",[51,2318,2319],{},"ansible_port"," to the target port that you want to change. For example: 1822",[10,2322,2323],{},"I've been written a lot of comments in the playbook so you can make sense from what is happening.",[10,2325,2326],{},"Here is some tips:",[668,2328,2329,2340],{},[17,2330,2331,2332,2335,2336,2339],{},"In general, we use the module ",[51,2333,2334],{},"ansible.builtin.waitfor"," for checking if the hosts are accessible on ports. if they are not reachable, the state of the ",[51,2337,2338],{},"waitfor"," module is undefined. We use this as a primary way to check this issue and act in systematic way.",[17,2341,2342,2343,2346,2347,2349,2350,2352],{},"Using ",[51,2344,2345],{},"elegate_to"," field in ",[51,2348,2338],{}," module is necessary. We want to initiate this process from our ",[51,2351,1805],{}," not the remote host!",[557,2354,2355],{},[10,2356,2357],{},"Do some research about how your linux distro is managing the ssh daemon and socket so you will make sure the changes are applied correctly. Things like Systemd are messing with everything nowdays...",[36,2359,2361],{"id":2360},"wrapping-up","Wrapping up",[10,2363,2364],{},"As you seen, Ansible has a really important place for automation In DevOps tool era. It's performant and ergonomic for writing common tasks. But as a downside, it's tedious to write and test properly. And also playbooks can become extremely tricky for custom and uncommon tasks.",[2366,2367,2368],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}",{"title":49,"searchDepth":75,"depth":75,"links":2370},[2371,2372,2373,2374,2375,2376,2377],{"id":38,"depth":75,"text":39},{"id":438,"depth":75,"text":439},{"id":547,"depth":75,"text":548},{"id":642,"depth":75,"text":643},{"id":871,"depth":75,"text":872},{"id":1523,"depth":75,"text":1524},{"id":2360,"depth":75,"text":2361},"2025-09-04T00:00:00.000Z","Let's write some playbooks",false,"md","/blog-images/cows.webp",{},"/practical-ansible",{"title":5,"description":2379},"17.practical-ansible",[2388,2389],"devops","linux","Ap2CgTQz98FDaWGgtHFCPX4NbRF5m0KI9ML6Tyb8bCw",1772192624686]