skip to Main Content

I have made an ansible script to collect IP in the nginx log and then the IP that has been collected will be analyzed, if a malicious IP is detected it will be blocked.

The idea is that in the ansible script each IP analyzed is not created individually but using a loop function

But I’m having a hard time making a looping script in ansible, can anyone help?

Here is the script that i have created


  - name: Execute a command using the shell module
    shell: awk '{print $1}' /var/log/nginx/*.log | sort | uniq -c | sort -nr| head -5
    register: results
  - debug: var=results.stdout_lines
  - name: Write Output
    local_action: shell echo "{{ results.stdout_lines }}" > /tmp/output

#Collect IP

  - name: Check 1st IP
    local_action: shell cat /tmp/output | awk  '{ print $3 }'| sed 's/.$//'|sed 's/.$//'
    register: ip1
  - debug: var=ip1.stdout

  - name: Check 2nd IP
    local_action: shell cat /tmp/output | awk  '{ print $6 }'| sed 's/.$//'|sed 's/.$//'
    register: ip2
  - debug: var=ip2.stdout

  - name: Check 3rd IP
    local_action: shell cat /tmp/output | awk  '{ print $9 }'| sed 's/.$//'|sed 's/.$//'
    register: ip3
  - debug: var=ip3.stdout

  - name: Check 4th IP
    local_action: shell cat /tmp/output | awk  '{ print $12 }'| sed 's/.$//'|sed 's/.$//'
    register: ip4
  - debug: var=ip4.stdout

  - name: Check 5th IP
    local_action: shell cat /tmp/output | awk  '{ print $15 }'| sed 's/.$//'|sed 's/.$//'
    register: ip5
  - debug: var=ip5.stdout

#Anlyze IP

  - name: Analyze 1st IP
    local_action: shell /usr/bin/abuseipdb -C {{ ip1.stdout }} -o plaintext
    register: vb1
  - debug: var=vb1.stdout

  - name: Analyze 2nd IP
    local_action: shell /usr/bin/abuseipdb -C {{ ip2.stdout }} -o plaintext
    register: vb2
  - debug: var=vb2.stdout

  - name: Analyze 3rd IP
    local_action: shell /usr/bin/abuseipdb -C {{ ip3.stdout }} -o plaintext
    register: vb3
  - debug: var=vb3.stdout

  - name: Analyze 4th IP
    local_action: shell /usr/bin/abuseipdb -C {{ ip4.stdout }} -o plaintext
    register: vb4
  - debug: var=vb4.stdout

  - name: Analyze 5th IP
    local_action: shell /usr/bin/abuseipdb -C {{ ip5.stdout }} -o plaintext
    register: vb5
  - debug: var=vb5.stdout

##Block IP if score more than 25

  - name: Check 1rst IP and Block IP if score more than 25
    shell: /sbin/route add "{{ ip1.stdout }}" gw 127.0.0.1 lo
    when: "{{ vb1.stdout }} >= 25"

  - name: Check 2nd IP and Block IP if score more than 25
    shell: /sbin/route add "{{ ip2.stdout }}" gw 127.0.0.1 lo
    when: "{{ vb2.stdout }} >= 25"

  - name: Check 3rd IP and Block IP if score more than 25
    shell: /sbin/route add "{{ ip3.stdout }}" gw 127.0.0.1 lo
    when: "{{ vb3.stdout }} >= 25"

  - name: Check 4th IP and Block IP if score more than 25
    shell: /sbin/route add "{{ ip4.stdout }}" gw 127.0.0.1 lo
    when: "{{ vb4.stdout }} >= 25"

  - name: Check 5th IP and Block IP if score more than 25
    shell: /sbin/route add "{{ ip5.stdout }}" gw 127.0.0.1 lo
    when: "{{ vb5.stdout }} >= 25"

#Check Route

  - name: Make Sure the IP has been blocked
    shell: /sbin/route -n
    register: route
  - debug: var=route.stdout_lines

Here is the /tmp/output

└─$ cat /tmp/output
['  40545 87.250.224.147', '  20873 87.250.224.126', '  16665 213.180.203.67', '  15420 87.250.224.142', '  14503 13.81.52.25']

Here is the output when running


└─$ cat /tmp/output | awk  '{ print $3 }'| sed 's/.$//'|sed 's/.$//'
87.250.224.147

└─$ cat /tmp/output | awk  '{ print $6 }'| sed 's/.$//'|sed 's/.$//'
87.250.224.126

└─$ cat /tmp/output | awk  '{ print $9 }'| sed 's/.$//'|sed 's/.$//'
213.180.203.67

└─$ cat /tmp/output | awk  '{ print $12 }'| sed 's/.$//'|sed 's/.$//'
87.250.224.142

└─$ cat /tmp/output | awk  '{ print $15 }'| sed 's/.$//'|sed 's/.$//'
13.81.52.25

2

Answers


  1. so you could use range to loop over your command shell, i show you an example: when you loop with register, it records all output in list.

    - name: "tips2"
      hosts: localhost
      tasks: 
        - name: Check 1st IP
          local_action: shell echo {{ item }}
          register: result
          loop: "{{ range(0, 4 + 1, 2)|list }}" #loop from 0 to 4 with a step 2
        - debug: var=result
        #then you build your final list
        - set_fact: 
              ips: "{{ ips | d([]) + item.stdout_lines }}"   
          loop: "{{ result.results }}"  
        - debug: var=ips
    

    display variable result:

    ok: [localhost] => {
        "result": {
            "changed": true,
            "msg": "All items completed",
            "results": [
                {
                    "ansible_loop_var": "item",
                        :
                        :
                        }
                    },
                    "item": 0,
                    "rc": 0,
                    "start": "2022-01-21 09:38:12.092426",
                    "stderr": "",
                    "stderr_lines": [],
                    "stdout": "0",
                    "stdout_lines": [
                        "0"
                    ]
                },
                {
                    "ansible_loop_var": "item",
                        :
                        :
                        }
                    },
                    "item": 2,
                    "rc": 0,
                    "start": "2022-01-21 09:38:12.348007",
                    "stderr": "",
                    "stderr_lines": [],
                    "stdout": "2",
                    "stdout_lines": [
                        "2"
                    ]
                },
                {
                    "ansible_loop_var": "item",
                        :
                        :
                        }
                    },
                    "item": 4,
                    "rc": 0,
                    "start": "2022-01-21 09:38:12.607555",
                    "stderr": "",
                    "stderr_lines": [],
                    "stdout": "4",
                    "stdout_lines": [
                        "4"
                    ]
                }
            ]
        }
    }
    

    and result final ips after set_fact:

    ok: [localhost] => {
        "ips": [
            "0",
            "2",
            "4"
        ]
    }
    

    just adapt the solution to your case: as i dont know your output, check the value of register…

      - name: Check All IPs
        local_action: shell cat /tmp/output | awk  '{ print ${{item}} }'| sed 's/.$//'|sed 's/.$//'
        register: result
        loop: "{{ range(3, 15 + 1, 3)|list }}" #loop from 3 to 15 with a step 3
    
      - set_fact:
          ips: "{{ ips | d([]) + item.stdout_lines }}" 
        loop: "{{ result.results }}" 
    
      - name: Analyze All IPs
        local_action: shell /usr/bin/abuseipdb -C {{ item }} -o plaintext
        register: vbresult
        loop: "{{ ips }}"
      - debug: var=vbresult
    

    another solution will be to trap alls your ips from your file in one task..
    but you have to know your output, but the solution i show is easily adaptable to your case quickly

    EDITED:

    following your output, you could work directly from your result.stdout_lines:

    - name: simulate your output result.stdout_lines
      set_fact:
          values: 
            - '  40545 87.250.224.147'
            - '  20873 87.250.224.126'
            - '  16665 213.180.203.67'
            - '  15420 87.250.224.142'
            - '  14503 13.81.52.25'
    - name: trap ips values
      set_fact:
          ips: "{{ ips | d([]) + [_ip] }}"
      loop: "{{ values }}"
      vars:
        _ip: "{{ (item | trim).split(' ') | last }}"
        
    - debug:
        var: ips
    

    result:

    ok: [localhost] => {
        "ips": [
            "87.250.224.147",
            "87.250.224.126",
            "213.180.203.67",
            "87.250.224.142",
            "13.81.52.25"
        ]
    }
    
    Login or Signup to reply.
  2. It is probably better to generate a JAML file from your Nginx log in order to use include_vars after that.

    Btw: The idea of Ansible is to be platform independent. Because of that, it is better to have most of the platform specific stuff in an external script and not in the playbook. When you change the platform, you just have to migrate the external script, instead of rewriting the playbook.

    Another alternative is to use scripted inventories. You can create a dynamic inventory by reading the IP addresses to block from your Nginx log. If you put the hosts to block into a group, you can define a single task for the group and delegate it to the firewall, where you block the hosts. You do not need to care about iterations. It is the job of Ansible to iterate.

    This is much easier to maintain, than the playbook in your question. And it is extremely inefficient to put every line of a shell script into one local action. Don’t do it.

    PS: Yes you can program in a playbook, somehow. But try to avoid it. A playbook is not a useful programming language. You can not abstract with procedures and it has almost no scope. When you work with Ansible you have to change the way you think. Do not think about procedures. Instead thing about data. Take your data and arrange the data in a way, that is suitable for Ansible. If the data does not fit, use Jinja filters to rearrange the data until it fits. But avoid "programming" in a playbook by all means. And if you need to program, better do it in Jinja statements or Python.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search