Ansible - Mode 755 for directories and 644 for files recursively - file-permissions

I'd like to allow anyone to list and read all files in my directory tree, but I don't want to make the files executable :
dir
\subdir1
file1
\subdir2
file2
...
\subdirX
fileX
The following task makes my directories and files readable, but it makes all the files executable as well:
- name: Make my directory tree readable
file:
path: dir
mode: 0755
recurse: yes
On the other hand, if I choose mode 0644, then all my files are not executable, but I'm not able to list my directories.
Is it possible to set mode 755 for all directories and 644 for all files in a directory tree?

Since version 1.8, Ansible supports symbolic modes. Thus, the following would perform the task you want:
- name: Make my directory tree readable
file:
path: dir
mode: u=rwX,g=rX,o=rX
recurse: yes
Because X (instead of x) only applies to directories or files with at least one x bit set.

The Ansible file/copy modules don't give you the granularity of specifying permissions based on file type so you'd most likely need to do this manually by doing something along these lines:
- name: Ensure directories are 0755
command: find {{ path }} -type d -exec chmod -c 0755 {} \;
register: chmod_result
changed_when: "chmod_result.stdout != \"\""
- name: Ensure files are 0644
command: find {{ path }} -type f -exec chmod -c 0644 {} \;
register: chmod_result
changed_when: "chmod_result.stdout != \"\""
These would have the effect of recursing through {{ path }} and changing the permissions of every file or directory to the specified permissions.

Due to a "inadequate" implementation Ansible supports symbolic modes only partially (see explanation below).
Other than using the command line chmod, setting mode to u=rwX,g=rX,o=rX with Ansible will not allways be sufficient to get your files set to 644.
The resulting permissions will also depend on the original mode of the file!
As stated in the docs for chmod and already pointed out by some comments to other answers of this question:
If file permission for either u, g, or o is executable, then X will set the file permission also to x.
For example. If one file has mode 740 -rwxr-----, setting mode u=rwX,g=rX,o=rX with ansible, you will get 755 -rwxr-xr-x instead the expected 644 -rw-r--r--.
Despite that this is not what you wanted, it will make the file executable by group and others with unwanted security problems.
In those cases, with Ansible you will need two steps to set file the permissions to 644.
- file:
path: /path/to/dir
state: directory
recurse: yes
mode: '{{ item }}'
loop:
- '-x'
- 'u=rwX,g=rX,o=rX'
Note that if you wanted mode 744 for files you'll need u=rwx,g=rX,o=rX (first x lowercase!). This will work in Ansible as for the current implementation BUT this is not the way command line chmod works. See **Symbolic nodes with* chmod below.
Why is this so:
Ansible states in the function _symbolic_mode_to_octal to include things like: u=rw-x+X,g=r-x+X,o=r-x+X.
Nonetheless if the given mode is g=-x+X, ansible ignores the -x perm.
The function _symbolic_mode_to_octal iterates over the given permissions,
when it comes to X the function _get_octal_mode_from_symbolic_perms doesn't compare the requested permissions to the already applied but to the original ones, thus ignoring it:
This is probably a bug in Ansible.
The simplest and efficient way is to delegate to a shell command: As proposed in #BruceP's answer.
If for some reason you are averse to use 'workarounds' and need an ansible-ish way to solve the problem AND you don't care about performance you could try the following:
NOTE: This will take very, very long depending on the number of files and directories!
- name: example
hosts: 192.168.111.123
become: yes
gather_facts: no
vars:
path_to_dir: /path/to/dir
target_mode_for_directories: 755
target_mode_for_files: 644
tasks:
- name: collect list of directories '{{ path_to_dir }}'
find:
paths: '{{ path_to_dir }}'
recurse: yes
file_type: directory
register: found_directories
- name: set collected directories to mode '{{ target_mode_for_directories }}'
file:
dest: '{{ item.path }}'
mode: '{{ target_mode_for_directories }}'
loop: '{{ found_directories.files }}'
- name: collect list of files under '{{ path_to_dir }}'
find:
paths: '{{ path_to_dir }}'
recurse: yes
file_type: file
register: found_files
- name: set collected files to mode '{{ target_mode_for_files }}'
file:
dest: '{{ item.path }}'
mode: '{{ target_mode_for_files }}'
loop: '{{ found_files.files }}'
Symbolic modes with chmod
Remember that setting symbolic modes with chmod can be very tricky.
See the following examples that simply differ on the order of lowercase and uppercase X, i.e. u=X,g=X,o=x (o=lowercase x) vs. u=x,g=X,o=X (u=lowercase x) which results in 001 ---------x vs. 111 ---x--x--x:
$ sudo chmod -R 000 path/to/file; \
sudo chmod -R u=X,g=X,o=x path/to/file; \
sudo find path/to/file -printf ""%03m" "%M" "%p\\n"";
001 ---------x path/to/file
$ sudo chmod -R 000 path/to/file; \
sudo chmod -R u=x,g=X,o=X path/to/file; \
sudo find path/to/file -printf ""%03m" "%M" "%p\\n"";
111 ---x--x--x path/to/file
This is due to the fact that the perms are processed first for u, then for g and last for o. In the first example X won't apply for files because there is no x perm. In the second case X will apply for files after u=x has been set, thus setting both g=x and o=x

This worked for me:
- file:
path: dir
mode: a-x
recurse: yes
- file:
path: dir
mode: u=rwX,g=rX,o=rX
recurse: yes
Remove execution permission first from all, otherwise group and others get execution permission to files.
See chmod man pages about X-directive:
execute/search only if the file is a directory or already has execute
permission for some user (X)
Also this works:
- shell: "chmod -R a-x,u=rwX,g=rX,o=rX dir"
For some reason combination of these two does not work:
- file:
path: dir
mode: a-x,u=rwX,g=rX,o=rX
recurse: yes

Related

How to setup PATH to sh script in gitlab pipeline?

I have a bash script plan.sh that executes multiple times during the pipeline execution.
plan:
before_script:
- chmod +x ${CI_PROJECT_DIR}/gitlab/deploy/plan/plan.sh
script:
- cd ${FOLDER1}
- ${CI_PROJECT_DIR}/gitlab/deploy/plan/plan.sh
- cd ${FOLDER2}
- ${CI_PROJECT_DIR}/gitlab/deploy/plan/plan.sh
- cd ${FOLDER3}
- ${CI_PROJECT_DIR}/gitlab/deploy/plan/plan.sh
I would like to execute script using just a file name instead of full path. So I am setting the PATH environment variable.
plan:
before_script:
- export PATH=${CI_PROJECT_DIR}/gitlab/deploy/plan:$PATH
- chmod +x plan.sh
script:
- cd ${FOLDER1}
- plan.sh
- cd ${FOLDER2}
- plan.sh
- cd ${FOLDER3}
- plan.sh
However this throws error
$ chmod +x plan.sh
chmod: cannot access 'plan.sh': No such file or directory
PATH is used to look up the location of binaries when executing them by their name only.
PATH is not used to resolve arbitrary file arguments passed to another binary. For chmod this means that you still have to provide the full path (relative or absolute) or cd to the correct directory first.
Here's another example that does/not work:
PATH=/bin
cd /
ls bin/ls # works
ls /bin/ls # works
bin/ls bin/ls # works
bin/ls /bin/ls # works
/bin/ls /bin/ls # works
/bin/ls ls # error
bin/ls ls # error
ls ls # error

change directory in ansible playbook

i have ansible playbook and i want to look at specified directory (with ls command). the directory is in the root, i check with the pwd command :
/root/git/ansible/data/example
and the ansible the task looks like this :
name: open directory
become: yes
become_user: root
command: chdir=/data/example ls
after i execute the playbook i get this error :
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Unable to change directory before execution: [Errno 2] No such file or directory: '/data/example'"}
while the directory actually does exist. i also tried this :
name: open directory
become: yes
become_user: root
shell:
cmd: ls
chdir: /data/example
and got the same error. can anyone help?
Given the tree
shell> tree /data/example
/data/example
├── file1
├── file2
└── file3
0 directories, 3 files
all forms of command and shell
- hosts: localhost
tasks:
- command:
chdir: /data/example
cmd: ls
register: result
tags: t1
- shell:
chdir: /data/example
cmd: ls
register: result
tags: t2
- command: chdir=/data/example ls
register: result
tags: t3
- shell: chdir=/data/example ls
register: result
tags: t4
- debug:
var: result.stdout
tags: always
work as expected
result.stdout:
file1
file2
file3

chroot jail + ssh key refused

I'm trying to create a chroot jail on an EC2 Instance (Amazon AMI Linux). I've tried the instructions here: https://allanfeid.com/content/creating-chroot-jail-ssh-access
My EC2 instance has an SSH key called demosystems.pem and I can successfully login (as expected) via ec2-user.
$ groupadd sshusers
$ adduser -g sshusers janedoe
$ mkdir -p /var/jail/{dev,etc,lib,lib64,usr,bin}
$ mkdir -p /var/jail/usr/bin
$ chown root.root /var/jail
$ mknod -m 666 /var/jail/dev/null c 1 3
$ cd /var/jail/etc
$ cp /etc/ld.so.cache .
$ cp /etc/ld.so.conf .
$ cp /etc/nsswitch.conf .
$ cp /etc/hosts .
$ cd /var/jail/usr/bin
$ cp /usr/bin/ls .
$ cp /usr/bin/bash .
$ cd /sbin
$ wget -O l2chroot http://www.cyberciti.biz/files/lighttpd/l2chroot.txt
$ chmod +x l2chroot
$ l2chroot ls
$ l2chroot bash
$ nano /etc/ssh/sshd_config
Match group sshusers
ChrootDirectory /var/jail/
X11Forwarding no
AllowTcpForwarding no
I've also tried using the geerlingguy.ansible-role-ssh-chroot-jail Ansible role:
---
- name: Create chroot jail
hosts: chroot
become: yes
vars:
- ssh_chroot_jail_users:
name: janedoe
homedir: /home/janedoe
shell: /bin/bash
roles:
- geerlingguy.ssh-chroot-jail
In both cases, the error I get when trying to SSH in as janedoe is:
No supported authentication methods available (server sent: public key). Server refused our key.
Therefore, I believe the problem to be somewhere in the final step. Setting the Match group above.
Any thoughts / ideas?
.ssh directory and authorized_keys file doesn't exist. Solution with Ansible is to create two post tasks:
post_tasks:
- name: Create SSH Directory
file:
path: /home/janedoe/.ssh
state: directory
- name: Copy SSH from ec2-user to janedoe
copy:
remote_src: yes
src: /home/ec2-user/.ssh/authorized_keys
dest: /home/janedoe/.ssh/authorized_keys
owner: janedoe

Shell commands with Ansible

I would like to use Ansible in order to execute several commands. In my case, I would like to create a file system. Here my code:
- name: Create file system
raw: |
mkdir -p {{ out }} {{ scripts }} {{ sql }}
chown -R postgres:postgres {{ root_dir }}
lvcreate -n lv_test -L 5G data_vg
mkfs.ext4 {{ fs }}
mount {{ fs }} {{ home }}
when: (target_app == "app")
I want to do all these actions by using the root user. Any ideas?
You can just use the shell module similar to this:
- name: set a fact
hosts: localhost
tasks:
- name: add users
become: yes
become_user: root
shell: |
mkdir -p /tmp/blah/moreblah;
chown -R root:root /tmp/blah/moreblah
That being said there are modules like file that can do some of the lifting for you. E.g.
# create a directory if it doesn't exist
- file:
path: /etc/some_directory
state: directory
group: root
owner: root
mode: 0755
recurse: yes
In general, if you're using shell to perform everyday UNIX commands you're probably doing it wrong.
References
https://docs.ansible.com/ansible/latest/modules/file_module.html
https://docs.ansible.com/ansible/latest/modules/shell_module.html

Why is Ansible task being skipped?

I have 2 ansible tasks that I am trying to run in a CIS hardening script on an Ubuntu 14.04 Server.
The first task is
- name: 8.1.12 Collect Use of Privileged Commands (Scored)
shell: /usr/bin/find {/usr/local/sbin,/usr/local/bin,/sbin,/bin,/usr/sbin,/usr/bin} -xdev \( -perm -4000 -o -perm -2000 \) -type f | awk '{print "-a always,exit -F path=" $1 " -F perm=x -F auid>=500 -F auid!=4294967295 -k privileged" }'
register: privileged_programs
tags:
- scored
- section8.1.12
This is supposed to register a list of privileged programs to be used in the next task. If I copy the command above onto the Ubuntu VM and run it, I get a long list of programs just like I should.
The second task is this:
- name: 8.1.12 Collect Use of Privileged Commands (Scored)
lineinfile: dest=/etc/audit/audit.rules line="{{item}}" insertafter=EOF state=present
with_items: privileged_programs.stdout_lines
when: privileged_programs is defined and privileged_programs.stdout_lines|length > 0
notify: restart auditd
tags:
- scored
- section8.1.12
It should fire if any results are registered but so far I have not been able to get it to run. It is skipped every time I try to run the 2 tasks. I am assuming that the privileged_programs variable is not being stored or passed correctly.
Note: I tried changing the first task from shell to command but I then got an error "stderr: /usr/bin/find: paths must precede expression"
Note2: I also checked in the etc/audit/audit.rules and verified that the privileged programs are not contained therein yet.
Edit: I added a debug in between the two tasks to output var=privileged_programs. Here is part of it that I think may indicate part of the issue:
"stderr": "/usr/bin/find: `{/usr/local/sbin,/usr/local/bin,/sbin,/bin,/usr/sbin,/usr/bin}': No such file or directory",
"stdout": "",
"stdout_lines": [],
"warnings": []
Anyone know why this would be?
Thanks in advance!
Bourne shell has some issue with the syntax. Works fine in Bash.
I made it working. Try the following syntax.
shell: /usr/bin/find /usr/local/sbin /usr/local/bin /sbin /bin /usr/sbin /usr/bin
I am not sure if this is the case, but ansible documentation states: If two handler tasks have the same name, only one will run. *
Try changing second task name.
my ansible skipping was related to a "when:" in state not matching the hostname.
e.g.
- set_fact:
efs_mount_target: "10.10.10.1"
when: ansible_hostname == 'server-01'

Resources