Lukáš Bařinka
© 2021
REV 2.10
mod_ext_filter modulemod_ext_filter
ExtFilterDefine lower-to-upper mode=output \
intype=text/plain outtype=text/plain \
cmd="/usr/bin/tr '[:lower:]' '[:upper:]'"
intype attribute selects types of filtered filesouttype attribute is necessary in case the filter changes mime-type of filtered file
<Directory "/tmp/example">
SetOutputFilter lower-to-upper
AddType text/plain .txt
</Directory>
ExtFilterDefine lower-to-upper mode=output \
intype=text/plain outtype=text/plain \
cmd="/usr/bin/tr '[:lower:]' '[:upper:]'"
intype slouží pro omezení filtrovaných souborůouttype je nezbytné použít v případě, že filtr mění mime-typ souboru
<Directory "/tmp/example">
SetOutputFilter lower-to-upper
AddType text/plain .txt
</Directory>
The goal is to configure output filter that will change response data using external command.
It is important the external filter is able to read data from stdin and write data to stderr.
The filter will process text files only.
Cílem je nakonfigurovat výstupní filtr, který bude upravovat odesílaná data pomocí externího programu.
Je potřeba aby externí filtr četl data ze stdin a zapisoval na stderr.
Zpracovávat bude pouze textové soubory.
tidy program to change/correct HTML files
apt install tidy
\ characters must be escaped ⇒ "/usr/bin/tr -d '\\n'"tidy, který upravuje vzhled HTML souborů
apt install tidy
\ musí být escapované ⇒ "/usr/bin/tr -d '\\n'"tr and tidy commands as an external filters using mod_ext_filter directives with following names:
TR, filter should remove all new-line characters (\n)TIDYTR filter for all files with html extension in /var/www/main/filter directorycurlTIDY filter for all files with html extension in /var/www/main/filter directorytr resp. tidy jako externí výstupní filtry pomocí direktiv mod_ext_filter pod názvy:
TR, filtr z textu smaže všechny znaky nový řádek tj. (\n)TIDY/var/www/main/filter zaregistrujte pro všechny soubory s příponou html filtr TRcurl/var/www/main/filter zaregistrujte pro všechny soubory s příponou html filtr TIDYhtml extension in /var/www/main/filter directory/var/www/main/filter zaregistrujte pro všechny soubory s příponou html oba filtry
ExtFilterDefine SUM mode=output \
cmd="/usr/bin/awk '{ data[$1]+=$10; c[$1]++ } \
END { for (ip in data) print ip,c[ip],data[ip] }'"
ExtFilterDefine SORT mode=output \
cmd="/usr/bin/sort -k2rn"
SetOutputFilter SUM;SORT
There is no possibility to use a pipe in external filter “cmd1 | cmd2” (it is not a shell).
⇒ The chain of filter is a way in that case
V externím filtru nelze použít rouru “cmd1 | cmd2” (není to shell).
⇒ Je potřeba zřetězit jednotlivé filtry.
mod_cgi = Process-based MPM (prefork)
Uses an internal mechanism to create new proccesses
mod_cgid = Thread-based MPM (worker/event)
Forking a process with multiple threads is expensive operation
Uses an external CGI daemon to create new processes
Apache - CGI daemon communication uses Unix socket
ScriptSock directivemod_cgi = Procesové zpracování požadavků (prefork)
Používá interní mechanizmus vytváření procesů
mod_cgid = Vláknové zpracování požadavků (worker/event)
Replikace procesů s více vlákny je nákladná operace
Používá externího daemona pro vytváření nových procesů
Komunikaci s externím daemonem zajišťuje socket
ScriptSock direktivuScriptAlias directivemod_cgid module
LoadModule cgid_module libexec/mod_cgid.so
ScriptSock /var/run/cgisock
ScriptAliasmod_cgid)
LoadModule cgid_module modules/mod_cgid.so
ScriptSock /var/run/cgisock
Mime-type: application/x-httpd-cgi For backward compatibility (deprecated)
Mime-type: application/x-httpd-cgi Pro zpětnou kompatibilitu (deprecated)
ScriptAlias /cgi-bin/ /www/cgi-bin/
<Directory /www/cgi-bin/>
…
</Directory>
ScriptAliasMatch "^~([a-z0-9]+)/cgi-bin/(.*)" "/home/$1/www/cgi-bin/$2"
<DirectoryMatch "/home/[a-z0-9]+/www/cgi-bin">
Options +ExecCGI
AddHandler cgi-script .cgi .sh …
</Directory>
Content-Type: text/html
Content-Type: text/html
stdin) in case of POST request
REQUEST_METHOD – method of client request
CONTENT_LENGTH – length of stdin in bytes
stdin) při použití metody POST
REQUEST_METHOD – metoda požadavku
CONTENT_LENGTH – délka stdin v bajtech
Content-Type?Content-Type?Forbidden" error message is displayedInternal Server Error" message is displayedPremature end of script headers" – Check whether the CGI script produces response header and body separated by an empty lineForbidden"Internal Server Error"Premature end of script headers" – Zkontrolujte, jestli program správně generuje hlavičku odpovědi oddělenou prázdným řádkem od těla
ScriptLog path/to/log/file
ScriptLogLength bytes
ScriptLogBuffer bytes
ScriptLog cesta/k/log/souboru
ScriptLogLength bytes
ScriptLogBuffer bytes
%% [time] request-line
%% HTTP-status CGI-script-filename
%%error
error-message
%request
HTTP request headers
POST / PUT input (when present)
%response
HTTP response headers
%stdout
CGI standard output
%stderr
CGI standard error output
%% [time] request-line
%% HTTP-status CGI-script-filename
%%error
error-message
%request
HTTP hlavičky požadavku
POST / PUT vstup (pokud existuje)
%response
HTTP hlavičky výstupu
%stdout
CGI standardní výstup
%stderr
CGI standardní chybový výstup
List of environment variables Environment of CGI script (example)
Zjištění nastavení prostředí běhu CGI skriptu
#!/bin/bash
echo "Content-type: text/plain"
echo
/usr/bin/env
DOCUMENT_ROOT /var/www/localhost/htdocs
PATH /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin…
PWD /var/www/modules/cgi-bin
SCRIPT_FILENAME /var/www/modules/cgi-bin/df.sh
REQUEST_URI /modules/cgi-bin/df.sh?dir=/tmp
SCRIPT_NAME /modules/cgi-bin/df.sh
REQUEST_METHOD POST
CONTENT_LENGTH 328
QUERY_STRING dir=/tmp
SERVER_NAME ewait
SERVER_ADDR 147.32.80.97
REMOTE_ADDR 147.32.80.98
The goal is to configure web server to execute CGI scripts. Simple script listing environment is a part of this task. It will helps you to write more meaningful scripts later.
Cílem je nakonfigurovat spouštění CGI skriptů webovým serverem. Součástí je napsání jednoduchého skriptu, který vypíše informace o systému.
mod_cgid moduleHTTP_… variables
curl -H "Foo-bar: baz" …
Is in variable
HTTP_FOO_BAR=baz
…?foo=bar&baz
QUERY_STRING="foo=bar&baz"
mod_cgid)HTTP_…
curl -H "Foo-bar: baz" …
Je v proměnné
HTTP_FOO_BAR=baz
…?foo=bar&baz
QUERY_STRING="foo=bar&baz"
/var/www/cgi-bin directory and map that directory into webspaceScriptAliasHandler cgi-script
Beware of CGI file extension configuration
/var/www/cgi-bin a namapujte tento adresář do webspaceScriptAliasHandler cgi-script
Pozor na nastavení přípony CGI skriptu
mod_include modulemod_include
Options +IncludesNoExec
Options +Includes
+Include SSI including external command execution
INCLUDES to search for and process SSI commands
Options +IncludesNoExec
Options +Includes
+Include je SSI včetně vykonávání externích příkazů
INCLUDES pro hledání a zpracování SSI příkazů
<IfModule mime_module>
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml
</IfModule>
XBitHack directive usage for directory
XBitHack On
SetOutputFilter INCLUDES
<IfModule mime_module>
AddType text/html .shtml
AddOutputFilter INCLUDES .shtml
</IfModule>
XBitHack v adresáři
XBitHack On
SetOutputFilter INCLUDES
<!--#element attribute=value attribute=value … -->
There must be no space between <!-- and #element!
config | sets output formats |
echo | prints out variable content |
exec | executes external command |
fsize | prints out file size |
flastmod | prints out last modification date of file |
include | includes file content |
printenv | prints out environment (all env. variables) |
set | sets variable value |
<!--#element attribute=value attribute=value … -->
Mezi <!-- a #element nesmí být mezera!
config | nastavuje výstupní formáty |
echo | vypisuje obsah proměnných |
exec | spouští externí program |
fsize | vypisuje velikost souboru |
flastmod | vypisuje datum poslední modifikace souboru |
include | vkládá obsah souboru |
printenv | vypisuje obsah prostředí (všech proměnných) |
set | nastavuje hodnotu proměnné |
include file="..." | virtual="..."
file File in current sub/directory, absolute path or .. can't be usedvirtual Web space address of a documentPATH_INFO usage must be enabled
AcceptPathInfo On|Default
REQUEST_URI will not change, only PATH_INFO will change
include file="..." | virtual="..."
file Soubor ve stejném pod/adresáři, nelze použít absolutní cestu nebo ..virtual Dokument s adresou ve webspace serveruPATH_INFO
AcceptPathInfo On|Default
Požadavku se nezmění REQUEST_URI, jenom PATH_INFO
Usage of virtual option can lead to unexpected results
E.g. request processing repetition.
An action (Action - see later) may be called recursively because of the constant REQUEST_URI variable value during request processing when all files (e.g. .ssi, .s/html, .sh) are in one directory and mod_actions is configured.
Použití virtual může vést k nečekanému výsledku
Např. k opakovanému zpracování požadavku.
Když budou všechny soubory
(např .ssi, .s/html, .sh) v jednom adresáři a bude nastaven mod_actions,
může dojít k rekurzivnímu volání akce (Action) díky stále stejné REQUEST_URI !
<pre>
<!--#exec cmd="ls" -->
</pre>
<!--#config timefmt="%D" -->
This file was modified: <!--#echo var="LAST_MODIFIED" -->
<!--#include virtual="/footer.html" -->
DATE_LOCALDOCUMENT_NAME (including pathname)DOCUMENT_URILAST_MODIFIED
<!--#set var="modified" value="$LAST_MODIFIED" -->
strstr1 = str2, str1 == str2, str1 != str2
/str2/ represents RE
Subexpressions in RE are str1 < str2, str1 <= str2, str1 > str2, str1 >= str2( condition ), ! conditioncondition1 && condition2, condition1 || condition2
strstr1 = str2, str1 == str2, str1 != str2
/str2/ představuje RE
Podvýrazy v RE jsou dostupné v proměnných $1 .. $9
str1 < str2, str1 <= str2, str1 > str2, str1 >= str2( podmínka ), ! podmínkapodmínka1 && podmínka2, podmínka1 || podmínka2The goal is to configure SSI when a requested source is served. That can be useful to include header/footer to content, or to generate file timestamp, and other simple cases.
Cílem je použít dynamické vkládání obsahu do poskytovaného zdroje. To se může hodit v jednoduchých příkladech vkládání hlavičky/patičky do obsahu nebo generování časových značek apod.
ab commandab
<!--#config timefmt="%R, %B %d, %Y" -->
<!--#flastmod file=$DOCUMENT_NAME -->
<!--#exec cmd="/bin/date" -->
DATE_LOCAL variable And set time format using timefmt variabledate to achieve the same resultINCLUDES output filter for created documentsab commandDATE_LOCAL A nastavením formátu času pomocí timefmtdateINCLUDES pro vytvořený dokumentabWatch ErrorLog during configuration attempts
Je dobré průběžně sledovat ErrorLog
AH00082: an unknown filter was not added: INCLUDES
Load module mod_include e.g. using
a2enmod include
AH01374: mod_include: Options +Includes (or IncludesNoExec) wasn't set,
INCLUDES filter removed: /ssi/index.html
Enable SSI for chosen directory
<Directory …>
Options +Includes
…
</Directory>
AH00082: an unknown filter was not added: INCLUDES
Načíst modul mod_include např. pomocí
a2enmod include
AH01374: mod_include: Options +Includes (or IncludesNoExec) wasn't set,
INCLUDES filter removed: /ssi/index.html
Povolit SSI pro daný adresář
<Directory …>
Options +Includes
…
</Directory>
mod_actions modulemod_actionsProvides CGI execution of (another) configured script instead of requested file or method
Umožňuje podle MIME typu požadovaného souboru nebo metody vykonat zvolený (jiný) CGI skript místo požadovaného souboru
ScriptAlias directory and act like standard CGI script
LoadModule actions_module modules/mod_actions.so
<Directory "…">
# Text files wrapped inside special header and footer
Action text/plain "/actions/txt.sh" virtual
</Directory>
virtual flag specifies whether the requested file must exist or not
If virtual flag is not specified and requested file does not exist, the response status is 404 Not Found
If virtual flag is specified, the action script is executed and has to deal with requested file existence by itself
ScriptAlias a měl všechny vlastnosti CGI skriptu
LoadModule actions_module modules/mod_actions.so
<Directory "…">
# textové soubory dostanou spec. hlavičku a patičku
Action text/plain "/actions/txt.sh" virtual
</Directory>
virtual určuje, zda soubor musí existovat
Pokud není uvedeno a soubor neexistuje, dostaneme 404 Not Found
Pokud je uvedeno, skript se vykoná a s existencí/neexistencí souboru se musí vyrovnat sám
#!/bin/bash
echo "Content-Type: text/html"
echo
echo "<h1>File: ${REDIRECT_URL}</h1><hr>"
if [ "$REQUEST_METHOD" == "POST" ]; then
QUERY_STRING="$QUERY_STRING&$( head -c $CONTENT_LENGTH )"
QUERY_STRING=${QUERY_STRING#&}
fi
if [ ! -f "${PATH_TRANSLATED}" ]; then
echo "File does not exist."
elif [ $( echo "${QUERY_STRING}" | tr '&' '\n'| grep "^full=" | tail -1 ) == "full=yes" ]; then
echo '<pre>'
cat "${PATH_TRANSLATED}"
echo "$QUERY_STRING</pre><hr>"
echo "<a href='${REDIRECT_URL}'>Display first 10 lines</a>"
echo '<form action="#" method="POST"><input type="submit" name="full" value="no">
<input type="hidden" name="foo" value="bar"></form><hr>'
else
echo "<pre>"
head "${PATH_TRANSLATED}"
echo "$QUERY_STRING</pre><hr>"
echo "<a href='${REDIRECT_URL}?full=yes'>Display full content</a>"
echo '<form action="#" method="POST"><input type="submit" name="full" value="yes">
<input type="hidden" name="foo" value="bar"></form><hr>'
fi
stat "${PATH_TRANSLATED}" | egrep "Modify:"
#!/bin/bash
echo "Content-Type: text/html"
echo
echo "<h1>Soubor: ${REDIRECT_URL}</h1><hr>"
if [ "$REQUEST_METHOD" == "POST" ]; then
QUERY_STRING="$QUERY_STRING&$( head -c $CONTENT_LENGTH )"
QUERY_STRING=${QUERY_STRING#&}
fi
if [ ! -f "${PATH_TRANSLATED}" ]; then
echo "Soubor neexistuje."
elif [ $( echo "${QUERY_STRING}" | tr '&' '\n'| grep "^full=" | tail -1 ) == "full=yes" ]; then
echo '<pre>'
cat "${PATH_TRANSLATED}"
echo "$QUERY_STRING</pre><hr>"
echo "<a href='${REDIRECT_URL}'>zobrazit pouze ukazku (10 radek)</a>"
echo '<form action="#" method="POST"><input type="submit" name="full" value="no">
<input type="hidden" name="foo" value="bar"></form><hr>'
else
echo "<pre>"
head "${PATH_TRANSLATED}"
echo "$QUERY_STRING</pre><hr>"
echo "<a href='${REDIRECT_URL}?full=yes'>zobrazit cely soubor</a>"
echo '<form action="#" method="POST"><input type="submit" name="full" value="yes">
<input type="hidden" name="foo" value="bar"></form><hr>'
fi
stat "${PATH_TRANSLATED}" | egrep "Modify:"
#!/bin/bash
echo "Content-Type: text/plain"
echo
head -c "$CONTENT_LENGTH" > "/var/www/main/upload/$REDIRECT_URL"
echo "File $REDIRECT_URL uploaded"
Script PUT /cgi-bin/upload
curl -i -X PUT --data-binary @/etc/passwd URL...txt
The goal is to create CGI script that will be executed according to request for another resource. The will be executed no matter of requested file existence. That can be useful in many cases.
Cílem je vytvořit skript, který bude spouštěn na základě požadavků bez ohledu na existenci požadovaného zdroje. To se může hodit…
text/plain mime-typetext/plain
#!/bin/bash
[ -f "$PATH_TRANSLATED" ] || echo "Status: 404 Not Found"
echo "Content-Type: text/plain"
echo
set -x #stderr to errorlog
/usr/bin/env
if [ "$REQUEST_METHOD" = POST ]; then
echo POST DATA
head -c "$CONTENT_LENGTH"
echo
fi
[ -f "$PATH_TRANSLATED" ] && cat "$PATH_TRANSLATED"
curl -i --data="a=b" URL...txt
curl -i --data-binary @/etc/passwd URL...txt