0

I have a need to edit lines in /etc/fstab and add/change fsoptions to lines where a matching volume is found. I have tried using sed and putting found blocks into registers to place them back, but am finding this one a challenge. Perhaps sed is not the best tool - I tried augeas and while it appends it did not replace and also failed if a matching line wasn't found but needed to be added (eg was only visible using the 'mount' command such as /dev/shm).

for example, if /etc/fstab has a line:

/dev/mapper/VolGroup1-tmp /tmp                    xfs     dev,nosuid 0 0

I want to make it

/dev/mapper/VolGroup1-tmp /tmp                    xfs     nodev,nosuid,noexec 0 0
  1. Note that the volume group in the first block could be any name
  2. The KEYWORD in the string would (in this example) be /tmp (but careful not to match /var/tmp unless specified)
  3. The filesystem could be anything (not necessarily xfs)
  4. Any 'exec' or 'suid' present (for example) needs to be replaced if present and even if not, 'noexec' or 'nosuid' inserted.
  5. The trailing '0 0' needs to be retained.

I'm sure I am missing an easy way to do this. Using 'mount -o remount,noexec /tmp' doesn't write to /etc/fstab so I guess the only way to make changes persistent is to edit /etc/fstab directly?

I am actually going to wrap the solution in Puppet. The augeas example below fails in 2 regards:

  • if a line (eg /dev/shm) does not exist in /etc/fstab, it fails

  • if a line exists and has 'exec' it appends 'noexec' but leaves the exec also

      augeas{ "/etc/fstab - ${opt} on ${mount}":
    context => '/files/etc/fstab',
    changes => [
      "ins opt after /files/etc/fstab/*[file = '${mount}']/opt[last()]",
      "set *[file = '${mount}']/opt[last()] ${opt}",
    ],
    onlyif  => "match *[file = '${mount}']/opt[. = '${opt}'] size == 0",
    notify  => Exec["remount_${mount}_${opt}"],
    

    }

xit
  • 135
  • 1
  • 12
  • 1
    Consider using `Mount` resources instead of Augeas or weird `Exec`s. Of course, this follows a different paradigm: a prescriptive one instead of a reactive one. The former kind are much better suited for central infrastructure management such as Puppet provides. – John Bollinger Oct 04 '19 at 12:06
  • That's an option, however I want to simply append any fsoptions, not set them entirely, or set the fstype. Kind of a search and replace or create if not exists. Seems like it should be simple to catch strings and do insertion or substitution, but so far augeas (as horrible as it is) seems like the closest method. Hence my seeking some 'sed foo'. – xit Oct 04 '19 at 13:51
  • Like I said: a prescriptive paradigm. As a matter of best practices, setting all the options is what you *should* be doing. That it's easier, too, is just gravy. Nevertheless, a `sed`-based solution should be possible. I'll see what I can do. – John Bollinger Oct 04 '19 at 23:53
  • I do agree with you however the issue is that the first string (fsname) could be anything, as could the fstype. I also want to preserve existing fsoptions that don't clash with new desired settings. If you can think of a method to do that with Puppet Mount or other method I agree that it would be better. – xit Oct 05 '19 at 03:11
  • Alternatively some refinement to the Puppet Augeus block above would be appreciated. – xit Oct 05 '19 at 03:25

1 Answers1

3

I'm sure I am missing an easy way to do this. Using 'mount -o remount,noexec /tmp' doesn't write to /etc/fstab so I guess the only way to make changes persistent is to edit /etc/fstab directly?

There is no for-purpose CLI for modifying mount records in /etc/fstab, if that's what you mean. Editing the file manually with a text editor is the old-school way.

As I mentioned in comments, the standard Puppet approach for working with mount records is via Mount resources. As long as I'm writing an answer, I repeat that using these is the way you should be going about the job.

You objected in comments that

the issue is that the first string (fsname) could be anything, as could the fstype. I also want to preserve existing fsoptions that don't clash with new desired settings.

That is the issue, but not in the way I think you mean. You are in a tight spot because you are trying to accommodate multiple authorities over this aspect of your nodes' configurations. The best practice would be to give the responsibility over the mounts of interest wholly over to Puppet. It has more than enough flexibility to provide for different mount configurations on different machines.

However, if you are determined to use Puppet in this way, then it can be done. But although the task is relatively easy to describe, the particulars make it relatively complex. A sed-based approach is possible, but would be comparatively lengthy and extremely cryptic. A better command-line tool for the main job would be awk, and in particular, this awk script will do the job for the case you've presented:

$1 ~ /^#.*/ || $2 != "/tmp" {print; next}
$4 ~ /.*nosuid.*/ && $4 ~ /.*noexec.*/ {print; next}
{
  split($4, opts, ",")
  printf "%s    %s  %s  ", $1, $2, $3
  for (i in opts) {
    if (opts[i] !~ /(no)?(exec|suid)/) {
      printf "%s,", opts[i]
    }
  }
  printf "noexec,nosuid %s %s\n", $5, $6
}

Wrapping that into an Exec resource and making any other adaptations to your specific requirements are left as an exercise.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157