Optimizing a sed/tr shell script - apache

I wrote a script to automatically add a ServerAlias to an Apache configuration file, then restart Apache, with a rather remedial understanding of sed and tr and probably shell scripting in general. This is what I came up with:
cp /etc/httpd/sites/site.conf tmphost &&
sed s/ServerName\ site.com/ServerName\ site.com^#ServerAlias\ sub.site.com/ \
tmphost |
tr '^#' '\n\t' >/etc/httpd/sites/site.conf &&
rm -f tmphost &&
apachectl restart
Basically I'm creating a copy, replacing the ServerName line with itself + the new alias, using tr to put in the newline and tab (sed was being weird about that?), overwriting the old configuration file, deleting the copy, then restarting Apache.
It works, but doesn't really make mama proud, if you know what I mean. Any ideas on how to clean that up?

If you want to add a ServerAlias line after ServerName line, why not use the 'append' command to add a line:
sed '/ServerName site.com/a\
ServerAlias sub.site.com' tmphost >/etc/httpd/sites/site.conf
There's a 'tab' at the start of the extra line, which you seem to need.
If you have GNU sed, you can do the edit in-place with the -i option and without the temporary file (but don't use the -i option if the file you are editing has multiple hard links, and probably not it if it is a symlink, either; see the comments below from William Pursell).
Note that you should wrap the code in:
trap "rm -f tmphost; exit 1" 0 1 2 3 13 15
...script manipulating tmphost...
rm -f tmphost
trap 0
so that the temporary file is not left around if the shell exits because of a signal. Of course, if you have an in-place alter, you don't need the temporary file at all.

I like the way with direct ServerAilas addition using sed :
sed -i "0,/^ServerName.\+/s//\0\nServerAlias sub.site.com/" /etc/httpd/sites/site.conf
apachectl reload

Related

Linux remove lines contain certain ip addresses from log file (66.249)

i have a 6gb httpd log file and i want to remove lines beginging in
66.249 (ip block of googlebot) i did have a
SetEnvIf Remote_Addr "66\.249\.\." dontlog
entry in my httpd.conf file but it didnt seem to work
so is there a linux command like
grep -removelines-starting "66.49" acessslog
Using sed: Use -i flag if you make changes in file directly.
sed '/^66\.49/d' logfile
Using grep: This will print lines apart from lines starting with 66.49
grep -v '^66\.49' logfile
Using awk:This will print lines apart from lines starting with 66.49
awk '!/^66\.49/' logfile
I can imagine sed is a better fit for this task.
sed -i '/66\.249/d' ./acessslog
"d" is for deleting matched pattern, while -i is for overwriting input file.

How to add EnvironmentFile directive to systemctl using Docker with centos7/httpd base image

I am not sure if this is possible without creating my own base image, but I use environment variables in /etc/environment on our servers and typically make them accessible to apache by doing the following:
$ printf "HTTP_VAR1=var1-value\n\
HTTP_VAR2=var2-value"\
>> /etc/environment
$ mkdir /usr/lib/systemd/system/httpd.service.d
$ printf "[Service]\n\
EnvironmentFile=/etc/environment"\
> /usr/lib/systemd/system/httpd.service.d/environment.conf
$ systemctl daemon-reload
$ systemctl restart httpd
$ reboot
The variables are then available in any PHP calls to getenv('HTTP_VAR1'); and etc. However, in running this from a docker file I get dbus errors on the systemctl commands. Without the systemctl commands it seems the variables are not available to apache as it seems the new EnvironmentFile directive doesn't take effect. My docker file snippet:
FROM centos/httpd:latest
RUN printf "HTTP_VAR1=var1-value\n\
HTTP_VAR2=var2-value"\
>> /etc/environment
RUN mkdir /usr/lib/systemd/system/httpd.service.d &&\
printf "[Service]\n\
EnvironmentFile=/etc/environment"\
> /usr/lib/systemd/system/httpd.service.d/environment.conf
RUN systemctl daemon-reload &&\
systemctl restart httpd
COPY entrypoint.sh /entrypoint.sh
So I happened upon the answer to the issue today. It seems that systemd drops backslashes inside single quotes, but it may effect double-quotes too from what I saw in testing. I found the systemd development mailing list thread from April 2014 where patching the issue was being discussed. It seems as though the fix never made it in. So we have to work around it.
In attempting to work around it I noticed some issues with actually reading the variables at all. It seemed as though either Apache or php-cli would get the correct variables, and sometimes not at all, it took a bit of sleuthing to figure out what was going on. Then I started reading into the systemd's EnvironmentFile directive to see if there was more to gain from the docs. It turns out it does not evaluate bash so export won't work. It expects a text file with variable assignments and herein lies one of the main issues that might keep this from being resolved.
I then devised a workable solution. Utilizing systemd's ExecStartPre directive I am able to run a script on startup of the httpd service. I then read in the environment file and write a new plain text one that will then be used by httpd's systemd unit. Here is the code:
Firstly, I moved my variables to /etc/profile.d/ directory rather than /etc/environment file.
file: /etc/profile.d/environment.sh
This is where we store all our environment variables, this gets easily sourced on all interactive shell logins. In the rarer cases where we need to have these variables available non-interactively we can either provide --login flag to /bin/bash or source it manually.
export HTTP_VAR1=var1-value-with-a-back\slash
export HTTP_VAR2=var2-value
file: /usr/lib/systemd/system/httpd.service.d/environment.conf
Our drop-in unit file to extend how the httpd service works. I add in a script that runs before httpd starts up. This gets ran on all httpd restarts and starts. The script that runs generates a plain text file at /etc/profile.d/environment.env which we subsequently tell systemd to load as an EnvironmentFile.
[Service]
ExecStartPre=/usr/bin/bash -c "/usr/local/bin/generate-plain-environment-file"
EnvironmentFile=/etc/profile.d/environment.env
file: /usr/local/bin/generate-plain-environment-file
Here is the script I am using, I whipped this together really fast, I really don't think it is that robust and it could be better. It just removes the export from the beginning of the lines and then escapes any backslashes since systemd drops single backslashes. A more proper solution might be to use bash to evaluate each line and obtain the variable value in case of usage of variables or other bash in the actual bash variables, then output them as plain text name=value assignments, however this is not part of my use-case so I didn't bother.
#!/bin/bash
cd /etc/profile.d/
rm -rf "./environment.env"
while IFS='' read -r line || [[ -n "$line" ]]; do
echo $(echo "${line}" | sed 's/^export //' | sed 's/\\/\\\\/g') >> "./environment.env";
done < "./environment.sh"
file: /etc/profile.d/environment.env
This is the resulting file generated by the described script.
HTTP_VAR1=var1-value-with-a-back\\slash
HTTP_VAR2=var2-value
Conclusion is that the I now have two files with the same thing in them but I only need to maintain the one, the other is generated each time we restart httpd. Also, we fix the backslash issue in the process. Hurray!

Set EnableSendfile Off from Dockerfile

Hi I'm trying to understand how to add configuration commands in Dockerfile but so far I don't get any good results.
I wrote this line
RUN sed -i 'EnableSendfile Off' /etc/httpd/conf/httpd.conf
how would be the correct version?
Many thanks
I am wondering if the sed command works as your wrote it down.
RUN sed -i -e 's/this/that/g' filename
works fine, so it could be
RUN sed -i -e 's/EnableSendfile On/EnableSendfile Off/g' /etc/httpd/conf/httpd.conf
What kind of results or errors do you have?

How does one use Apache in a Docker Container and write nothing to disk (all logs to STDIO / STDERR)?

I'm running Apache2 in a docker container and want to write nothing to the disk, writing logs to stdout and stderr. I've seen a few different ways to do this (Supervisord and stdout/stderr, Apache access log to stdout) but these seem like hacks. Is there no way to do this by default?
To be clear, I do not want to tail the log, since that will result in things being written to the disk in the container.
The "official" version checked into Docker Hub (https://hub.docker.com/_/httpd/) still write to disk.
Also, what do I need to do to stop Apache from failing when it tries to roll the logs?
One other thing - ideally, I'd really like to do this without another add-on. nginx can do this trivially.
I'm not positive that this won't mess with httpd's logging at all (e.g. if it tries to seek within the file), but you can set up symlinks from the log paths to /dev/stdout and /dev/stderr, like so:
ln -sf /dev/stdout /path/to/access.log
ln -sf /dev/stderr /path/to/error.log
The entry command to the vanilla httpd container from Docker Hub could be made to be something like
ln -sf /dev/stdout /path/to/access.log && ln -sf /dev/stderr /path/to/error.log && /path/to/httpd
According to the apache mailing list, you can just directly write to /dev/stdio (on Unix like systems) as that's just a regular ol' file handle. Easy! Pasting...
The most efficient answer depends on your operating system. If you're
on a UNIX like system which provides /dev/stdout and /dev/stderr (or
perhaps /dev/fd/1 and /dev/fd/2) then use those file names. If that
isn't an option use the piped output feature. For example, from my
config:
CustomLog "|/usr/sbin/rotatelogs -c -f -l -L
/private/var/log/apache2/test-access.log
/private/var/log/apache2/test-access.log.%Y-%m-%d 86400 "
krader_custom ErrorLog "|/usr/sbin/rotatelogs -c -f -l -L
/private/var/log/apache2/test-error.log
/private/var/log/apache2/test-error.log.%Y-%m-%d 86400"
Obviously you'll want to substitute another program for
/usr/sbin/rotatelogs in the example above that writes the data where
you want it to go.
https://mail-archives.apache.org/mod_mbox/httpd-users/201508.mbox/%3CCABx2=D-wdd8FYLkHMqiNOKmOaNYb-tAOB-AsSEf2p=ctd6sMdg#mail.gmail.com%3E
I know it's an old question, but I had this need today.
On an Alpine 3.6, the following instructions, in httpd.conf, are working:
Errorlog /dev/stderr
Transferlog /dev/stdout
I add them to my container this way:
FROM alpine:3.6
RUN apk --update add apache2
RUN sed -i -r 's#Errorlog .*#Errorlog /dev/stderr#i' /etc/apache2/httpd.conf
RUN echo "Transferlog /dev/stdout" >> /etc/apache2/httpd.conf
...
I adjusted config, as from the Dockerfile recipe of httpd, they use sed to adjust the config, to change ErrorLog and CustomLog as follows:
sed -ri ' \
s!^(\s*CustomLog)\s+\S+!\1 /proc/self/fd/1!g; \
s!^(\s*ErrorLog)\s+\S+!\1 /proc/self/fd/2!g; \
' /usr/local/apache2/conf/httpd.conf \
See https://github.com/docker-library/httpd/blob/master/2.4/Dockerfile (towards the end of the file)
You can send your ErrorLog to syslog directly, and you can send any CustomLog (access log) to any executable that reads from stdin. There are log aggregation tools, or you can again use syslog w/ e.g. /usr/bin/logger.
You could try using the dockerize tool. With that you could wrap the httpd-foreground command and redirect its log files to stdout/stderr (don't know exactly the httpd log file paths, simply adjust them to your needs):
CMD ["dockerize", "-stdout", "/var/log/httpd.log", "-stderr", "/var/log/httpd.err", "httpd-foreground"]
In addition to that you could grab that containers stdout/stderr then by specifying a syslog log driver and redirect them to the /var/log/syslog log file on the docker host:
docker run -d --log-driver=syslog ...

How to escape $ in sed over ssh command?

I am trying to create a patch that users can use to remotely edit a file in a pre-defined way using sed, and I could do this manually on each computer, but it would take a long time.
The line I am struggling with is as follows:
host=[hostname]
port=[portnum]
ssh -t $host -p $port "cp ~/file1 ~/file1.bak ; sed -i \"s/fcn1('param1', $2)\n/fcn2('param2'):$zoom\n/g\" ~/file1"
This makes a backup of file1 and then edits a line in the file. I actually want to edit more than one line, but this line demonstrates the problems:
The command works, provided no $ signs are used within the sed command.
I have tried a number of ways of escaping these $ signs but cannot seem to find one that works.
I can use a . wildcard in the find, but obviously not in the replace string.
I would use single quotes for the sed command, in order to avoid expanding the $2, but single quotes are already used inside the command.
Does anyone have any ideas of how to overcome this problem? Thanks in advance for any suggestions!
This should work as well:
ssh -t $host -p $port "cp ~/file1 ~/file1.bak && sed -i \"s/fcn1('param1', \\\$2)/fcn2('param2'):\\\$zoom/g\" file1"
You need 3 back slashes as you have to escape the $ sign in the string passed in the remote bash to sed. And you have to escape that back slash and the $ sign when sending it over via ssh.