Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Best way to write to a file owned by root?

by nysus (Parson)
on Mar 13, 2017 at 20:50 UTC ( [id://1184478]=perlquestion: print w/replies, xml ) Need Help??

nysus has asked for the wisdom of the Perl Monks concerning the following question:

I'm using the Config::Hosts to make updates to the /etc/hosts file. My script runs under my regular user account and so it is unable to write to the /etc/hosts which is owned by root. I tried slapping sudo in front of it but then started getting errors (root doesn't know where my personal perl library resides). Plus, I prefer my script to run as a regular user so the files it writes are owned by my regular user.

One approach I'm considering is to give the user my script runs under NOPASSWD access to the chmod command and then having my script sudo chmod on the /etc/hosts file temporarily and then switch back to the original permissions. This seems like a less than ideal, solution. I'm wondering if there might be a better idea.

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Best way to write to a file owned by root?
by afoken (Chancellor) on Mar 14, 2017 at 07:26 UTC
    I'm using the Config::Hosts to make updates to the /etc/hosts file. My script runs under my regular user account and so it is unable to write to the /etc/hosts which is owned by root.

    Just a few extra notes:

    • You don't want to write critical system files like /etc/hosts directly. Imagine your program being killed or crashing in the middle of writing the file. You will end up with an incomplete, or worse, an unparseable file. The sane way of updating critical files is to write to a temporary file in the same directory, and then rename that temp file to the destination file. Rename is atomar (at least on local filesystems), so it is done either completely or not at all.
    • Updating /etc/hosts generally sounds wrong. /etc/hosts should be almost empty, hostnames should be resolved using DNS. Updating /etc/hosts was proven not to scale in the early 1980s.
    • Config::Hosts is old and probably unmaintained code, written in an even older style, using bare word file handles (without local *HANDLE, to make it worse), two-argument open, non-working prototypes for methods, print STDERR instead of warn or die. And without digging into details, it seems to do a lot of unnecessary work and keeps data in RAM at least twice. Plus, it directly overwrites the hosts file instead of using a temp file and an atomar rename.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      On my computers, I use /etc/hosts as a first line ad-blocker. For that, it works quite well, especially on my phone.

Re: Best way to write to a file owned by root?
by thanos1983 (Parson) on Mar 13, 2017 at 21:59 UTC

    Hello nysus,

    Have you tried to execute the script with sudo?

    Sample:

    sudo hosts.pl

    For me the following worked just fine:

    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; sub read_hosts { my $file = "/etc/hosts"; open (my $fh, "<", $file) or die "Can't open $file for read: $!"; my @hosts = <$fh>; close $fh or die "Cannot close $file: $!"; return @hosts; } sub write_hosts { my $file = "/etc/hosts"; open (my $fh, ">", $file) or die "Can't open $file for read: $!"; for my $host (@_) { print $fh $host; } # Or print $fh $_ for (@_); close $fh or die "Cannot close $file: $!"; return; } my @hosts = read_hosts; print Dumper \@hosts; $hosts[2] = "Test_host\t127.0.0.4\n"; write_hosts(@hosts); my @updates = read_hosts(); print Dumper \@updates; __END__ sudo perl hosts.pl $VAR1 = [ '127.0.0.1 localhost ', '127.0.1.1 user ', '# The following lines are desirable for IPv6 capable hosts ', '::1 ip6-localhost ip6-loopback ', 'fe00::0 ip6-localnet ', 'ff00::0 ip6-mcastprefix ', 'ff02::1 ip6-allnodes ', 'ff02::2 ip6-allrouters' ]; $VAR1 = [ '127.0.0.1 localhost ', '127.0.1.1 user ', 'Test_host 127.0.0.4 ', '::1 ip6-localhost ip6-loopback ', 'fe00::0 ip6-localnet ', 'ff00::0 ip6-mcastprefix ', 'ff02::1 ip6-allnodes ', 'ff02::2 ip6-allrouters' ];

    Update: Adding print $fh $_ for (@_);

    Update2: Or use Sudo module:

    Make sure the script "hosts.pl" is executable chmod +x hosts.pl

    #!/usr/bin/perl use strict; use warnings; use Sudo; my $su; my $name = "root"; my $pass = "password"; $su = Sudo->new( { sudo => '/usr/bin/sudo', #sudo_args => '...', username => $name, password => $pass, program => '/home/user/hosts.pl', #program_args => '...' } ); my $result = $su->sudo_run(); if (exists($result->{error})) { printf "STDERR: %s\n",$result->{error}; #&handle_error($result); } else { printf "STDOUT: %s\n",$result->{stdout}; printf "STDERR: %s\n",$result->{stderr}; printf "return: %s\n",$result->{rc}; } __END__ $ perl sudo.pl STDOUT: $VAR1 = [ '127.0.0.1 localhost ', '127.0.1.1 user ', ' ', '::1 ip6-localhost ip6-loopback ', 'fe00::0 ip6-localnet ', 'ff00::0 ip6-mcastprefix ', 'ff02::1 ip6-allnodes ', 'ff02::2 ip6-allrouters' ]; $VAR1 = [ '127.0.0.1 localhost ', '127.0.1.1 user ', 'Test_host 127.0.0.4 ', '::1 ip6-localhost ip6-loopback ', 'fe00::0 ip6-localnet ', 'ff00::0 ip6-mcastprefix ', 'ff02::1 ip6-allnodes ', 'ff02::2 ip6-allrouters' ]; STDERR: return: 1

    Hope this helps.

    Seeking for Perl wisdom...on the process of learning...not there...yet!

      I did try running my script as sudo, yes, but I get errors because I have use statements for modules that the root user does not know about. Also, my script outputs files that I want to be owned by my system user, not root.

      The sudo module looks promising. Thanks! I will take a look at it and report my findings when I get a chance. Thanks for your help!

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        I have use statements for modules that the root user does not know about.

        Using lib or a combination of FindBin and lib in the script should help. For details, see the documentation of those modules.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        Hello again,

        I was thinking about the case that you where describing. "Best way to write to a file owned by root?"

        I did a bit of search on the web and there is an alternative. Give root privileges to an user just for a specific directory closed, the proposed solution is:

        Taken from the question:

        You do not need root permissions - you need group permissions. For example if the group is www-data, do something along the lines of usermod -a -G www-data misterX and make sure the files are group-writable.

        It is not bad as a solution to add your user in a group strictly to update the /etc/hosts file. But to be honest I do not know how vulnerable it becomes your file regarding security.

        Maybe another monk who has more knowledge regarding security issues can provide more information.

        But this could be a solution in avoiding using Sudo module.

        Hope this is a better solution.

        No matter what you decide at the end, post your solution here, it is interesting. :D

        Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: Best way to write to a file owned by root?
by huck (Prior) on Mar 14, 2017 at 23:36 UTC

    but then started getting errors (root doesn't know where my personal perl library resides).

    let me suggest a different direction to solve that

    i have a file in /usr/bin called perlhuck. It is chmod ugo+x. it contains

    #!/bin/sh perl -I /home/huck/cvs/perl_site_lib2 "$@"
    where /home/huck/cvs/perl_site_lib2 is my personal perl library. Im very old school, for decades when i wanted to run a perl program id say perl <program>.pl, so it is very natural to say perlhuck <program>.pl too. Having this script around allows me to place this line into my cgi-bin programs as the first line.
    #!/usr/bin/perlhuck
    And they run just fine knowing where my personal libs are. I also place that as the first line of my chmod ugo+x perl programs. If i wanted to i could extend the path and do all sorts of other things in perlhuck too.

    Part of my motivation for perlhuck also come from working in production environments where besides a perllib and/or path i need to set other env variables to insure my process ran correctly.

    over time i have written dozens of scripts like this, each designed to set up the correct environment before running some production script.

    if you had such a script, say perlnysus then you could say sudo perlnysus <program>.pl and everything would still work fine.

    So i suggest you create a perlnysus script, and either say sudo perlnysus <program>.pl or place #!/usr/bin/perlnysus into the first line of your executable perl programs and say sudo <program>.pl

Re: Best way to write to a file owned by root?
by Anonymous Monk on Mar 13, 2017 at 22:15 UTC
    "One approach I'm considering is to give the user my script runs under NOPASSWD access to the chmod command ..."

    Since you refuse to explain what you actually want accomplished, one can assume that you have no idea what you want to accomplish and you are fixing to pay for your ignorance when someone compromises your security. Please let us know who your users are so they can be properly warned not to trust you and your lack of concern for security.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1184478]
Approved by beech
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (9)
As of 2024-04-23 18:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found