20

the resource_directory has only 2 actions available: create and delete

I need to update the owner and group of a directory recursively.

How do I do that?

using a simple resource_execute?

execute "chown-data-www" do
  command "chown -R www-data:www-data /var/www/myfoler"
  user "root"
  action :run
end
jww
  • 97,681
  • 90
  • 411
  • 885
zabumba
  • 12,172
  • 16
  • 72
  • 129
  • 3
    Are you creating the directory or just chown-ing an existing dir? `resource_directory`will let you set the `mode`, which you can use to set the sticky bit and have everything under it inherit from that. Also reference: https://tickets.opscode.com/browse/CHEF-690 as to why they never added a recursive chmod. Your execute statement is probably the easiest way to go – Display Name is missing May 29 '14 at 15:51
  • 1
    Not sure if it overwrites the directories or just changes the permissions but you can use the `directory` resource and the `recursive true` option. – jarsever Aug 06 '14 at 19:41
  • 4
    @jarsever Unrelated user, similar problem. Using the directory resource, with recursive action does not edit owner of all the files in the directory. The docs say only leaf node is chowned. – theTuxRacer Aug 07 '14 at 06:31
  • 1
    @jarsever recursive option is to specify to create parent directories as well if needed – kamaradclimber Oct 11 '14 at 13:38

5 Answers5

13

You can set the default action to nothing then have resources that may screw things up notify the perm fixer:

execute "chown-data-www" do
  command "chown -R www-data:www-data /var/www/myfoler"
  user "root"
  action :nothing
end

resource "that may screw up perms" do
  stuff "one two three"
  notifies :run, execute "chown-data-www"
end

with more options you could have the action :run but not if the parent folder is already the correct perms. You could alter this to include a deeper/problem file/directory, or with a find command similar to this

execute "chown-data-www" do
  command "chown -R www-data:www-data /var/www/myfoler"
  user "root"
  action :run
  not_if '[ $(stat -c %U /var/www/myfolder) = "www-data" ]'
end

Edit: fix to reflect comment below

Bill Warner
  • 639
  • 6
  • 18
  • In the last section I guess you meant: not_if "stat -c %U /var/www/myfolder |grep www-data" – Dex Jan 14 '16 at 13:06
2

We could write a chef resource which iterates through your directory hierarchy and creates a file or directory resource for every file that it finds and then set it up to manage the owner and group on those files. You won't like that, however, since if you have a thousand files in that directory then you'll get a thousand chef resources and your converges will be slow.

If you want to you can, in fact, roll your own code which does something like that which I wrote in the ticket tickets.opscode.com/browse/CHEF-690 which @name referenced, but I wouldn't recommend it.

If you're trying to prevent "tampering" and fix random corruption of user and group attributes then your solution is probably correct. You'll always execute that command on every chef convergence and the resource will always show as being updated, but the command will run as fast as possible (chown -R is basically convergent and idempotent as it checks the perms before trying to set the perms). You will not get reporting back on fixed perms which is the only downside.

If you are just trying to fix the perms once on building a server, then you should throw a not_if conditional in there to check that if the root directory has the correct perms you don't run it every time. That will give you idempotent behavior and will not execute the command on every run, but the downside is clearly that if one of the files under that directory structure has its perms mangled by someone or something in the future, then it will not get corrected.

There's a possible use case here for a single resource which behaves like chown -R and then reports what it fixed (and array of files that had perms changed) which would be useful for cases like SOX and PCI-DSS reporting, but we don't currently cover that use case.

tl;dr is that your solution is fine and you can add a not_if guard if you like

lamont
  • 3,854
  • 1
  • 20
  • 26
  • You can run `chown` with `--verbose` to have it list out all the operations it is performing (changes or already correct "retained" ownership). – dragon788 Jan 10 '18 at 20:49
  • Same with chmod, you can call with `--verbose` to get a listing of all the changes. I'd imagine with a fairly basic regex you could handle both the `chown` and `chmod` output syntax quite nicely for reporting/testing of application of permissions as they are VERY similar. – dragon788 Jan 10 '18 at 20:50
  • How much faster I wonder would could this be using the native platform tools be than trying to optimize the Ruby for each platform's file permissions quirks and ACLs etc? – dragon788 Jan 10 '18 at 20:55
2

If this as a one-off to fix permissions your shortest path might be just knife ssh. (I just did that after ending up here on my own search.)

knife ssh 'name:*' "sudo chown -R $user:$group /full/path/to/directory"

knife ssh 'name:*' "sudo chmod -R 770 /full/path/to/directory"

If I were setting this up from scratch I think I'd need to set up the directory path and proper permissions with one line (NOTE: explicitly apply the perms to each parent in the path)

%w[ /foo /foo/bar /foo/bar/baz ].each do |path|
  directory path do
    owner 'root'
    group 'root'
    mode '0755'
  end

and then create each individual file as a cookbook_file.

jorfus
  • 2,804
  • 27
  • 23
2

https://stackoverflow.com/a/28283020/11822923

Answer met my need but there is an issue with grep command, even if the user is apache2 or apach the grep exit code will be 0, but I needed the user to exactly be apache (Apache web server user on CentOS 7.7).

Here is my recipe:

node["apache"]["sites"].each do |sitename, data|
  document_root = "/content/sites/#{sitename}"

  directory document_root do
    action :create
    mode "0755"
    recursive true
    owner "apache"
    group "apache"
  end

  execute "chown_to_apache_user" do
    command "chown -R apache:apache /content"
    user "root"
    action :run
    not_if '[ $(stat -c %U /content/) = "apache" ]'
  end

  template "/etc/httpd/conf.d/#{sitename}.conf" do
    source "vhost.erb"
    mode "0644"
    variables(
      :document_root => document_root,
      :port => data["port"],
      :domain => data["domain"]
    )
    notifies :restart, "service[httpd]"
  end

end

For comparison:

enter image description here

P.S.: to check group as well:

node["apache"]["sites"].each do |sitename, data|
  document_root = "/content/sites/#{sitename}"

  directory document_root do
    action :create
    mode "0755"
    recursive true
    owner "apache"
    group "apache"
  end

  execute "chown_to_apache_user" do
    command "chown -R apache:apache /content"
    user "root"
    action :run
    not_if '[ $(stat -c %U /content/) = "apache" ] && [ $(stat -c %G /content/) = "apache" ]'
  end

  template "/etc/httpd/conf.d/#{sitename}.conf" do
    source "vhost.erb"
    mode "0644"
    variables(
      :document_root => document_root,
      :port => data["port"],
      :domain => data["domain"]
    )
    notifies :restart, "service[httpd]"
  end

end
0

I would use plain resource directory with action :create. Per documentation:

:create
Default. Create a directory. If a directory already exists (but does not match), update that directory to match

https://docs.chef.io/resource_directory.html