ansible-role-cis-amazon-linux
ansible-role-cis-amazon-linux copied to clipboard
6.2.6 ignores nonexistent directory in root's PATH
Root's PATH in Amazon Linux (and CentOS, and likely other similar distributions) by default has /root/bin
in it, which doesn't exist:
[root@ip-192-168-3-209 ~]# env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@ip-192-168-3-209 ~]# ls -ld /root/bin
ls: cannot access /root/bin: No such file or directory
That should trigger an action in item 6.2.6, but it skips over. Looks like ansible's script
module doesn't set environment as it's expected in the role.
PLAY [ip-192-168-3-209] ***************************************************************************
TASK [6.2.6 - Audit root PATH Integrity] **********************************************************
ok: [ip-192-168-3-209]
TASK [6.2.6 - Ensure root PATH Integrity] *********************************************************
skipping: [ip-192-168-3-209]
TASK [6.2.6 - Ensure root PATH Integrity] *********************************************************
skipping: [ip-192-168-3-209]
PLAY RECAP ****************************************************************************************
ip-192-168-3-209 : ok=1 changed=0 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
Running audit_6.2.6.sh
directly on the hosts provides expected results.
[root@ip-192-168-3-209 ~]# /tmp/audit_6.2.6.sh
/root/bin is not a directory
Hi Sergey (@arronax), thanks a lot for creating the issue and submitting the pull request.
This is a tricky situation.
Quick question, have you thought about trying to run the script as sudo
and see if that works?
I think, the reason for all of this is the different invocation of bash
. I'll check this, but it appears that ansible's script
gets interactive non-login shell, while we need login shell in order to get full env present.
Quoting man bash
When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.
Behavior can be shown using ssh
or sudo
.
$ sudo /tmp/audit_6.2.6.sh
$ sudo bash /tmp/audit_6.2.6.sh
$ sudo bash -l /tmp/audit_6.2.6.sh
/root/bin is not a directory
$ ssh -q ip-192-168-3-209 "/tmp/audit_6.2.6.sh"
$ ssh -q ip-192-168-3-209 "bash /tmp/audit_6.2.6.sh"
$ ssh -q ip-192-168-3-209 "bash -l /tmp/audit_6.2.6.sh"
/home/ec2-user/.local/bin is not a directory
/home/ec2-user/bin is not a directory
Better yet, it can be shown directly through PATH (script has single env | grep PATH
line)
$ sudo /tmp/env-path.sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
$ sudo bash /tmp/env-path.sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
$ sudo bash -l /tmp/env-path.sh
PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
Some trivial testing through ansible.
$ cat env.sh
#!/bin/bash
env | grep PATH
tasks:
- name: Copy test script to the remote machine
copy:
src: env.sh
dest: /tmp/env.sh
mode: 0700
- name: Get environment using `script`
script: env.sh
register: script_out
- name: debug print output
debug:
msg: "{{ script_out.stdout }}"
- name: Get environment using `script` and do become
script: env.sh
become: yes
register: script_become_out
- name: debug print output
debug:
msg: "{{ script_become_out.stdout }}"
- name: Get environment using `command`
command: /tmp/env.sh
register: command_out
- name: debug print output
debug:
msg: "{{ command_out.stdout }}"
- name: Get environment using `command` and do become
command: /tmp/env.sh
become: yes
register: command_become_out
- name: debug print output
debug:
msg: "{{ command_become_out.stdout }}"
- name: Get environment using `shell`
shell: /tmp/env.sh
register: shell_out
- name: debug print output
debug:
msg: "{{ shell_out.stdout }}"
- name: Get environment using `shell` and do become
shell: /tmp/env.sh
become: yes
register: shell_become_out
- name: debug print output
debug:
msg: "{{ shell_become_out.stdout }}"
- name: Get environment using `command` with bash invocation
command: bash /tmp/env.sh
register: command_bash_out
- name: debug print output
debug:
msg: "{{ command_bash_out.stdout }}"
- name: Get environment using `command` with bash invocation and do become
command: /tmp/env.sh
become: yes
register: command_become_bash_out
- name: debug print output
debug:
msg: "{{ command_become_bash_out.stdout }}"
- name: Get environment using `command` with bash login invocation
command: bash -l /tmp/env.sh
register: command_bash_login_out
- name: debug print output
debug:
msg: "{{ command_bash_login_out.stdout }}"
- name: Get environment using `command` with bash login invocation and do become
command: bash -l /tmp/env.sh
become: yes
register: command_become_bash_login_out
- name: debug print output
debug:
msg: "{{ command_become_bash_login_out.stdout }}"
PLAY [ip-192-168-3-209] ******************************************************************************************
TASK [Copy test script to the remote machine] ********************************************************************
ok: [ip-192-168-3-209]
TASK [Get environment using `script`] ****************************************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/usr/local/bin:/usr/bin\r\n"
}
TASK [Get environment using `script` and do become] **************************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/sbin:/bin:/usr/sbin:/usr/bin\r\n"
}
TASK [Get environment using `command`] ***************************************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/usr/local/bin:/usr/bin"
}
TASK [Get environment using `command` and do become] *************************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/sbin:/bin:/usr/sbin:/usr/bin"
}
TASK [Get environment using `shell`] *****************************************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/usr/local/bin:/usr/bin"
}
TASK [Get environment using `shell` and do become] ***************************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/sbin:/bin:/usr/sbin:/usr/bin"
}
TASK [Get environment using `command` with bash invocation] ******************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/usr/local/bin:/usr/bin"
}
TASK [Get environment using `command` with bash invocation and do become] ****************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/sbin:/bin:/usr/sbin:/usr/bin"
}
TASK [Get environment using `command` with bash login invocation] ************************************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/ec2-user/.local/bin:/home/ec2-user/bin"
}
TASK [Get environment using `command` with bash login invocation and do become] **********************************
changed: [ip-192-168-3-209]
TASK [debug print output] ****************************************************************************************
ok: [ip-192-168-3-209] => {
"msg": "PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin"
}
PLAY RECAP *******************************************************************************************************
ip-192-168-3-209 : ok=21 changed=10 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
I do not think that ansible will support login shells any time soon, and I am personally not sure that's required, as there are workarounds available. Environment variables and scripts are sometimes tricky, and can break whatever ansible is running.
Some ansible team answers to the related issues.
https://github.com/ansible/ansible/issues/4854#issuecomment-37657751 https://github.com/ansible/ansible/issues/29637#issuecomment-380672737
Thanks again @arronax for doing all the research and hardwork.
One tiny request. Could you please send the pull request to build
instead of master
.
We are trying to follow this approach so to allow others to test run build
and report any bugs they find before we merge the changes in master
which will become prodcuction version.
@arronax Please ignore the request, was able to modify the PR to pull into build