ansible creating virtualenv with root owner - ansible

This Ansible task creates a virtualenv (good!), but the directory (/home/chris/.virtualenvs/foobar) is owned by root (not so good):
- name: install requirements
pip:
chdir: /home/chris/website
requirements: ./requirements.txt
virtualenv: /home/chris/.virtualenvs/foobar
But what's driving me nuts is that the next task fails, apparently because of the root ownership
- name: copy sitecustomize.py
file:
src: /home/chris/website/sitecustomize.py
dest: /home/chris/.virtualenvs/foobar/lib/python2.7/sitecustomize.py
remote_src: yes
I think the problem I really want to solve is creating the .virtualenv so that it's owned by "chris". Any idea how to force that?
Failing that, how can get the "copy" task to run with the same permissions as the "pip" task, so that I can copy my file?
EDIT: Solved second problem -- I needed to use the "copy" task, not the file task. So this...
- name: copy sitecustomize.py
copy:
src: /home/chris/website/sitecustomize.py
dest: /home/chris/.virtualenvs/foobar/lib/python2.7/sitecustomize.py
remote_src: yes

Try to run pip as chris:
- name: install requirements
pip:
chdir: /home/chris/website
requirements: ./requirements.txt
virtualenv: /home/chris/.virtualenvs/foobar
become: yes
become_user: chris
And to be consistent, make chris the owner of sitecustomize.py:
- name: copy sitecustomize.py
file:
src: /home/chris/website/sitecustomize.py
dest: /home/chris/.virtualenvs/foobar/lib/python2.7/sitecustomize.py
remote_src: yes
group: chris
owner: chris

Related

Better way to install helm with Ansible?

The following recipe works, but I find the number of tasks being high and I'm wondering if there is a better way to install Helm while performing a checksum. Currently, I have to:
Check current installed helm version, if any
Download helm and notify the handler to unarchive the binary
Unarchive the binary, if needed
Delete downloaded file, if needed
roles/node/tasks/main.yaml:
- name: Set current helm version
ansible.builtin.command:
cmd: helm version --client --template={{ "'{{ .Version }}'" }}
changed_when: false
failed_when: false
register: current_helm_version
- name: Download helm archive
ansible.builtin.get_url:
url: https://get.helm.sh/helm-{{ helm_version }}-linux-arm64.tar.gz
checksum: sha256:https://get.helm.sh/helm-{{ helm_version }}-linux-arm64.tar.gz.sha256sum
dest: /tmp
owner: root
group: root
mode: 0644
notify: Unarchive helm binary
when: helm_version != current_helm_version.stdout | default(false)
- name: Flush handlers
ansible.builtin.meta: flush_handlers
- name: Delete helm archive
ansible.builtin.file:
path: /tmp/helm-{{ helm_version }}-linux-arm64.tar.gz
state: absent
roles/node/handlers/main.yaml:
- name: Unarchive helm binary
ansible.builtin.unarchive:
src: /tmp/helm-{{ helm_version }}-linux-arm64.tar.gz
dest: /usr/local/bin
extra_opts: "--strip-components=1"
owner: root
group: root
mode: 0755
remote_src: true
You can do it with one single task:
---
- hosts: localhost
tasks:
- name: Install helm if not exists
unarchive:
src: https://get.helm.sh/helm-v3.11.0-linux-amd64.tar.gz
dest: /usr/local/bin
extra_opts: "--strip-components=1"
owner: root
group: root
mode: 0755
remote_src: true
args:
creates: /usr/local/bin/helm
When /usr/local/bin/helm exists, it will not execute the task.

how could I install a snap package via Ansible on an air-gapped system

Installing snap packages via Ansible on systems that are connected to internet is rather simple. EG:
- name: Install microk8s
become: yes
snap:
name: microk8s
classic: yes
channel: "{{ microk8s_version }}"
Now I would need to do the same on a set of nodes that are air-gapped (no direct connection to internet).
I can do a 'snap download' for the required packages, and move them to the target machine(s).
But then how to do this in Ansible? Is there any support for this? Or do I have to use the shell/command module ?
thx
I have not tested this, but this method works with other modules.
- name: install microk8s, file on local disk
become: yes
snap:
name: /path/to/file
using the hint of #Kevin C I was able to solve the problem using the following playbook
- name: copy microk8s snap to remote
copy:
src: "{{ item }}"
dest: "~/microk8s/"
remote_src: no
with_fileglob:
- "../files/microk8s/*"
- name: snap ack the new package
become: yes
shell: |
snap ack ~/microk8s/microk8s_1910.assert
snap ack ~/microk8s/core_10583.assert
- name: install microk8s, file on local disk
become: yes
snap:
name: "~/microk8s/core_10583.snap"
- name: install microk8s, file on local disk
become: yes
snap:
name: "~/microk8s/microk8s_1910.snap"
classic: yes
I hope this helps others also.
Would be nice to see this documented.
If you can get the packages onto the Ansible server, the following code will copy the file to the target(s). Something like the the following code should work.
- name: copy files/local/microk8s.deb
copy:
src: "files/local/microk8s.deb"
dest: "~/microk8s.deb"
remote_src: no
Where files/ is at the same level as the playbook.

Ansible: Handling temporary file idempotently and securely

In Ansible (RHEL 8 if it matters), I need to create a temporary file from a template with sensitive content. After a few other tasks are completed, it should be deleted. The temporary file is an answerfile for an installer that will run as a command. The installer needs a user name and password.
I can't figure out if there is a way to do this easily in Ansible.
The brute-force implementation of what I'm looking for would look similar to this:
- name: Create answer file
template:
src: answerfile.xml.j2
dest: /somewhere/answer.xml
owner: root
group: root
mode: '0600'
- name: Install
command: /somewhere/myinstaller --answerfile /somewhere/answer.xml
creates: /somewhereelse/installedprogram
- name: Delete answerfile
file:
path: /somewhere/answer.xml
state: absent
Of course, this code is not idempotent - the answer file would get created and destroyed on each run.
Is there a better way to do this?
Test the existence of the file. If it exists skip the block. For example
- stat:
path: /somewhereelse/installedprogram
register: st
- block:
- name: Create answer file
template:
src: answerfile.xml.j2
dest: /somewhere/answer.xml
owner: root
group: root
mode: '0600'
- name: Install
command: /somewhere/myinstaller --answerfile /somewhere/answer.xml
- name: Delete answerfile
file:
path: /somewhere/answer.xml
state: absent
when: not st.stat.exists
(not tested)
Taking the task "Delete answerfile" out of the block will make the code more secure. It will always make sure the credentials are not stored in the file. The task won't fail if the file is not present.
- stat:
path: /somewhereelse/installedprogram
register: st
- block:
- name: Create answer file
template:
src: answerfile.xml.j2
dest: /somewhere/answer.xml
owner: root
group: root
mode: '0600'
- name: Install
command: /somewhere/myinstaller --answerfile /somewhere/answer.xml
when: not st.stat.exists
- name: Delete answerfile
file:
path: /somewhere/answer.xml
state: absent
(not tested)
I think that this solution potentially represents a slight improvement on your solution.
With this solution the tasks which create and delete the answerfile will be skipped (rather than always run and reporting changed) if the program you're targeting is already installed.
I still don't love this solution as I don't really like skips.
# Try call the installedprogram. --version is arbitrary here.
# --help, or a simple `which installedprogram` could be alternatives.
- name: Try run installedprogram
command: '/somewhereelse/installedprogram --version'
register: installedprogram_exists
ignore_errors: yes
changed_when: False
# Only create answer file if installedprogram is not installed
- name: Create answer file
template:
src: answerfile.xml.j2
dest: /somewhere/answer.xml
owner: root
group: root
mode: '0600'
when: installedprogram_exists.rc != 0
- name: Install
command: /somewhere/myinstaller --answerfile /somewhere/answer.xml
creates: /somewhereelse/installedprogram
# Only delete answer file if installedprogram is not installed
- name: Delete answerfile
file:
path: /somewhere/answer.xml
state: absent
when: installedprogram_exists.rc != 0

Install rpm after copy, with ansible

I have an ansible playbook which will copy a file into a location on a remote server. It works fine. In this case, the file is an rpm. This is the way it works:
---
- hosts: my_host
tasks:
- name: mkdir /tmp/RPMS
file: path=/tmp/RPMS state=directory
- name: copy RPMs to /tmp/RPMS
copy:
src: "{{ item }}"
dest: /tmp/RPMS
mode: 0755
with_items:
[any_rpm-x86_64.rpm]
register: rpms_copied
Now, with the file successfully on the remote server, I need to start some new logic that will install the rpm that sits in /tmp/RPMS. I have run many different versions of the below (So this code is added onto the above block):
- name: install rpm from file
yum:
name: /tmp/RPMS/any_rpm-x86_64.rpm
state: present
become: true
I don't know if the formatting is incorrect, or if this is not the way. Can anyone advise as to how I can get the rpm in the directory /tmp/RPMS installed using a new few lines in the existing playbook?
Thanks.
I did not find this anywhere else, and it genuinely took me all of my working day to get to this point. For anyone else struggling:
- name: Install my package from a file on server
shell: rpm -ivh /tmp/RPMS/*.rpm
async: 1800
poll: 0
become_method: sudo
become: yes
become_user: root

Ansible Failed to set permissions on the temporary

I am using ansible to replace the ssh keys for a user on multiple RHEL6 & RHEL7 servers. The task I am running is:
- name: private key
copy:
src: /Users/me/Documents/keys/id_rsa
dest: ~/.ssh/
owner: unpriv
group: unpriv
mode: 0600
backup: yes
Two of the hosts that I'm trying to update are giving the following error:
fatal: [host1]: FAILED! => {"failed": true, "msg": "Failed to set
permissions on the temporary files Ansible needs to create when
becoming an unprivileged user (rc: 1, err: chown: changing ownership
of /tmp/ansible-tmp-19/': Operation not permitted\nchown: changing
ownership of/tmp/ansible-tmp-19/stat.py': Operation not
permitted\n). For information on working around this, see
https://docs.ansible.com/ansible/become.html#becoming-an-unprivileged-user"}
The thing is that these two that are getting the errors are clones of some that are updating just fine. I've compared the sudoers and sshd settings, as well as permissions and mount options on the /tmp directory. They are all the same between the problem hosts and the working ones. Any ideas on what I could check next?
I am running ansible 2.3.1.0 on Mac OS Sierra, if that helps.
Update:
#techraf
I have no idea why this worked on all hosts except for two. Here is the original playbook:
- name: ssh_keys
hosts: my_hosts
remote_user: my_user
tasks:
- include: ./roles/common/tasks/keys.yml
become: yes
become_method: sudo
and original keys.yml:
- name: public key
copy:
src: /Users/me/Documents/keys/id_rsab
dest: ~/.ssh/
owner: unpriv
group: unpriv
mode: 060
backup: yes
I changed the playbook to:
- name: ssh_keys
hosts: my_hosts
remote_user: my_user
tasks:
- include: ./roles/common/tasks/keys.yml
become: yes
become_method: sudo
become_user: root
And keys.yml to:
- name: public key
copy:
src: /Users/me/Documents/keys/id_rsab
dest: /home/unpriv/.ssh/
owner: unpriv
group: unpriv
mode: 0600
backup: yes
And it worked across all hosts.
Try to install ACL on remote host, after that execute ansible script
sudo apt-get install acl
You could try something like this:
- name: private key
become: true
become_user: root
copy:
src: /Users/me/Documents/keys/id_rsa
dest: ~/.ssh/
owner: unpriv
group: unpriv
mode: 0600
backup: yes
Notice the:
become: true
become_user: root
Check the "become" docs for more info
While installing the acl module works there is an alternative.
Add the line below to the defaults section of your ansible.cfg.
allow_world_readable_tmpfiles = True
Of better, just add it to the task that needs it with:
vars:
allow_world_readable_tmpfiles: true
A similar question with more details is Becoming non root user in ansible fails
I'm using ad-hoc and when I got into this problem, adding -b --become-user ANSIBLE_USER to my command fixes my problem.
example:
ansible all -m file -a "path=/etc/s.text state=touch" -b --become-user ansadmin
Of course, before this, I had given Sudo access to the user
If you give Sudo access to your user, you can write like this :
ansible all -m file -a "path=/var/s.text state=touch" -b --become-user root

Resources