saltstack / saltstack/salt

[BUG] Default parameters for file.managed can cause unintended OS-breaking issues

Open
#64,403 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs-triage
Dominant language
Python
Stars
15.7k
Forks
5.6k
Avg merge
2d 44m
Merged PRs (30d)
80

Description

Description
When using file.managed, the default of follow_symlinks is true.

Which means that if (somehow) the intended destination is already symlinked to /dev/null - you will end up OVERWRITING the destination, leaving the symlink intact.

In our case, this happened due to someone using systemctl mask on a service, with a unit file residing in /etc/systemd/system.

What systemd does when masking, is create a symlink from /etc/systemd/system/<unit>.<type> to /dev/null.

The end result, was a broken /dev/null which can lead to plenty of (obvious and dangerous) problems.

Setup

systemd application service:
  file.managed:
    - name: /etc/systemd/system/application.service
    - source: salt://application/files/systemd/system/application.service
    - mode: "0644"
    - user: root
    - group: root

Steps to Reproduce the behavior

> systemctl mask application
Unit application.service does not exist, proceeding anyway.
Created symlink /etc/systemd/system/application.service → /dev/null.

... and run Salt with the above SLS.

Expected behavior
Unsure what the viable Expected Behaviour here is to "resolve" it, but here are some that comes to mind;

  1. The symlink should be removed, and replaced by a file (/dev/null being identified as a null device)
  2. The output should cleanly be written to /dev/null and Saltstack should fail the operation with an error message

There is probably be legitimate use-cases where you want to follow a symlink and write to a file, but in that case Saltstack should identify that the destination is already a normal file (where mode should be applied) or a socket (where it should fail if mode is defined).

... maybe follow_symlinks being true is a bad default for file.managed?

Ansible for example, does not default to true. It used to (until they found out it was insane), and changed it a long time ago.

Additional Details
When simulating the same "approach" using Ansible's ansible.builtin.template module, the null device is not corrupted.

> mknod -m 0666 /tmp/null c 1 3
> ln -s /tmp/null /tmp/testfile

> echo "testing {{ ansible_host }}" > /tmp/testfile.tmpl
> ansible '*' -c local -i localhost, -m template -a 'src=/tmp/testfile.tmpl dest=/tmp/testfile mode=0644 owner=root group=root'
localhost | CHANGED => {
    "changed": true,
    "checksum": "9dd8f3c5506d4aea83c1327519a3883ac726871b",
    "dest": "/tmp/testfile",
    "gid": 0,
    "group": "root",
    "md5sum": "08426641dc49c48cdf4bf80330c3f708",
    "mode": "0644",
    "owner": "root",
    "size": 18,
    "src": "/root/.ansible/tmp/ansible-tmp-1685699801.9378633-4307-217502886448945/source",
    "state": "file",
    "uid": 0
}

> cat /tmp/testfile
testing localhost

> stat /tmp/null
  File: /tmp/null
  Size: 0               Blocks: 0          IO Block: 4096   character special file
Device: 0,75    Inode: 154         Links: 1     Device type: 1,3
Access: (0666/crw-rw-rw-)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2023-06-02 11:51:49.390389817 +0200
Modify: 2023-06-02 11:51:49.390389817 +0200
Change: 2023-06-02 11:51:49.390389817 +0200
 Birth: -

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the file.managed state entry point and reproduce the systemctl mask scenario described in the issue, where the destination points to /dev/null. Determine the safe handling for follow_symlinks and define completion by ensuring the null device is not corrupted while legitimate symlink behavior remains supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.