ansible - part3

dos2unix

Well-Known Member
Joined
May 3, 2019
Messages
3,245
Reaction score
2,996
Credits
28,678
What is RBAC? You'll hear this term a lot if you become a professional system admin.
It doesn't only pertain to ansible, but of course this is about ansible, so we'll limit it for now.

Role-Based Access Control (RBAC) in Ansible is used to restrict and manage user access to resources based on their role within an organization. This helps ensure that users can only perform actions that they are authorized to do

Normally I would do this with an ansible GUI like AWX/Tower, but that's beyond the scope of this article.
For now, it pretty much comes down to what users have permissions to run which playbooks.

Instead of putting all your playbooks in a central local location and giving everyone read access to them, I recommend
putting webserver script in the webserver admin's home directory, and chown'ing him as the owner. The chmod the
file so no one else can see it.

Do the same for the database admin, give him custom playbooks, that only he can see and run.

Do the same for the network admin, and so on...

This can also be managed with ssh accounts and ssh keys. For example only give the webserver admins accounts on the webservers.
Only give the database admins accounts on the database servers, etc...

Being system admin is always being aware of security and permissions. There is an art to it that comes from experience.
It's difficult to read one or two articles like this and know everything you need to know.

I mentioned earlier, users can have custom hosts/inventory files. Remember you can only run scripts on the systems in your
hosts file. Don't put the IPs of the database servers in the webadmins directory. Don't put the webserver IPs in the host file
of the DBAs. Use a little common sense here. Now of course if you're the only guy running the whole show, then it doesn't matter
that much.

One nice thing about ansible, is that it is "agentless". You don't have to install any special software or client package in order for
it to work. All you need is ssh and python3. You can use ansible with AIX, Solaris, BSD, Newer Mac OS's, there is even some support
for Windows. (although Windows setup is a bit more complicated, there are additional steps required)

In part 1 we covered basic installation and how to run basic playbooks. In part 2 we covered some more advanced topics like
jinja2 variables, if and efif logic, and doing some basic math. We also talked about running multiple complex tasks in a single
playbook. Now we will get even more advanced in part 3.
 


While we are on the subject of security, I should mention the ansible vault.
What if I have some application, say a database. I don't want everyone to know my login credentials
to that database. I can hide those credentials using a vault file.

Code:
ansible-vault create secret.yml

You can name this file whatever you want, but I recommend naming it something that makes sense so you know what it is.
Put the following lines in this file.

Code:
secret_user: bob
secret_password: s3cr3tp@s$w0rd

When you run this command, it will prompt you for yet another password.
Remember this password! I would say, write it down somewhere, but that kind of defeats the security purpose here.
Obviously change the username and password to match your requirements.
So that's great, I have some hidden credentials, how do I use them in a playbook.
Without this password, the contents of this file are encrypted. Even if I have permission to view
this file, I wouldn't know the credentials.

Code:
cat secret.yml

$ANSIBLE_VAULT;1.1;AES256
32633366306231323066346661313833383432623438323662626135373762346338343864653434
6634316531626639613366373232306230636464666266380a386565653034373633643465396239
35636664343630616639316562653665653830326533623065656661306633626466316535626466
3838356533346365650a623132316562386166323065373436313832303830636534366366633930
61343966363431626563323762376361616263613637303332643030346465353338353761303530
37386461666663636634316465346530333537393363363233393462343432323365653130613337
623363616263396466316461616561356637

Code:
---
- hosts: all
  tasks:
    - name: Use vaulted variables
      debug:
        msg: "User: {{ secret_user }}, Password: {{ secret_password }}"

Notice this is using the {{ jinja2 }} variables. But how did it know what file to find them in?
Well now we have to run our playbook command a little differently.

The first method
Code:
ansible-playbook playbook.yml --ask-vault-pass

This will ask for the password you used when you created the vault file. Not the password in the vault file.
But it will use the credentials in the vault file.

There is another way to do this, but it's less secure.

Code:
echo "your_vault_password" > vault_pass.txt
chmod 600 vault_pass.txt
[code]

I could run my playbook like this
[code]
ansible-playbook playbook.yml --vault-password-file vault_pass.txt

You would put the password you used to create the vault file in this text file.
By changing the permissions to 600, only you can see this file. (well root can also)
The advantage to doing this is now I can automate the automation.

For example, I have a playbook that updates all my computers to the latest fixes and security patches.

I want to run it every week. I would use cron to do this.

Code:
0 2 * * 1 /usr/bin/ansible-playbook /path/to/playbook.yml --vault-password-file /path/to/vault_pass.txt

This runs my update script every Monday at 2:00am. I don't have to deal with it any more.
Total automation at this point. I can move on to my next task.
 
The next thing I want to talk about here, is something called nested values.
If you've ever done much programming, you're already familiar with this concept.

But let's run a command on our ansible server.

Code:
ansible -m setup localhost

We did this same command back in the first post of part1. It's a lot of information.
1200 lines or more. But if you scroll through it, you will see some things like this...

Code:
        "ansible_default_ipv4": {
            "address": "10.0.0.66",
            "alias": "eno1",
            "broadcast": "10.0.0.255",
            "gateway": "10.0.0.1",
            "interface": "eno1",
            "macaddress": "54:b2:03:89:d3:07",
            "mtu": 1500,
            "netmask": "255.255.255.0",

..or maybe something like this...

Code:
            "nvme0n1": {
                "holders": [],
                "host": "Non-Volatile memory controller: Intel Corporation SSD DC P4101/Pro 7600p/760p/E 6100p Series (rev 03)",
                "links": {
                    "ids": [
                        "nvme-INTEL_SSDPEKKR256G8_BTHH83100WD7256B",
                        "nvme-INTEL_SSDPEKKR256G8_BTHH83100WD7256B_1",
                        "nvme-eui.0000000001000000e4d25ccb6cd54f01"
                    ],
                    "labels": [],
                    "masters": [],
                    "uuids": []
                },
                "model": "INTEL SSDPEKKR256G8",
                "partitions": {
                    "nvme0n1p1": {
                        "holders": [],
                        "links": {
                            "ids": [
                                "nvme-INTEL_SSDPEKKR256G8_BTHH83100WD7256B-part1",
                                "nvme-INTEL_SSDPEKKR256G8_BTHH83100WD7256B_1-part1",
                                "nvme-eui.0000000001000000e4d25ccb6cd54f01-part1"
                            ],
                            "labels": [],
                            "masters": [],
                            "uuids": [
                                "0512-6120"
                            ]
                        },
                        "sectors": "757760",
                        "sectorsize": 512,
                        "size": "370.00 MB",
                        "start": "2048",
                        "uuid": "0512-6120"
                    },

But I don't want all that information, I just want a part of it. How do I parse it?
Notice there are a lot of indentations in this output. Those indentations matter.

How would get the IP address and gateway from the first output above?

Code:
---
- name: Display Address and Gateway
  hosts: all
  gather_facts: yes

  tasks:
    - name: Display default IPv4 address
      debug:
        msg: "The default IPv4 address is {{ ansible_facts.ansible_default_ipv4.address }}"

    - name: Display default IPv4 gateway
      debug:
        msg: "The default IPv4 gateway is {{ ansible_facts.ansible_default_ipv4.gateway }}"

Notice the period between ansible_facts and default_ipv4.
Notice another period between default_ipv4 and address.

Note that the number of periods match the number of indentations in my output above.
Now for the output of the second example, all I want, is the size of the nvme0n1p1 partition. How would I get that?

Code:
---
- name: Display NVMe Partition Size
  hosts: all
  gather_facts: yes

  tasks:
    - name: Display size of nvme0n1p1 partition
      debug:
        msg: "The size of nvme0n1p1 partition is {{ ansible_facts.nvme0n1.partitions.nvme0n1p1.size }}"

It's the same concept; the number of periods match the number of indentations. We call this "nested values".
 
So that basically concludes this tutorial. There is tons more we could discuss about ansible, but this should
be enough to get you going. More than enough to get you in trouble :)

We've gotten into some pretty advanced stuff here. maybe not enough to make you a ansible guru just yet, but you're
on your way now.

There is an old joke among system admins. There are different variations of it but basically
the punchline of the joke is... "rm -rf /* is my favorite command".

Now of course that command will erase everything on your hard drive. Almost the same as re-formatting it.
But the point here is, ansible will let you do crazy things.

Have fun, but be careful.
 
Last edited:
I forgot I was going to mention lint.

Again different distro's have different names for this. But it will be similar to this.
Go ahead and install this.

Code:
python3-ansible-lint

Let's say we have a playbook with a problem.

Code:
---
- name: Sample broken playbook
  hosts: all
  tasks:
    - name: Install a package
      apt: name=nginx state=present
      become: yes
    - name: Start nginx service
      service:
        name: nginx
        state: started

Did you spot the problem already? if not, run this...

Code:
ansible-lint playbook.yaml

It should retrun something similar to this...

Code:
[ANSIBLE0002] Syntax Error while loading YAML.
  [Syntax Error] line 6, column 23

We have our "state" on the same line as our apt command ( line 6 )

So now you know that there is an error and which line to look for it on.

Here is our fixed playbook.

Code:
---
- name: Sample fixed playbook
  hosts: all
  tasks:
    - name: Install a package
      apt:
        name: nginx
        state: present
      become: yes
    - name: Start nginx service
      service:
        name: nginx
        state: started
 

Staff online


Top