The course is part of this learning path
An overview of Ansible Tower and Ansible Galaxy
Welcome back to Introduction to Managing Ansible Infrastructure. Now that I have Ansible Tower running my playbooks successfully, we need to discuss security.
Ansible Tower's main goal is a true orchestration tool with Ansible Playbooks at its heart. Once fully integrated into an environment, Tower may have control over a great many resources. Access control to these resources is absolutely critical and Tower offers the ability to control this in the form of users, organizations and teams.
I've brought these terms up a handful of times in this course, but have yet to go into any detail. The simplest form of access control is that of a user. Users are the individual users of the system that will have associated permissions and credentials.
To ease the management of groups of users, the concepts of organizations has been introduced. An organization is a collection of users, teams, projects and inventories. Lastly, Tower has the concept of teams. Teams are a subset of an organization that provide a method to delegate responsibilities across organizations in a role-based like access control method.
I know these definitions may seem a little vague, so I will go over each in more detail. As I mentioned earlier, users are the simplest form of access control in Tower.
Tower breaks down its users in three classes. A normal user who has limited read and write access to inventories and projects. A system auditor which is a normal user with read only access to all resources, and a system administrator which is a user with unlimited administrative access to Tower. Don't confuse these user types with the underlying Linux system users.
Creating a user is very straight forward. Select the system configuration icon, the gear at the top of the screen and choose users. Clicking the green add button will bring up the new user dialogue. Complete the form and click save. You have now created a new Tower user.
Organizations are a collection of users, teams, projects and inventories. The purpose of an organization is to bind resources, projects and inventories to users, users and teams. Allowing a logical unit to which to assign access control.
Organizations are managed from the settings menu. To add a new organization, click the green add button, fill out the name and description and click save. Users and teams can now be added via the permissions tab. Organizations also support the ability to notify members of events within the organization.
The notifications tab allows notifications to be configured. Notifications can be delivered by a number of methods including email, Slack, PagerDuty and IRC. On a trial Self-Support licence only the existing default organization can exist and be used. Teams can be a subdivision of organizations and provide a role based access control scheme across organizations.
Teams are made up of users and thus permissions can be granted to a whole team rather than each individual user on the team. When a user is added to a team, that user can be assigned as an admin with escalated privileges for managing the team including viewing the activity stream of a team, adding and removing members and managing permissions. If a user isn't an admin, they are simply a team member.
A team can be created or managed from the settings page under Teams. Create a new team by clicking the add button and note that a name and organization are required. Once created users can be added and permissions can be granted by editing the team.
I have spoken at length about permissions and access control, but only touched on the topic of credentials. Tower manages credentials for many systems that we'll need to interact with. These include individual machine, cloud, network and SCM credentials.
Machine credentials are those which Ansible will need to run on a machine. These are typical credentials involving system user names, not Ansible user names, SSH private keys, passwords and methods with privilege escalation.
Cloud credentials consist of the different supported clouds and their associated credential requirements for accessing that cloud. Currently, the following cloud infrastructures are supported by Tower: Amazon Web Services, Rackspace, VMware vCenter, Red Hat Satellite 6, Red Hat CloudForms, Google Compute Engine, Microsoft Azure and OpenStack.
The network credentials allow Ansible to connect to networking modules for managed network devices. Ansible supports a vast array of network modules, such as modules for JUNOS, Nexus, F5, EOS, and many others.
The last type of credentials I mentioned are SCM, Source Control Management modules. Used for retrieving playbooks, these are remote code repositories and require credentials. Added credentials are stored encrypted in Tower. They cannot be retrieved in plain text by any user from any interface. Storing them in Tower prevents the need to manage credentials when a user joins or leaves an organization, a team or Tower itself.
Management of credentials in Tower are accessible from the settings button. Credentials can be added to Tower from the credentials page by clicking the green add button. A name is required and based on the credentials I mentioned earlier, choose the appropriate credential and fill out the required credential data. Once the credential is saved, it can be used a user, team, an organization.
The last Ansible Tower topic I would like to touch on is the availability of both a command line interface and an application programming interface. The Ansible Tower CLI is written in Python and can be installed with pip. It utilizes the Tower API and is capable of creating, deleting, modifying and reading most objects in Tower. It also supports authentication against Tower's API and will run against a remote Tower host.
Credentials and configurations are stored in the config files, but can also be passed at run time. General usage is to pass the Tower's CLI, a resource and an action to use on that resource.
I have provided a handful of example uses for the command line interface: listing users, launching a job, disabling SSL warning which is necessary when using a self-signed SSL certificate, launching a job including extra vars, updating a user and accessing the included help system.
Ansible Tower offers an easy way to use representational state transfer, API or REST via HTTPS protocol. The API is browseable by pointing a web browser to Tower's server's address and appending /api/ to the URL. From there, the various resources can be browsed and put in post methods using JSON formatted strings for variables can be passed for manipulating the API.
Tower also supports multiple versions of the API which can be specified by appending the version number to the end of the URL. For example, /api/v1/.
When querying the API, returned data can be sorted by adding question mark, order, underscore, by, equals, open curly bracket, open curly bracket, field, closed curly bracket, closed curly bracket to the end of the URL requested. In a similar fashion, searching can be done as well with question mark search equals value.
While the API is fairly straight forward, it's not extremely useful from the web based interface. Let's look at a demonstration using the API from a Python script to view all the users currently in Tower. I'll start by creating view users.py and add the basic Python shebang.
As I mentioned earlier, values past two and received from the API will be in JSON format. So, I'll include the JSON library. Additionally, I'll use the urllib2 to talk to the API, and I'm gonna do this for security over https, so I want my SSL library. I'll then add the basic Python starting point.
Now, before we can do anything, I need to authenticate myself against the Tower API. For this example, we'll stick with using localhost for Tower host and admin Tower for the username and password.
If you recall in the command line section above, I mentioned disabling SSL certificate verification for self-signed certificates. I'll be doing the same thing here by using the SSL create unverified context syntax.
Now, to authenticate myself, we're gonna receive a token back so that we can have continued communications with Tower without having to re-authenticate ourselves for each call we make. So, we'll call that our session token.
Now in the same way I disabled the SSL cert verification, we're gonna set the URL to point to API version one and the auth token interface we're gonna pack, pass it local host. We then need to pass parameters. So what we're doing here is we're passing the username which is admin and the password which is Tower. As a dictionary, remember using json dumps to convert that to a string.
The last bit of information we need is the content type, and that's gonna be application json and now we can make our https request. Pass it the URL, the parameters and content type. Now, we're gonna open the URL and store the results. So, we send our request. We pass it the context which is our username and password and then we read in the results.
Now, the results are going to come back to us as a string and it's going to be a JSON string. As I said earlier, everything that we send and receive is JSON. So, we're gonna take the JSON called json loads which will convert this into a dictionary.
Now, we need the session token. The session token will be returned in that dictionary. So, we'll fetch it from there and return it back so that our session has a valid token. Now, that we've spoken to the API and gotten our token, we can make the request to take a look at the user.
So, we'll fetch our users using the function fetch users we'll pass the session token to it, but before we do that, let's stop for a moment and print out the session token, just so we can see that it's working properly.
Please note that this should be urllib2 and we'll test to see if it prints out the token, and there it goes. So, that's is the token string we'll be passing each time we make a call to the API from this time forward.
So now we'll create the function that will query the user endpoint of the API. The target URL is now going to point to the API versions one like before, but to the user's endpoint, and again, we must set the context to prevent it from checking for a valid certificate and this time we're adding a token header.
This is where we pass in our session token. It gets formatted as a string. It's gonna be token followed up by a space and the actual session token, and this gets passed in as a Python dictionary.
So, like with the authentication we make our request, pass it the target, pass it an empty dictionary, since we have no parameters to go in there and then finally, pass it our token header, and that will open up our URL with our request, and again, pass it the context to avoid checking, and we're gonna return.
Again, convert the string to JSON first. We will return exactly what was sent to us. So, now we can take a quick look, we can print our users and we can run this again. So, as you can see here, the list of our users was returned. We get a lot of detailed information including teams, roles, projects, et cetera. There is currently only one user, our default user admin. If there were more it would list each one as part of the results list.
This is a very basic example and much more can be done to automate much of Ansible Tower through its API. Hopefully, this short example and course was helpful in seeing the potential Ansible Tower has to manage an environment and fit into a CICD Pipeline.
About the Author
Stelligent's entire focus is DevOps automation and Continuous Delivery in the AWS cloud. Founded in 2007, Stelligent is an AWS Advanced Consulting Partner with the DevOps Competency. For more information please visit https://stelligent.com/