Difference between revisions of "Syslog"
m (→TODO) |
m (→In Mysql node) |
||
Line 356: | Line 356: | ||
Now you should be able to login to your http:// ... /syslog with account admin/admin |
Now you should be able to login to your http:// ... /syslog with account admin/admin |
||
+ | <br>But you will face an error since there is no entry yet in your db. |
||
+ | |||
====In Syslog node==== |
====In Syslog node==== |
||
Add to /etc/syslog-ng/syslog-ng.conf the example given in scripts/syslog.conf: |
Add to /etc/syslog-ng/syslog-ng.conf the example given in scripts/syslog.conf: |
Revision as of 21:16, 5 December 2006
Syslog-ng
Install
apt-get install syslog-ng
Example of /etc/syslog-ng/syslog-ng.conf:
Comment kernel source out as we are in a vserver:
source s_all { #file("/proc/kmsg" log_prefix("kernel: "));
I want to keep the original hostnames:
options { keep_hostname(1);
Enable logging per remote host
source net { udp(ip(<local_ip_or_hostname>)); };
Add specific destination files to collect auth.log and syslog.log of every remote (and local) node:
Note that for some we add a template to replace the IP by the hostname in the msgs
destination df_zeus_auth { file("/var/log/remote/MAIN/auth.log" owner("root") group("adm") perm(0640) template("$DATE MAIN $MESSAGE\n")); }; destination df_zeus_syslog { file("/var/log/remote/MAIN/syslog.log" owner("root") group("adm") perm(0640) template("$DATE MAIN $MESSAGE\n")); }; destination df_zeus_kern { file("/var/log/remote/MAIN/kern.log" owner("root") group("adm") perm(0640) template("$DATE MAIN $MESSAGE\n")); }; destination df_mx_auth { file("/var/log/remote/mx/auth.log" owner("root") group("adm") perm(0640)); }; destination df_mx_syslog { file("/var/log/remote/mx/syslog.log" owner("root") group("adm") perm(0640)); }; destination df_public_auth { file("/var/log/remote/public/auth.log" owner("root") group("adm") perm(0640) template("$DATE public $MESSAGE\n")); }; destination df_public_syslog { file("/var/log/remote/public/syslog.log" owner("root") group("adm") perm(0640) template("$DATE public $MESSAGE\n")); }; destination df_private_auth { file("/var/log/remote/private/auth.log" owner("root") group("adm") perm(0640) template("$DATE private $MESSAGE\n")); }; destination df_private_syslog { file("/var/log/remote/private/syslog.log" owner("root") group("adm") perm(0640) template("$DATE private $MESSAGE\n")); }; destination df_ns0_auth { file("/var/log/remote/ns0/auth.log" owner("root") group("adm") perm(0640) template("$DATE ns0 $MESSAGE\n")); }; destination df_ns0_syslog { file("/var/log/remote/ns0/syslog.log" owner("root") group("adm") perm(0640) template("$DATE ns0 $MESSAGE\n")); }; destination df_sql_auth { file("/var/log/remote/sql/auth.log" owner("root") group("adm") perm(0640) template("$DATE sql $MESSAGE\n")); }; destination df_sql_syslog { file("/var/log/remote/sql/syslog.log" owner("root") group("adm") perm(0640) template("$DATE sql $MESSAGE\n")); }; destination df_devel_auth { file("/var/log/remote/devel/auth.log" owner("root") group("adm") perm(0640) template("$DATE devel $MESSAGE\n")); }; destination df_devel_syslog { file("/var/log/remote/devel/syslog.log" owner("root") group("adm") perm(0640) template("$DATE devel $MESSAGE\n")); }; destination df_others_auth { file("/var/log/remote/$HOST/auth.log" owner("root") group("adm") perm(0640)); }; destination df_others_syslog { file("/var/log/remote/$HOST/syslog.log" owner("root") group("adm") perm(0640)); }; destination df_nf_ethr_in { file("/var/log/remote/MAIN/NF/ethr_in.log" owner("root") group("adm") perm(0640) template("$DATE MAIN $MESSAGE\n")); }; destination df_nf_ethr_out { file("/var/log/remote/MAIN/NF/ethr_out.log" owner("root") group("adm") perm(0640) template("$DATE MAIN $MESSAGE\n")); }; destination df_nf_others { file("/var/log/remote/MAIN/NF/others.log" owner("root") group("adm") perm(0640) template("$DATE MAIN $MESSAGE\n")); };
Add filters for remote nodes:
This is quite strange but msgs from the host are seen as coming from the IP of the collecting node
As the source will be net instead of s_all, we can still distinguish both from each other
Filters are sub-string regexps so we have to pay attention that IP x.x.x.1 can catch IP x.x.x.100 if not ended with a $
filter f_zeus { host(192.168.x.x$); }; filter f_public { host(192.168.x.x$); }; filter f_private { host(192.168.x.x$); }; filter f_ns0 { host(192.168.x.x$); }; filter f_devel { host(192.168.x.x$); }; filter f_sql { host(192.168.x.x$); };
For the host which gets the kernel logs, we add filters for the netfilter logs:
filter f_nf { match("NF Dropped "); }; filter f_ethr_in { match("ETH_R IN "); }; filter f_ethr_out { match("ETH_R OUT "); };
Then combine all those in log statements
They all contain the flag final so each entry will be captured only once, this avoids us to write negated filters and this should accelerate a bit the processing
log { source(net); filter(f_zeus); filter(f_auth); destination(df_zeus_auth); flags(final); }; log { source(net); filter(f_zeus); filter(f_kern); filter(f_nf); filter(f_ethr_in); destination(df_nf_ethr_in); flags(final); }; log { source(net); filter(f_zeus); filter(f_kern); filter(f_nf); filter(f_ethr_out); destination(df_nf_ethr_out); flags(final); }; log { source(net); filter(f_zeus); filter(f_kern); filter(f_nf); destination(df_nf_others); flags(final); }; log { source(net); filter(f_zeus); filter(f_kern); destination(df_zeus_kern); flags(final); }; log { source(net); filter(f_zeus); filter(f_syslog); destination(df_zeus_syslog); flags(final); }; # Don't forget ourselves: log { source(s_all); filter(f_auth); destination(df_mx_auth); }; log { source(s_all); filter(f_syslog); destination(df_mx_syslog); }; # Other vservers: log { source(net); filter(f_public); filter(f_auth); destination(df_public_auth); flags(final); }; log { source(net); filter(f_public); filter(f_syslog); destination(df_public_syslog); flags(final); }; log { source(net); filter(f_private); filter(f_auth); destination(df_private_auth); flags(final); }; log { source(net); filter(f_private); filter(f_syslog); destination(df_private_syslog); flags(final); }; log { source(net); filter(f_ns0); filter(f_auth); destination(df_ns0_auth); flags(final); }; log { source(net); filter(f_ns0); filter(f_syslog); destination(df_ns0_syslog); flags(final); }; log { source(net); filter(f_sql); filter(f_auth); destination(df_sql_auth); flags(final); }; log { source(net); filter(f_sql); filter(f_syslog); destination(df_sql_syslog); flags(final); }; log { source(net); filter(f_devel); filter(f_auth); destination(df_devel_auth); flags(final); }; log { source(net); filter(f_devel); filter(f_syslog); destination(df_devel_syslog); flags(final); }; # Catch other remote hosts: log { source(net); filter(f_auth); destination(df_others_auth); flags(final); }; log { source(net); filter(f_syslog); destination(df_others_syslog); flags(final); };
Allow inbound connections from monitoring subnet
iptables -A INPUT -s xxxx -d xxxx -p udp --dport 514 -m state --state NEW -j ACCEPT
On satellite hosts: add to /etc/syslog.conf
*.* @192.168.x.xxx
Resources & Credits
Logrotate
We add a file to rotate our new log files:
/etc/logrotate.d/local-syslog-ng: /var/log/remote/*/*.log { rotate 28 missingok notifempty daily compress }
The call to syslog-ng reload will be done by the native /etc/logrotate.d/syslog-ng so we don't need to do it ourselves
Logcheck
Install
apt-get install logcheck logcheck-database
In /etc/logcheck/logcheck.conf:
INTRO=0 REPORTLEVEL="paranoid" ADDTAG="yes"
In /etc/logcheck/logcheck.logfiles
/var/log/remote/MAIN/auth.log /var/log/remote/MAIN/syslog.log /var/log/remote/MAIN/kern.log /var/log/remote/mx/auth.log /var/log/remote/mx/syslog.log /var/log/remote/public/auth.log /var/log/remote/public/syslog.log /var/log/remote/private/auth.log /var/log/remote/private/syslog.log /var/log/remote/ns0/auth.log /var/log/remote/ns0/syslog.log /var/log/remote/sql/auth.log /var/log/remote/sql/syslog.log /var/log/remote/devel/auth.log /var/log/remote/devel/syslog.log #/var/log/remote/MAIN/NF/ethr_in.log /var/log/remote/MAIN/NF/ethr_out.log /var/log/remote/MAIN/NF/others.log
Because logrotate runs as cron.daily, e.g. 03:25 every day, we want logcheck to have run once just before (e.g. at xx:24) so I edit /etc/cron.d/logcheck:
24 * * * * logcheck if [ -x /usr/sbin/logcheck ]; then nice -n10 /usr/sbin/logcheck; fi
Tuning logcheck filters
Solving the issue at the source
I have many such messages in the vserver:
pam_limits[863]: setrlimit limit #6 to soft=-1, hard=-1 failed: Operation not permitted; uid=0 euid=0
Not sure why, probably because vserver max limits are reduced.
To get rid of it, comment the line in /etc/pam.d/cron and /etc/pam.d/ssh:
#session required pam_limits.so
Writing and testing new rules
Add your rules into files prepend by "local-" to distinguish your own rules.
Be sure to tune ownership and rights of those new files so that user logcheck can read them.
e.g. rw-r----- root:logcheck
To test logcheck filtering rules:
sed -e 's/[[:space:]]*$//' <logfile> | egrep '<regexp>'
Then you can dry run logcheck on the command line:
su logcheck -s /bin/bash -c "/usr/sbin/logcheck -l <logfile> -o -t"
This is easier if you have sudo installed...
Examples of home-made rules
As I run in paranoid mode, I take some rules from server mode:
- /etc/logcheck/ignore.d.paranoid/local-sa-exim -> /etc/logcheck/ignore.d.server/sa-exim
- /etc/logcheck/ignore.d.paranoid/local-fetchmail -> /etc/logcheck/ignore.d.server/fetchmail
For imapd sessions:
/etc/logcheck/ignore.d.paranoid/local-imap: ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ imapd: Connection, ip=\[[:.0-9a-f]+\]$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ imapd: LOGIN, user=[a-z]+, ip=\[[:.0-9a-f]+\], protocol=IMAP$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ imapd: (TIMEOUT|LOGOUT), (user=[a-z]+, )?ip=\[[:.0-9a-f]+\], (headers=[0-9]+, body=[0-9]+, )?rcvd=[0-9]+, sent=[0-9]+(, time=[0-9]+, starttls=1)?$
For imapproxy sessions:
Probably because of the templates in syslog-ng we have to change a bit the rules of ignore.d.server/imapproxy
/etc/logcheck/ignore.d.paranoid/local-imapproxy: ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ in.imapproxyd\[[0-9]+\]: LOGOUT: \\?'\\?"[_[:alnum:]-]+(@[-_.[:alnum:]]+)?\\?"\\?' from server sd \[[0-9]+\]$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ in.imapproxyd\[[0-9]+\]: LOGIN: \\?'\\?"[_[:alnum:]-]+(@[-_.[:alnum:]]+)?\\?"\\?' \([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\) on (existing|new) sd \[[0-9]+\]$ ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ in.imapproxyd\[[0-9]+\]: Expiring server sd \[[0-9]+\]$
For ssh just an excerpt from the ignore.d.server/ssh:
/etc/logcheck/ignore.d.paranoid/local-ssh: ^\w{3} [ :0-9]{11} [._[:alnum:]-]+ sshd\[[0-9]+\]: Accepted (gssapi(-with-mic)?|rsa|dsa|password|publickey|keyboard-interactive/pam) for [^[:space:]]+ from [^[:space:]]+ port [0-9]+( (ssh|ssh2))?$
php-syslog-ng
cf http://www.phpwizardry.com/php-syslog-ng.php
Install
In Apache node
Get http://www.phpwizardry.com/php-syslog-ng/phpsyslogng-2.8.tar.gz
Untar it in /usr/local/share/phpsyslogng
Manage the config file the Debian way: move config/config.php to /etc/phpsyslogng/config.php and make a symlink
In config.php, change at least those lines and assign your passwords:
define('DBUSERPW', 'aaaa'); define('DBADMINPW', 'cccc');
And in our case as the sql is running on a separate node:
define('DBHOST', 'sql');
Create /etc/apache2/sites-available/phpsyslogng with:
Alias /syslog /usr/local/share/phpsyslogng <Directory /usr/local/share/phpsyslogng> Options FollowSymLinks </Directory>
Reload apache2 and go to http:// ... /syslog you will get a help page for the installation process.
In Mysql node
Take scripts/dbsetup.sql from php-syslog-ng
But adapt some lines to our situation:
# create users INSERT INTO user (Host, User, Password) VALUES ('private','sysloguser', password('aaaa')); INSERT INTO db (Host, Db, User) VALUES ('private','syslog','sysloguser'); INSERT INTO user (Host, User, Password) VALUES ('mail','syslogfeeder', password('bbbb')); INSERT INTO db (Host, Db, User) VALUES ('mail','syslog','syslogfeeder'); INSERT INTO user (Host, User, Password) VALUES ('private','syslogadmin',password('cccc')); INSERT INTO db (Host, Db, User) VALUES ('private','syslog','syslogadmin'); COMMIT; FLUSH PRIVILEGES; # grant rights to user syslogadmin for backup purpose GRANT USAGE ON syslog.* TO syslogadmin@private; GRANT ALL ON syslog.* TO syslogadmin@private; GRANT RELOAD ON *.* TO syslogadmin@private; REVOKE ALL PRIVILEGES ON syslog.* FROM sysloguser@private; GRANT USAGE ON syslog.* TO sysloguser@private; GRANT SELECT ON syslog.* TO sysloguser@private; GRANT UPDATE ON syslog.users TO sysloguser@private; REVOKE ALL PRIVILEGES ON syslog.* FROM syslogfeeder@mail; GRANT USAGE ON syslog.* TO syslogfeeder@mail; GRANT INSERT ON syslog.* TO syslogfeeder@mail; GRANT ALL ON syslog.search_cache TO sysloguser@private; GRANT SELECT ON syslog.user_access TO sysloguser@private; GRANT ALL ON syslog.user_access TO syslogadmin@private; GRANT SELECT ON syslog.actions TO sysloguser@private; GRANT ALL ON syslog.actions TO syslogadmin@private;
Then use it:
mysql -uroot -p < dbsetup.sql
Now you should be able to login to your http:// ... /syslog with account admin/admin
But you will face an error since there is no entry yet in your db.
In Syslog node
Add to /etc/syslog-ng/syslog-ng.conf the example given in scripts/syslog.conf:
destination d_mysql { pipe("/var/log/mysql.pipe" template("INSERT INTO logs (host, facility, priority, level, tag, datetime, program, msg) VALUES ( '$HOST', '$FACILITY', '$PRIORITY', '$LEVEL', '$TAG', '$YEAR-$MONTH-$DAY $HOUR:$MIN:$SEC', '$PROGRAM', '$MSG' );\n") template-escape(yes)); };
And the log section but with our additional source:
log { source(s_all); source(net); destination(d_mysql); };
Get inspired by scripts/syslog2mysql.sh to make an init.d script:
- add option "-h sql.vlan2" to mysql client to connect to the remote host
- save the password in a secured file instead of leaving it on the cmd line of mysql, e.g. /etc/syslog-ng/my.cnf with strich access rights (600 root.root)
- script is based on /etc/init.d/skeleton but much more tricky to track PIDs of processes as mysql client is not a daemon!
[client] password="bbbb"
The init.d script itself, to be stored in /etc/init.d/syslog2mysql
#! /bin/sh # Author: Philippe Teuwen # Do NOT "set -e" PATH=/usr/sbin:/usr/bin:/sbin:/bin DESC="Fetch queries from syslog-ng to mysql db" NAME=syslog2mysql PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if mysql client is not installed [ -x "/usr/bin/mysql" ] || exit 0 # Load the VERBOSE setting and other rcS variables [ -f /etc/default/rcS ] && . /etc/default/rcS # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions # # Function that starts the daemon/service # do_start() { [ ! -e /var/log/mysql.pipe ] && echo " (Creating $NAME pipe)." && mkfifo /var/log/mysql.pipe if [ -e $PIDFILE ]; then if ps -p $(cat $PIDFILE) >/dev/null; then echo -n -e "\nError: $NAME seems to be already running!" return 1 else rm -f $PIDFILE fi fi { while [ -e /var/log/mysql.pipe ] do mysql --defaults-file=/etc/syslog-ng/my.cnf -u syslogfeeder -h sql.vlan2 syslog < /var/log/mysql.pipe >/dev/null sleep 1 done } & echo $! > $PIDFILE } # # Function that stops the daemon/service # do_stop() { if [ -e $PIDFILE ]; then PID=$(cat $PIDFILE) if ps -p $PID >dev/null; then # get PID of mysql child CPID=$(pgrep -P $PID) # kill syslog2mysql script kill $PID # kill mysql child kill $CPID rm -f $PIDFILE return 0 else echo -e "\nWarning: $NAME was not running." echo -n -e "\nCleaning PID file" rm -f $PIDFILE return 1 fi else echo -n -e "\nWarning: $NAME was not running" return 1 fi } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" do_start case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" do_stop case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; restart|force-reload) # # If the "reload" option is implemented then remove the # 'force-reload' alias # log_daemon_msg "Restarting $DESC" "$NAME" do_stop case "$?" in 0|1) do_start case "$?" in 0) log_end_msg 0 ;; 1) log_end_msg 1 ;; # Old process is still running *) log_end_msg 1 ;; # Failed to start esac ;; *) # Failed to stop log_end_msg 1 ;; esac ;; *) echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 exit 3 ;; esac :
And create the links to run the script at startup:
update-rc.d syslog2mysql defaults
TODO
- source IP of zeus seems to be 100
- fwlogwatch?