How to run an ansible task if ufw is enabled? - ansible

I need to run some tasks only if ufw is enabled on the Ubuntu server.
Pseudocode looks like:
- name: detect if ufw is enabled
magical_module: ???
register: ufw_is_enabled
- name: my task
when: ufw_is_enabled
I see only this nonelegant solution:
exec remotely ufs status
parse the output:
root#test:~# ufw status
Status: inactive
Maybe there is some file when ufw is enabled? In this case, it will be possible to use stat module. Any other solutions?

Something along the lines of:
- name: check whether ufw status is active
shell: ufw status
changed_when: false
register: ufw_check
- debug:
msg: "{{ ufw_check }}"
- name: do something if ufw is enabled
shell: echo
when: "'inactive' not in ufw_check.stdout"
Make sure to use become: true, since root privileges are required to successfully execute the command.
To manage systems with the UFW module, see this Ansible module
What you can do, for example is, disable UFW on systems on which you want it disabled:
- name: Disable UFW on hosts
ufw:
state: disabled # unloads firewall and disables firewall on boot.

Related

Firewall Functional Test

I am new to Ansible, I am only using a central machine and a host node on Ubuntu server, for which I have to deploy a firewall; I was able to make the SSH connections and the execution of the playbook. What I need to know is how to verify that the port I described in the playbook was blocked or opened, either on the controller machine and on the host node. Thanks
According your question
How to verify that the port I described in the playbook was blocked or opened, either on the controller machine and on the host node?
you may are looking for an approach like
- name: "Test connection to NFS_SERVER: {{ NFS_SERVER }}"
wait_for:
host: "{{ NFS_SERVER }}"
port: "{{ item }}"
state: drained
delay: 0
timeout: 3
active_connection_states: SYN_RECV
with_items:
- 111
- 2049
and have also a look into How to use Ansible module wait_for together with loop?
Documentation
Ansible wait_for Examples
You may also interested in Manage firewall with UFW and have a look into
Ansible ufw Examples

How to reload Firewalld service using Ansible?

I added some rule to firewalld in centos 7 with ansible. But I must reload firewalld daemon thus service work properly. Is there any idea?
Here is my ansible code:
- name: Add port to firewalld
firewalld:
port: "{{ item }}"
permanent: yes
state: enabled
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'
loop:
- 8080/tcp
- 8000/tcp
- 8090/tcp
- 8040/tcp
First of all use with_items for list of ports as below:
- name: Add port to firewalld
firewalld:
port: "{{ item }}"
permanent: yes
state: enabled
when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux'
loop:
- 8080/tcp
- 8000/tcp
- 8090/tcp
- 8040/tcp
You can also use the below code to enter ports if they are not fixed and use its as a variable:
- hosts: localhost
gather_facts: no
vars_prompt:
- name: ports
prompt: "Enter port(s) number"
private: no
tasks:
- name: add port
firewalld:
service: "{{ item }}"
permanent: yes
immediate: yes
state: enabled
with_items: "{{ ports.split(',') }}"
and regarding reloading firewalld its mentioned here we can't reload firewalld using state parameter So use systemd module as below:
- name: reload service firewalld
systemd:
name: firewalld
state: reloaded
firewalld module has immediate option which is performing the same reload within firewall-cmd cli tool.
- name: Add port to firewalld
firewalld:
port: "{{ item }}"
permanent: yes
state: enabled
immediate: true
You can use service or systemd module.
#Supports init systems include BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart.
- name: Restart service
service:
name: firewalld
state: restarted
#Controls systemd services on remote hosts.
- name: Restart service
systemd:
state: restarted
daemon_reload: yes
name: firewalld
I'm a bit late but given that all previous answers seem to just speculate I will give another input. Firewalld is not reloaded with 'service' or 'systemctl' commands but rather with it's own specific command:
firewall-cmd --reload
This is because that way you can load new rules without interrupting any active network connections as would be the case when using iptables directly.
Given this I think using service or systemctl is not a good solution.
So if you just want to create a task I suggest using the command module from ansible to execute this command. Or you could write a handler like so:
- name: reload firewalld
command: firewall-cmd --reload
Just put the handler in the handlers/main.yml file inside your role. Then in your tasks you can call that handler with:
notify: reload firewalld
That way Ansible only executes the handler at the end of your Ansible run. I successfully tested this on RHEL7.
If you are using permanent conditional, you can use immediate option.
Example:
- name: Apply Base Services
ansible.posix.firewalld:
service: "{{ item }}"
state: enabled
zone: public
permanent: yes
immediate: true
loop:
- http
- https
After this rule will applied, firewalld will reload automatically.
You already got a number of excellent answers. There is yet another possible approach (although the reloading part is the same as in cstoll's answer).
If you are certain that nobody and nothing else but Ansible will ever manipulate firewalld rules, you can use a template to directly generate the zone XML files in /etc/firewalld/zones . You will still need to add
notify: reload firewalld
and the corresponding handler, as in cstoll's answer.
The main advantage of this approach is that it can be dramatically faster and simpler than adding the rules one at a time.
The drawback of this approach is that it will not preserve any rules added to firewalld outside of Ansible. A second drawback is that it will not do any error checking; you can create invalid zone files easily. The firewall-cmd command (and thus the firewalld module) will verify the validity of each rule. For instance, it checks that zones do not overlap.

Create vars from host inventory

I have below infrastructure (3 servers running windows 2016 server edition) where master server runs a IIS service on port 80(example) and 2 agents need to connect to them. To allow the communication, I need to add windows firewall rules to whitelist the ip addresses
one master server (mas)
and two agent servers (agt)
The task which I need to execute through ansible is, I need to add the below firewall rule only on the master server and should not run on the agent hosts. How to run the below task only on the master server so that the ip address details of agent(agt) machines are used while configuring the firewall rules.
- hosts: mas, agt
tasks:
- name: Firewall Rule Modifications
win_firewall_rule:
name: "IIS port"
localport: "80"
direction: in
action: allow
remoteip: "{{ansible_ip_addresses[0]}}"
protocol: "tcp"
description: "Allow agents"
enabled: yes
state: present
I was able to create a solution(with a vagrant test setup with centos 7) as mentioned below but I think there should be a simpler way to achieve this :-)
Inventory File:
[master]
mas
[agents]
agt1
agt2
Playbook:
- name: Configure Iptables
hosts: all
serial: 1
tasks:
- name: create a file to store inventory IP's
file:
dest: /tmp/foo
state: touch
force: yes
delegate_to: localhost
- name: Register IP address
shell: echo "{{ ansible_enp0s8.ipv4.address }}"
register: op
delegate_to: localhost
- name: write IP's to a temp file
lineinfile:
dest: /tmp/foo
line: "{{ op.stdout_lines[0] }}"
insertafter: EOF
delegate_to: localhost
- name: Add firewall rules
iptables:
chain: INPUT
source: "{{item}}"
protocol: tcp
destination_port: 80
jump: ACCEPT
with_lines: cat /tmp/foo
when: ansible_hostname == 'mas'

Ansible ufw module ERROR: Could not find a profile matching 'xxxxx'

I'm working to setup UFW rules via Ansible. I'm able to get it installed, start it and deny everything. I then attempt to allow connections from http, https, and ssh. All attempts to add the allow for those items are met with errors that look like:
failed: [lempy1] (item={u'service': u'http'}) => {"failed": true, "item": {"service": "http"}, "msg": "ERROR: Could not find a profile matching 'http'\n"}
failed: [lempy1] (item={u'service': u'https'}) => {"failed": true, "item": {"service": "https"}, "msg": "ERROR: Could not find a profile matching 'https'\n"}
failed: [lempy1] (item={u'service': u'ssh'}) => {"failed": true, "item": {"service": "ssh"}, "msg": "ERROR: Could not find a profile matching 'ssh'\n"}
The entire role looks like this:
tasks/main.yml
---
- name: Install ufw
apt: name=ufw state=present
tags:
- security
- name: Allow webservery things
ufw:
rule: allow
name: '{{item.service}}'
with_items:
- service: http
- service: https
- service: ssh
tags:
- security
- name: Start ufw
ufw: state=enabled policy=deny
tags:
- security
Any idea why I wouldn't be able to allow these services? I am able to add the services properly when ssh'ing into the server and running sudo ufw allow http, etc.
As mentioned in the ufw module docs, the name (or app) parameter uses applications that are registered in /etc/ufw/applications.d which have an INI format and look something like this:
[CUPS]
title=Common UNIX Printing System server
description=CUPS is a printing system with support for IPP, samba, lpd, and other protocols.
ports=631
Normally you can use ufw allow application-profile to allow an application defined either in /etc/ufw/applications.d or /etc/services to open up iptables for things that aren't necessarily defined in /etc/ufw/applications.d.
Unfortunately, Ansible's ufw module instead builds the ufw command in this format instead:
/usr/sbin/ufw allow from any to any app 'application-profile'
Which only uses the /etc/ufw/applications.d list and won't read /etc/services.
In your case you could simply specify the ports as these are well known, potentially using a named variable to further explain your Ansible code:
- name: Allow webservery things
ufw:
rule: allow
port: '{{ item }}'
with_items:
- '{{ http_port }}'
- '{{ https_port }}'
- '{{ ssh_port }}'
tags:
- security
And then define the variables somewhere (such as your role defaults):
http_port: 80
https_port: 443
ssh_port: 22
As an aside, you might want to notice that I simplified your list of dictionaries with a single key into a simpler straight list which tidies up your task a bit.
Alternatively you could easily template the application profiles using Ansible's template module.

How to know external ip address of ansible client

During deployment needed setup ufw firewall rule: enable access for hosts group servers from inventory. Target host is anazon ec2 instance, but it maybe placed at other provider.
I tried:
- name: Ufw rules
ufw: rule=allow from_ip={{ hostvars[item]['ansible_eth0']['ipv4']['address'] }}
with_items: groups['servers']
notify:
- Restart ufw
file hosts:
..
[servers]
server1.site.com
server2.site.com
server3.site.com
..
But host server1.site.com, actually have host_vars:
"ansible_eth0": {
"active": true,
"device": "eth0",
"ipv4": {
"address": "10.x.x.x",
...
Ip address 10.x.x.x, as I understand, is internal amazon network ip address.
If execute ping server1.site.com from outside ec2, I get:
64 bytes from server1.site.com (46.x.x.x): icmp_seq=1 ttl=56 time=49.5 ms
...
Executing ansible server1.site.com -m setup -i hosts | grep 46.x.x.x found nothing.
How to known external ip address of host from inventory group using ansible?
Or how setup ufw firewall using host names from inventory?
I think your best bet is to use the lookup plugin written by #dsedivec (https://github.com/dsedivec/ansible-plugins).
Create a lookup_plugins/ directory at the top directory of your playbook.
Put dns.py in it
curl https://raw.githubusercontent.com/dsedivec/ansible-plugins/master/lookup_plugins/dns.py > lookup_plugins/dns.py
And use it like so :
- name: Ufw rules
ufw: rule=allow from_ip={{ lookup('dns', item) }}
with_items: groups['servers']
notify:
- Restart ufw
Good luck !
The easiest way is to add connection: local
---
- name: get my public IP
ipify_facts: api_url=http://api.ipify.org
connection: local
- firewalld: rich_rule='rule family=ipv4 source address={{ ipify_public_ip }} accept' permanent=no state=enabled timeout=300
become: true

Resources