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

It's that time of year when I get to sit at a keyboard and script out what I'm thinking about, how I might pull off getting the data I want from api's, for example, but doing so from server space I own as opposed to me here on my laptop. We have had to be very aware of smoke in Idaho, and I consider all the gui steps I had to do to fire up a browser, go to an url, change to CO, resize, take a screenshot, all things that could be mechanized. Then I run a script and send comments, translations, and images to the server in a sample html page.

I have been plotting this task for a while now, intending to do much as bod did with threads like Debugging a module that's failing under taint mode. I have tried to imitate the architecture and write a script that would be aware of perlsec. I'd like to see how many of these issues I can check off, and how I need to organize the filesystems in the cloud to be secure. So far, in view of the security risks that I'm still trying to understand, I don't have a single perl script on my site yet. So I'd like to get on the proverbial scoreboard.

Let's get started with a little output and some source:

$ perl -T 4.dt.pl tiny path is /home/hogan/merrillpjensen.com/prod/lib/1.env.txt real bin is /home/hogan/merrillpjensen.com/prod $
#!/usr/bin/env perl use v5.030; use warnings; use Data::Dumper; use FindBin qw($RealBin); use Path::Tiny; my ($prefix,$website,$environment,$basedir); BEGIN { # truncate envelope $ENV{PATH} = '/bin:/usr/bin'; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer $prefix = "/home/hogan"; #needs to toggle with #$prefix = "/home/fred"; # $website = 'merrillpjensen.com'; use FindBin qw($RealBin); if ($RealBin =~ m!$prefix/$website/(dev|test|prod)!) { $environment = $1; # This is now untainted! $basedir = "$prefix/$website/$environment"; } else {die "Bad environment '$1'"; } } #my $tt = Template->new({INCLUDE_PATH => "$basedir/templates"}); # now I have Path::Tiny my $file_name="1.env.txt"; my $tiny_path=path($basedir, ,'lib',$file_name)->touchpath; say "tiny path is $tiny_path"; my $d = Data::Dumper->new( [ \%ENV ], ['*ENV'] )->Sortkeys(1)->Useqq( +1)->Dump(); my $return = $tiny_path->spew( $d); say "real bin is $RealBin";

Q1) If I'm gonna scp this to some place on my server, such that this script were to be run daily, what should that place be, and what permissions should I give the directory it is in and the file itself?

This is my OS:

fred@fourth:/var/www/html/perlmonks$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.3 LTS Release: 20.04 Codename: focal fred@fourth:/var/www/html/perlmonks$

This is the filesystem:

fred@fourth:/$ ls bin dev home lib32 libx32 media opt root sbin srv tmp + var boot etc lib lib64 lost+found mnt proc run snap sys usr fred@fourth:/$

Q2) How do I securely run this script daily?

I really struggled to find a regex that had some teeth in it without having to hard-code a path in the BEGIN section. I'm not clear what values I'm trying to exclude, that is what form an attacker might take. Would 'he' not begin with /home? On my laptop, I'm hogan, and on my server, I'm fred, so I don't see a way around having to toggle two lines like this:

$prefix = "/home/hogan"; #needs to toggle with #$prefix = "/home/fred"; #

Q3) Could a person be alright with

  if ($RealBin =~ m!/home/*+/$website/(dev|test|prod)!) {

, or would that take all the teeth out of the check?

I frequently use Log::Log4Perl as many of the data I look at need to be columnized to get the sense of them. I haven't completely understood Re^3: Log4Shell and Log::Log4perl, and would like to look at a concrete example:

$ cat 4.conf ###################################################################### +######### # Log::Log4perl Conf + # ###################################################################### +######### log4perl.rootLogger = INFO, LOG1, SCREEN log4perl.appender.SCREEN = Log::Log4perl::Appender::Screen log4perl.appender.SCREEN.stderr = 0 log4perl.appender.SCREEN.layout = Log::Log4perl::Layout::PatternLayou +t log4perl.appender.SCREEN.layout.ConversionPattern = %m %n log4perl.appender.LOG1 = Log::Log4perl::Appender::File log4perl.appender.LOG1.filename = /home/hogan/Documents/hogan/logs/4. +log4perl.txt log4perl.appender.LOG1.mode = append log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayou +t log4perl.appender.LOG1.layout.ConversionPattern = %d %p %m %n $

Q4) Where's a good place to put something like this and with what permissions? (No visitors to the site need access, except myself through ssh.)

Q5) if 4.conf had been maliciously and successfully corrupted, what kind of characters would be here instead?

Thanks for your comment, and Merry Solstice++++

Replies are listed 'Best First'.
Re: creating a secure environment for perl scripts to run
by talexb (Chancellor) on Dec 26, 2021 at 12:15 UTC
      Q1) If I'm gonna scp this to some place on my server, such that this script were to be run daily, what should that place be, and what permissions should I give the directory it is in and the file itself? / Q4) Where's a good place to put something like this and with what permissions? (No visitors to the site need access, except myself through ssh.)

    I believe these two questions are asking the same thing. The location needs to be outside the directory that your web server has access to, in order to prevent a bad actor from accessing your script using that vector, so you could put it into your ~/bin directory if this is a personal website. You could put it into the /opt directory tree for a less user-dependent location (so, /opt/foobar/bin for example).

      Q2) How do I securely run this script daily?

    I would use crontab. I have a corporate client with about twenty cron jobs running mostly during the day, one every minute, a couple every ten minutes, some every hour, some just daily, and one weekly. In addition, you can define environment variables inside the crontab configuration to define how the scripts behave.

      Q3) Could a person be alright with if ($RealBin =~ m!/home/*+/$website/(dev|test|prod)!) { , or would that take all the teeth out of the check?

    I'm not sure the '*+' is doing what you want. You may want to test that regexp against some actual paths and confirm that it's doing what you want. A replacement could be '[^/]+' which would then check that the RealBin directory was a user's home directory. (Of course, installing to the /opt directory as I've suggested in A1 would require a change to this rule.)

    However, I'm not exactly what this script is meant to do -- just check that the environment is clean? How is the output being used? Are you just logging on a looking at the output? Is there going to be a cron job that E-Mails this to you? Are you going to look at the results on a web page? Will the script stop if it's installed in a bad location?

      Q5) if 4.conf had been maliciously and successfully corrupted, what kind of characters would be here instead?

    Well, corruption is when a file is overwritten with junk characters. If it's a configuration file, the odds are that a corrupted file will fail to compile, so that may not be an issue. A corrupted file might have any values in it -- perhaps outside the usual 0x20 to 0x7f values.

    If someone maliciously changes the configuration, they're probably going to leave it so that it still compiles, but does something that helps them (or hurts you). Unless a bad actor has broken into your web provider's entire system, or stolen your credentials, it's unlikely you need to worry about someone altering your configuration file.

    Hope that helps you.

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

      [reordered]

      Hope that helps you.

      Thx, talexb, it certainly does. I opted for /opt, but I have to say that I'm confused about whose turf it is. I found that I could only scp as root:

      $ scp 2.begin.pl fred@164.90.158.33:/opt/scripts/dev scp: /opt/scripts/dev/2.begin.pl: Permission denied $ scp 2.begin.pl root@164.90.158.33:/opt/scripts/dev 2.begin.pl 100% 1011 12.7KB/s +00:00 $

      I think to remember someone with greater experience writing that it's best not to ssh as root. (Is that a thing?)

      Update: I was trying to recall what afoken wrote in Re^7: [OT] A New Everything ?. The caution wasn't against ssh'ing as root per se, but doing so with password authentication:

      $ ssh root@206.189.67.44 root@206.189.67.44's password: Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-45-generic x86_64)

      For where I ended up, I found the links refreshing to reread, in particular Greetings and salutations | sudo. In the wake of reading that I'm sshing as root with impunity, and doing things without using sudo.

      Another link I found in the rereading worth reposting is the Debian Administrator's Handbook by Raphaël Hertzog and Roland Mas

      End Update

      Anyways, I was confused about whether root or fred should own things and decided to make a group:

      fred@fourth:~$ sudo groupadd mygroup [sudo] password for fred: fred@fourth:~$ sudo usermod -a -G mygroup fred fred@fourth:~$ sudo usermod -a -G mygroup root fred@fourth:~$ getent group mygroup mygroup:x:1001:fred,root fred@fourth:~$ fred@fourth:/opt$ sudo chown root:mygroup scripts fred@fourth:/opt$ ll total 16 drwxr-xr-x 4 root root 4096 Dec 30 04:10 ./ drwxr-xr-x 19 root root 4096 Dec 2 20:03 ../ drwxr-xr-x 4 systemd-coredump root 4096 Sep 26 2020 digitalocean/ drwxr-xr-x 5 root mygroup 4096 Dec 30 04:13 scripts/ fred@fourth:/opt$ sudo chmod 770 scripts fred@fourth:/opt$ ll total 16 drwxr-xr-x 4 root root 4096 Dec 30 04:10 ./ drwxr-xr-x 19 root root 4096 Dec 2 20:03 ../ drwxr-xr-x 4 systemd-coredump root 4096 Sep 26 2020 digitalocean/ drwxrwx--- 5 root mygroup 4096 Dec 30 04:13 scripts/ fred@fourth:/opt$

      I thought 770 was the right permissions for this situation in terms of inclusion and exclusion. (?) And after changing the group behavior, I'm able to scp as fred to a directory that's owned by root.

      I would use crontab.

      Ok. I think I've got this lined up to go off at 6 tomorrow morning:

      fred@fourth:/opt/scripts/dev$ crontab -e no crontab for fred - using an empty one crontab: installing new crontab fred@fourth:/opt/scripts/dev$ crontab -l # Edit this file to introduce tasks to be run by cron. ...snip # m h dom mon dow command 0 6 * * * /opt/scripts/dev/2.begin.pl
      However, I'm not exactly what this script is meant to do -- just check that the environment is clean? How is the output being used? Are you just logging on a looking at the output? Is there going to be a cron job that E-Mails this to you? Are you going to look at the results on a web page? Will the script stop if it's installed in a bad location?

      Several questions there. 1. The code to dump the environment is just the stub-out to see what's there. I'll make some comparisons to see if my Begin section is pruning anything away.2. The output at this point is simply proof that I can do some minimal thing. 3. Output would eventually inform my day. 4. I would like an e-mail out of this. Is there some preferred way to do that? 5. I want weather reports logged so that Template can use the values to display a webpage. I'd also like a couple of useful screenshots and will see if I can use Corion's automated browsers to that end. I'd like to imitate the keystrokes I make almost every time I figure out what dangers to avoid in the mountain west. 6. I'm not sure whether this whole business of checking where the script is by comparing it to a hardcoded string in the Begin section solves any realistic attack. I still don't know what an attacker "looks like" from a perl/unix perspective.

      Anyways, I'm pushing close to midnight local, so time for me to celebrate sleep and hope that I wake up to automatic output in the year 2022. Cheers....

        Picking a few of your followups ..

          I think to remember someone with greater experience writing that it's best not to ssh as root. (Is that a thing?)

        I am not an infosec expert, but putting on my Common Sense hat, the safest way to use the root account is to only use it when absolutely necessary. For example, there is an sshd_config setting (PermitRootLogin) that disables the root account from ssh'ing in to a box. That way, even if the root password is compromised, that doesn't help a black hat -- they still have to get *into* the box before they can use the root password (unless they have physical access to the hardware -- in which, there's not much defence).

        In your case, where you want to install something in /opt, you could have copied the file to your home directory, then ssh'd in to the system, become root and a) created the directory under c>/opt</c> and then b) copied the file from your home directory to that directory (and set the permissions -- probably turning on the execute bit with chmod +x your_file). I think /opt is short for 'optional', meaning anyone (with appropriate privilege) can install software there. Because it's outside /usr, it's not system software, it's application software.

        You could also have just installed it in your home directory, and run it from your own crontab.

          4. I would like (to send) an e-mail out of this. Is there some preferred way to do that?

        I don't know about *preferred* way, but here's how I do it in one of my scripts ..

        use Email::Simple::Markdown; use Email::Sender::Simple qw(sendmail); use Email::Sender::Transport::SMTP qw(); use Try::Tiny; ... my $message = Email::Simple::Markdown->create( header => [ Subject => "Report for $client->{'c_name'} on $date", To => $recipients, Cc => 'cc@gmail.com', From => 'noreply@foo.com' ], body => $output ); try { sendmail( $message, { from => 'noreply@foo.com', transport => Email::Sender::Transport::SMTP->new( { host => $FooBar::Host, port => $FooBar::Port, sasl_username => $FooBar::User, sasl_password => $FooBar::Password, ssl => 'starttls', } ) } ); } catch { warn "sending failed: $_"; };
        Fill in the variables with the appropriate stuff. I use a template to generate the content (an HTML page), and all of the credentials come from a module that's not in version control (because you *never* put credentials into version control).

          I still don't know what an attacker "looks like" from a perl/unix perspective.

        Again, I am not an infosec specialist, so I couldn't explain that to you. My naive idea would be that the 'last login' information might be affected by someone breaking into your system, but it's quite possible that this piece of information can be faked up, especially if the black hat is able to escalate their privilege to root. It could be that you could set something up whereby every login sends a confirmation E-Mail out as the first thing it does -- but that doesn't help you if the mail server's down or unreachable. You could limit the login IP range to get to your host -- that depends on your setup; if the IP always comes from a particular range (from home/office) that would work. If you travel a lot, that might not work well. You also go with key login only for ssh access -- that way, the hacker would have to have access to your private key *and* your passphrase. (Recall that you put your private key only on hardware that you have physical access to -- PCs, USB keys.)

        Bottom line: For infosec advice, talk to an expert. I'm not that guy. :)

        Alex / talexb / Toronto

        Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: creating a secure environment for perl scripts to run
by eyepopslikeamosquito (Archbishop) on Jan 02, 2022 at 01:59 UTC