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.
Related
I have list of systemd services defined as
vars:
systemd_scripts: ['a.service', 'b.service', 'c.service']
Now I want to stop only a.service from above list. How this can be achieved using systemd_module?
What are you trying to achieve? As written, you could just do:
- name: Stop service A
systemd:
name: a.service
state: stopped
If instead you mean "the first service", use the first filter or an index:
- name: Stop first service
systemd:
name: "{{ systemd_scripts | first }}"
state: stopped
OR
- name: Stop first service
systemd:
name: "{{ systemd_scripts[0] }}"
state: stopped
Your question is very vague and unspecific. However, parts of your question could be answered with the following approach
- name: Loop over service list and stop one service
systemd:
name: "{{ item }}"
state: stopped
when:
- systemd_scripts[item] == 'a.service'
with_items: "{{ systemd_scripts }}"
You may need to extend and change it for your needs and required functionality.
Regarding discovering, starting and stopping services via Ansible facts (service_facts) and systemd you may have a look into
Further readings
Ansible: How to get disabled but running services?
How to declare a variable for service_facts?
How to check service exists and is not installed in the server using service_facts module in an Ansible playbook?
How can I write an Ansible playbook to start my nginx service only if firewalld.service is in stop state?
Note: Execution being done on a sandbox server. So no issues about firewalld in stop state.
You can check the service status, register a variable, and based on the variable result - start the service.
You can also use the service_facts module to get the service status.
Example:
- name: Populate service facts
service_facts:
- name: start nginx if firewalld.service is stopped
service:
name: nginx
state: started
when: ansible_facts.services['firewalld.service'].state == 'stopped'
Note: I did not tested that. Modify it accordingly
As already proposed by #Telinov, you can get facts for services and start your own service accordingly:
---
- name: Populate service facts
service_facts:
- name: start nginx if firewalld.service is stopped
service:
name: nginx
state: started
when: ansible_facts.services['firewalld.service'].state == 'stopped'
Meanwhile, ansible is about describing state. If you need firewalld stopped and nginx started, it is much easier to just say so:
- name: Make sure firewalld is stopped
service:
name: firewalld
state: stopped
- name: Make sure nginx is started
service:
name: nginx
state: started
You could be even smarter combining the above and open the firewall ports for nginx if it is running.
---
- name: Populate service facts
service_facts:
- name: Make sure port 80 and 443 are opened if firewall is running
firewalld:
port: "{{ port }}/tcp"
state: enabled
permanent: true
immediate: true
loop:
- 80
- 443
loop_control:
loop_var: port
when: ansible_facts.services['firewalld.service'].state == 'running'
- name: Make sure nginx is started
service:
name: nginx
state: started
I'm provisioning a server and hardening it by disabling root access after creating an account with escalated privs. The tasks before the account creation require root so once root has been disabled the playbook is no longer idempotent. I've discovered one way to resolve this is to use wait_for_connection with block/rescue...
- name: Secure the server
hosts: "{{ hostvars.localhost.ipv4 }}"
gather_facts: no
tasks:
- name: Block required to leverage rescue as only way I can see of avoid an already disabled root stopping the playbook
block:
- wait_for_connection:
sleep: 2 # avoid too many requests within the timeout
timeout: 5
- name: Create the service account first so that play is idempotent, we can't rely on root being enabled as it is disabled later
user:
name: swirb
password: {{password}}
shell: /bin/bash
ssh_key_file: "{{ssh_key}}"
groups: sudo
- name: Add authorized keys for service account
authorized_key:
user: swirb
key: '{{ item }}'
with_file:
- "{{ssh_key}}"
- name: Disallow password authentication
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^[\#]PasswordAuthentication"
line: "PasswordAuthentication no"
state: present
notify: Restart ssh
- name: Disable root login
replace:
path: /etc/ssh/sshd_config
regexp: 'PermitRootLogin yes'
replace: 'PermitRootLogin no'
backup: yes
become: yes
notify: Restart ssh
rescue:
- debug:
msg: "{{error}}"
handlers:
- name: Restart ssh
service: name=ssh state=restarted
This is fine until I install fail2ban as the wait_for_connection causes too many connections to the server which jails the IP. So I created a task to add the IP address of the Ansible Controller to the jail.conf like so...
- name: Install and configure fail2ban
hosts: "{{hostvars.localhost.ipv4}}"
gather_facts: no
tasks:
- name: Install fail2ban
apt:
name: "{{ packages }}"
vars:
packages:
- fail2ban
become: yes
- name: Add the IP address to the whitelist otherwise wait_for_connection triggers jailing
lineinfile: dest=/etc/fail2ban/jail.conf
regexp="^(ignoreip = (?!.*{{hostvars.localhost.ipv4}}).*)"
line="\1 <IPv4>"
state=present
backrefs=True
notify: Restart fail2ban
become: yes
handlers:
- name: Restart fail2ban
service: name=fail2ban state=restarted
become: yes
This works but I have to hard wire the Ansible Controller IPv4. There doesn't seem to be a standard way of obtaining the IP address of the Ansible Controller.
I'm also not that keen on adding the controller to every server white list.
Is there a cleaner way of creating an idempotent provisioning playbook?
Otherwise, how do I get the Ansible Controller IP address?
I'm trying to set up a role in ansible to install some servers with needed applications. One of the apps is docker.
Docker-ce is installed successfully. Now I'm trying to tell the system to startup docker.service and enable it by reboot.
When I'm creating a list over "with_items" it works fine, when I'm trying to use a list out of my defaults/main.yml file ansible tells me that it can't find the service docker. Now I'm wondering, maybe just some spelling problem?
This one works fine
- name: Start and enable needed services
systemd:
name: "{{ item }}"
state: started
enabled: yes
daemon_reload: yes
with_items:
- docker
This one doesn't work
- name: Start and enable needed services
systemd:
name: "{{ clientonline }}"
state: started
enabled: yes
daemon_reload: yes
-------
# in defaults/main.yml
clientonline:
- docker
Ansible can't find the docker service when I'm using my list from defaults/main.yml
[WARNING]: The value ['docker'] (type list) in a string field was converted to u"['docker']" (type string). If this does not look like what you expect, quote the entire value to ensure it does not change.
Also this example doesn't work:
- name: Start and enable needed services
systemd:
name: "{{ item }}"
state: started
enabled: yes
daemon_reload: yes
with_items:
- clientonline
That brings this error:
failed: [fgi_appdeploy_server] (item=clientonline) => {"ansible_loop_var": "item", "changed": false, "item": "clientonline", "msg": "Could not find the requested service clientonline: host"}
that will work:
- name: Start and enable needed services
systemd:
name: "{{ item }}"
state: started
enabled: yes
daemon_reload: yes
with_item:
- "{{ clientonline }}"
since clientonline is a list you need to loop through it
OK; this now works fine for systemd:
- name: Start and enable needed services
systemd:
name: '{{ item }}'
state: started
enabled: yes
daemon_reload: yes
with_items:
- '{{ clientonline }}'
But same style for yum module will bring this warning on bash, nice to know
- name: Install needed packages
yum:
name: '{{ item }}'
state: latest
with_items:
- '{{ clientpackages }}'
[DEPRECATION WARNING]: Invoking "yum" only once while using a loop via squash_actions is deprecated. Instead of using a loop to supply multiple items and specifying `name: "{{ item }}"`, please use `name: ['{{ serverpackages }}']` and remove the loop. This feature will
be removed in version 2.11. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
Nice to know - thanks to all!
I copy-pasted this from the manual and it fails in my playbook (version 2.0.2):
- service: name=network state=restarted args=eth0
I am getting this error:
"msg": "Failed to stop eth0.service: Unit eth0.service not loaded.\nFailed to start eth0.service: Unit eth0.service failed to load: No such file or directory.\n"}
What is the correct syntax, please?
Just do like this (#nasr already commented it):
- name: Restart network
service:
name: network
state: restarted
But if you change network configuration before restart, something like IP address, after restart ansible hangs because connection is lost (IP address changed).
There is a way to do things right.
tasks.yml
- name: net configuration step 1
debug:
msg: we changed some files
notify: restart systemd-networkd
- name: net configuration step 2
debug:
msg: do some more work, but restart net services only once
notify: restart systemd-networkd
handlers.yml
- name: restart systemd-networkd
systemd:
name: systemd-networkd
state: restarted
async: 120
poll: 0
register: net_restarting
- name: check restart systemd-networkd status
async_status:
jid: "{{ net_restarting.ansible_job_id }}"
register: async_poll_results
until: async_poll_results.finished
retries: 30
listen: restart systemd-networkd
As per Ansible 2.7.8. You have to make following changes to restart the network.
Note: I tried this on Ubuntu 16.04
Scenario 1: Only network restart
- name: Restarting Network to take effect new IP Address
become: yes
service:
name: networking
state: restarted
Scenario 2: Flush interface and then restart network
- name: Flushing Interface
become: yes
shell: sudo ip addr flush "{{net_interface}}"
- name: Restarting Network
become: yes
service:
name: networking
state: restarted
Note: Make sure you have net_interface configured and then imported in the file where you execute this Ansible task.
OUTPUT
Please find below output that I received on my screen.
- command: /etc/init.d/network restart
does work wonderfully but I feel that using command kinda defeats the purpose of using ansible.
I m using Ubuntu 22.04.1 LTS that uses systemd instead of init
The following worked fine with me ( I tried the solutions mentioned earlier but none has worked for me)
- name: restart network
systemd:
name: NetworkManager
state: restarted