I can't write to /etc with successful privilege escalation using become. I can however write to it directly from the host so sounds like become isn't behaving as expected.
Playbook (I've omitted other tasks for brevity but all tasks in this playbook require escalation to postgres user and are successful)
- name: Playbook Control
hosts: all
become: yes
become_user: postgres
tasks:
- name: Debug Perms
shell: ls -lrt /etc/pgbackrest.conf
- name: Initialize pgbackrest.conf
copy:
src: pgbackrest_init
dest: /etc/pgbackrest.conf
Inventory
hostname.net
[all:vars]
ansible_connection=ssh
ansible_user=pg_deployment
Playbook Command
ansible-playbook -vvv --vault-password-file=.vault_pw -i hosts/hosts playbook_control.yml
Using vault to store pg_deployment ssh password.
Debug Perms Output
"stdout": "-rw-r--r-- 1 postgres postgres 82 Aug 24 2021 /etc/pgbackrest.conf"
Initialize pgbackrest.conf Output
"msg": "Destination /etc not writable"
In the -vvv block preceding the task Initialize pgbackrest.conf, I see escalation succeeded.
Related
I am new to ansible.
i am trying to create a role where i start the playbook as root and then in the next play i switch to a different user and continue. The following files are within the role itself.
---
# tasks file for /etc/ansible/roles/dashmn
#
- name: create users logged in as root
remote_user: root
import_tasks: whoami.yml
import_tasks: create_users.yml
import_tasks: set_sudoer.yml
- name: log in as dashadmin
remote_user: dashadmin
become: true
import_tasks: whoami.yml
import_tasks: disable_rootlogin.yml
import_tasks: update_install_reqs.yml
import_tasks: configure_firewall.yml
import_tasks: add_swap.yml
i added a sudoer task that adds users to /etc/sudoer.d
---
- name: set passwordless sudo
lineinfile:
path: /etc/sudoers
state: present
regexp: '^%sudo'
line: '%sudo ALL=(ALL) NOPASSWD: ALL'
validate: 'visudo -cf %s'
I created a deploy.yml that uses the role i created as follows.
---
- hosts: test-mn
roles:
- dashmn
when i syntax-check the deploy.yml
[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names
by default, this will change, but still be user configurable on deprecation. This feature will be removed in
version 2.10. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
[WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details
[WARNING]: While constructing a mapping from /etc/ansible/roles/dashmn/tasks/main.yml, line 4, column 3, found
a duplicate dict key (import_tasks). Using last defined value only.
[WARNING]: While constructing a mapping from /etc/ansible/roles/dashmn/tasks/main.yml, line 10, column 3, found
a duplicate dict key (import_tasks). Using last defined value only.
Any help on how to organize this to make it better would be appreciated.
Now, my problem is that if in the tasks file i remove the plays themselves and just leave the import_tasks everything works but its not using the user dashadmin, its using root.
i would like to create the users and then only ever login as dashadmin and work as dashadmin.
I also get an error
FAILED! => {"msg": "Missing sudo password"}
something is clearly wrong, just not sure where ive gone wrong.
Here is /etc/sudoers file
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults env_reset
Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"
# Host alias specification
# User alias specification
# Cmnd alias specification
# User privilege specification
root ALL=(ALL:ALL) ALL
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL) NOPASSWD: ALL
# See sudoers(5) for more information on "#include" directives:
#includedir /etc/sudoers.d
First of all, the way you defined import_tasks will basically executes the last import_tasks only as the warning says.
Secondly, remote_user is used for logging in to defined host(s) but if you want to login as a user then execute tasks using a different user then you need to define become_user. By default, become_user is set to root.
So probably below is how you can change the role's import_tasks:
/etc/ansible/roles/dashmn/tasks/main.yml
- name: create users logged in as root
block:
- import_tasks: whoami.yml
- import_tasks: create_users.yml
- import_tasks: set_sudoer.yml
remote_user: root
- name: log in as dashadmin
block:
- import_tasks: whoami.yml
- import_tasks: disable_rootlogin.yml
- import_tasks: update_install_reqs.yml
- import_tasks: configure_firewall.yml
- import_tasks: add_swap.yml
remote_user: dashadmin
become: yes
Refer privilege escalation for more details.
Q: "Change remote_user within main.yml in a role"
Short answer: See the example in "Play 3" on how to change remote_user for each task.
Details: Keyword remote_user can be used in all playbook's objects: play, role, block, task. See Playbook Keywords.
The best practice is to connect to the remote host as an unprivileged user and escalate the privilege. For example,
- name: Play 1
hosts: test_01
remote_user: user1
become: true
tasks:
- command: whoami
register: result
- debug:
var: result.stdout
gives
ok: [test_01] =>
result.stdout: root
Without the escalation of priviledge the tasks will be executed by the remote_user at the remote host. For example,
- name: Play 2
hosts: test_01
remote_user: user1
tasks:
- command: whoami
register: result
- debug:
var: result.stdout
gives
ok: [test_01] =>
result.stdout: user1
It's possible to declare the remote_user for each task. For example
- name: Play 3
hosts: test_01
remote_user: user1
tasks:
- command: whoami
register: result
- debug:
var: result.stdout
- command: whoami
remote_user: user2
register: result
- debug:
var: result.stdout
gives
ok: [test_01] =>
result.stdout: user1
ok: [test_01] =>
result.stdout: user2
All plays can be put into one playbook.
Example of sudoers file
root.test_01# cat /usr/local/etc/sudoers
...
#includedir /usr/local/etc/sudoers.d
admin ALL=(ALL) NOPASSWD: ALL
user1 ALL=(ALL) NOPASSWD: ALL
user2 ALL=(ALL) NOPASSWD: ALL
I have defined the following in my ansible.cfg
# default user to use for playbooks if user is not specified
# (/usr/bin/ansible will use current user as default)
remote_user = ansible
However I have a playbook bootstrap.yaml where I connect with root rather than ansible
---
- hosts: "{{ target }}"
become: no
gather_facts: false
remote_user: root
vars:
os_family: "{{ osfamily }}}"
roles:
- role: papanito.bootstrap
However it seems that remote_user: root is ignored as I always get a connection error, because it uses the user ansible instead of root for the ssh connection
fatal: [node001]: UNREACHABLE! => {"changed": false,
"msg": "Failed to connect to the host via ssh:
ansible#node001: Permission denied (publickey,password).",
"unreachable": true}
The only workaround for this I could find is calling the playbook with -e ansible_user=root. But this is not convenient as I want to call multiple playbooks with the site.yaml, where the first playbook has to run with ansible_user root, whereas the others have to run with ansible
- import_playbook: playbooks/bootstrap.yml
- import_playbook: playbooks/networking.yml
- import_playbook: playbooks/monitoring.yml
Any suggestions what I am missing or how to fix it?
Q: "remote_user: root is ignored"
A: The playbook works as expected
- hosts: test_01
gather_facts: false
become: no
remote_user: root
tasks:
- command: whoami
register: result
- debug:
var: result.stdout
gives
"result.stdout": "root"
But, the variable can be overridden in the inventory. For example with the inventory
$ cat hosts
all:
hosts:
test_01:
vars:
ansible_connection: ssh
ansible_user: admin
the result is
"result.stdout": "admin"
Double-check the inventory with the command
$ ansible-inventory --list
Notes
It might be also necessary to double-check the role - role: papanito.bootstrap
See Controlling how Ansible behaves: precedence rules
I faced a similar issue, where ec2 instance required different username to ssh with. You could try with below example
- import_playbook: playbooks/bootstrap.yml
vars:
ansible_ssh_user: root
Try this
Instead of “remote_user: root”use “remote_user: ansible” and additional “become: yes” ,”become_user: root”,”become_method: sudo or su”
I'm trying to create an ansible playbook that will work in my current work environment. I login to servers as user "myuser" using ssh keys. I was never given a password, so I don't know it. Most of the commands I run are executed as a different non-root user - e.g. "appadmin". I become these users via "sudo su - appadmin", since I don't have the passwords for this user either.
Different variations I've tried either complain "sudo: a password is required" or time out after 12 seconds. I'll show this second example.
The playbook is very simple:
---
- hosts: sudo-test
gather_facts: False
remote_user: myuser
become: yes
become_user: appadmin
tasks:
- name: who
shell: whoami > qwert.txt
My host entry is as follows:
[sudo-test]
appserver.example.com ansible_become_method=su ansible_become_exe="sudo su"
This is the error I get:
pablo#host=> ansible-playbook test_sudo.yml
PLAY [sudo-test] ****************************************************************************************************
TASK [who] **********************************************************************************************************
fatal: [appserver.example.com]: FAILED! => {"msg": "Timeout (12s) waiting for privilege escalation prompt: "}
to retry, use: --limit #/home/pablo/ansible_dir/test_sudo.retry
PLAY RECAP **********************************************************************************************************
appserver.example.com : ok=0 changed=0 unreachable=0 failed=1
At this point I agree that the playbook and inventory are configured correctly. I believe the issue is that /etc/sudoers doesn't permit my "appadmin" user to run in a way that allows me to leverage ansible's ability to become another user. This thread describes a similar scenario - and limitation.
The relevant section of /etc/sudoers looks like this:
User myuser may run the following commands on this host:
(root) NOPASSWD: /bin/su - appadmin
It seems I would have to have the sysadmin change this to:
User myuser may run the following commands on this host:
(root) NOPASSWD: /bin/su - appadmin *
Does this sound right?
i dont find any issue with yaml, infact i got it tested in my ansible2.8 environment.
---
- hosts: node1
gather_facts: False
remote_user: ansible
become: yes
become_user: testuser
tasks:
- name: who
shell: whoami
register: output
- debug: var=output
and inventory:
[node1]
node1.example.com ansible_become_method=su ansible_become_exe="sudo su"
output:
TASK [debug] ****************************************************************************************************************************
ok: [node1.example.com] =>
I would request you to increase ssh timer (uncomment timeout line and set it to 60, whatever seconds you wish) in ansible.cfg file and observer this scenario.
# SSH timeout
#timeout = 300
Try this one:
- hosts: application
become: yes
become_exe: "sudo su - appadmin"
become_method: su
tasks:
I'm logging into another computer as the unprivileged user ansible and I'd like to have ansible enable a systemd user service for the user bob.
Without ansible, the solution would be ssh bob#machine followed by systemctl --user enable service
However, with ansible, there are two problems:
Newer versions of ansible will refuse to become the unprivileged user bob if already logged in as another unprivileged user (ansible).
Even if this worked, dbus would not be started and systemctl would not be able to talk to systemd (if I understood this correctly).
A horribly ugly workaround would be to execute the shell command, have the remote host ssh into itself as bob and run the systemctl raw command there.
Is there a nicer way to get this done?
Both options are feasible.
remote_user: bob
- hosts: test_01
become: no
remote_user: admin
tasks:
- command: whoami
register: result
- debug:
var: result.stdout
- command: whoami
remote_user: bob
register: result
- debug:
var: result.stdout
gives (abridged)
result.stdout: admin
result.stdout: bob
pipelining = true quoting from Becoming an Unprivileged User
Use pipelining. When pipelining is enabled, Ansible doesn’t save the module to a temporary file on the client. Instead, it pipes the module to the remote python interpreter’s stdin. Pipelining does not work for python modules involving file transfer (for example: copy, fetch, template), or for non-python modules.
- hosts: test_01
become: no
remote_user: admin
tasks:
- command: whoami
register: result
- debug:
var: result.stdout
- command: whoami
become_user: bob
become_method: sudo
become: yes
register: result
- debug:
var: result.stdout
gives (abridged) the same result
result.stdout: admin
result.stdout: bob
with
shell> grep pipe ansible.cfg
pipelining = true
ansible 192.168.1.115 -s -m shell -a "echo -e 'oldpassword\nnewpassword\nnewpassword' | passwd myuser" -u myuser --ask-sudo-pass
I would like to update existing user with new password, I had tried this command, but it doesn't work
appreciate any Tips !
You can leverage the user module to quickly change the password for desired account. Ansible doesn’t allow you to pass a cleartext password to user module so you have to install a password hashing library to be leveraged by Python.
To install the library:
sudo -H pip install passlib
Then simply exexute your command:
ansible 192.168.1.115 -s -m user -a "name=root update_password=always password={{ yourpassword | password_hash('sha512') }}" -u myuser --ask-sudo-pass
Hope that help you
Create your shadow password (linux) with
python -c 'import crypt; print crypt.crypt("YourPassword", "$6$random_salt")'
create
update_pass.yml
execute your ansible-playbook with sudoer (bash)
ansible-playbook update_pass.yml --become --become-method='sudo' --ask-become-pass
Update password for a list of hosts using dynamic variables:
In your inventory file set a variable (pass) as the following:
ip_1# ansible_user=xxxxxx ansible_ssh_pass=xxxx ansible_sudo_pass=xxx pass='aaaa'
ip_2# ansible_user=xxxxxx ansible_ssh_pass=xxxx ansible_sudo_pass=xxx pass='bbbb'
Now in the playbook we make a backup of the shadow file and set cron task to restore the shadow file in case something went wrong than we update the password:
- hosts: your_hosts
gather_facts: no
tasks:
- name: backup shadow file
copy:
src: /etc/shadow
dest: /etc/shadaw.org
become: yes
- name: set cron for backup
cron:
name: restore shadow
hour: 'AT LEAST GIVE YOURSELF ONE HOUR TO BE ABLE TO CALL THIS OFF'
minute: *
job: "yes | cp /tmp/shadow /etc/"
become: yes
- name: generate hash pass
delegate_to: localhost
command: python -c "from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt('{{pass}}')"
register: hash
- debug:
var: hash.stdout
- name: update password
user:
name: xxxxxx
password: '{{hash.stdout}}'
become: yes
Now we create a new playbook to call off cron task we use the new password for authentication and if authentication failed cron will remain active and restore the old password.
hosts file:
ip_1# ansible_user=xxxxxx ansible_ssh_pass=aaaa ansible_sudo_pass=aaaa
ip_2# ansible_user=xxxxxx ansible_ssh_pass=bbbb ansible_sudo_pass=bbbb
the playbook:
- hosts: your_hosts
gather_facts: no
tasks:
- name: cancel cron task
cron:
name: restore shadow
state: absent
!!Remember:
pass variable contain your password so you may consider using vault.
Give yourself time when setting cron for backup to be able to call it of (second playbook).
In worst case cron will restore the original password.
You need to have passlib installed in your ansible server.