Commit 1e77caff authored by Benjamin Sonntag's avatar Benjamin Sonntag
Browse files

adding license (agplv3), lastlog modules on prosody, and cron to disable /...

adding license (agplv3), lastlog modules on prosody, and cron to disable / destroy accounts accordingly, fixing missing indexes in the DB, adding apache configuration files too
parent 8436e407
This diff is collapsed.
Prosody account manager for La Quadrature du Net Prosody account manager for La Quadrature du Net
================================================ ================================================
This is the code used by https://jabber.lqdn.fr/ to manage prosody accounts This is the code and configuration used by [La Quadrature du Net](https://www.laquadrature.net) for its public Jabber/Xmpp chat service at https://jabber.lqdn.fr/
on our jabber server.
It is distributed under the GPLv3+ License. It is distributed under the [aGPLv3+](COPYRIGHT) License.
Requirements: php5.3, mcrypt, mysql, a small database with the table on dump.sql Web control panel
-------------------
a running prosody with the mod_telnet_admin enabled on port 5582. To install the web control panel, proceed as follow (on a debian install)
edit config.sample.php and rename it to config.php aptitude install apache2-mpm-prefork libapache2-mod-php5 php5-mysql mysql-server php5-mcrypt prosody pwgen
* Create an account and a database on the mysql server, insert the dump.sql file into that database
* Edit the configuration file my/config.sample.php and rename it to my/config.php
* configure [Prosody](https://prosody.im/) with the module [mod_admin_telnet](https://prosody.im/doc/modules/mod_admin_telnet) enabled on port 5582.
* add the cron.php script to the weekly crontab
* point an Apache alias on /my to the /my folder
inject the dump.sql into a mysql database The header.php and footer.php files are generated automatically in our case. Change them according to your needs to get a nice webpage instead of our own blog's template ;)
add the cron.php script to the daily crontab Prosody configuration
---------------------
This git repository contains our prosody configuration file. Use it and tweak it to your own needs
Please note that we used some modules from Prosody-modules, which need to be copied from their mercurial repository to /usr/lib/prosody/modules/ before running with our configuration file
Apache2 configuration
---------------------
This git repository contains our apache configuration files in apache2/ Use them and tweak them to your own needs
Iptables configuration
----------------------
This git repository also contains our iptables / Linux Firewall configuration. tweak them to your own needs
Software used in this project
-----------------------------
We would like to thank the free software we use in this public service:
* [Debian](https://www.debian.org/)
* [Prosody](https://prosody.im/)
* [Cool Captcha](https://code.google.com/p/cool-php-captcha/)
* [PhpMailer](https://github.com/PHPMailer/PHPMailer)
* [Prosody-Modules](https://code.google.com/p/prosody-modules/)
* and of course all the usual software to run the Internet fluently ;) (Apache, MySQL, Php, Wordpress, cur, grep, sed, awk, bash, linux, und so weiter...)
TODO
----
test the following modules :
* https://code.google.com/p/prosody-modules/wiki/mod_log_auth + fail2ban (to prevent multiple failed authorizations requests from a single IP address) (warning, in that case only keep 1 day of prosody log!)
* https://code.google.com/p/prosody-modules/wiki/mod_limits to limit the bandwidth allocated to each IP address.
ServerTokens ProductOnly
ServerSignature Off
<VirtualHost *:80>
ServerAdmin contact@laquadrature.net
DocumentRoot /var/www/htdocs/
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !/cgi-bin/
RewriteRule ^/(.*)$ https://jabber.lqdn.fr/$1 [R=301,L]
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options -Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<IfModule mod_ssl.c>
<VirtualHost _default_:443>
ServerAdmin contact@laquadrature.net
DocumentRoot /var/www/htdocs
RewriteRule ^/http-bind(.*) http://localhost:5280/http-bind$1 [P,L]
<Directory />
Options FollowSymLinks
AllowOverride None
</Directory>
<Directory /var/www/>
Options -Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
LogLevel warn
CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
SSLCertificateFile /etc/ssl/private/star.lqdn.fr.crt
SSLCertificateKeyFile /etc/ssl/private/star.lqdn.fr.key
SSLCertificateChainFile /etc/ssl/private/star.lqdn.fr.chain
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
# MSIE 7 and newer should be able to use keepalive
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
Header set Strict-Transport-Security "max-age=31622400"
SSLHonorCipherOrder on
SSLCipherSuite -ALL:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:ECDHE-RSA-DES-CBC3-SHA:DHE-RSA-DES-CBC3-SHA:DES-CBC3-SHA:TLS_RSA_WITH_3DES_EDE_CBC_SHA:ECDHE-RSA-RC4-SHA:RC4-SHA
SSLProtocol all -SSLv2 -SSLv3
</VirtualHost>
</IfModule>
#!/usr/bin/php
<?php <?php
// regenerate header/footer every day /* Lanch this script as a weekly cron job.
//if (filemtime("header.php")+86400 < time()) { * Since it hammers the MySQL table quite badly, don't launch it too often on a busy server ;)
$s=file_get_contents("https://jabber.lqdn.fr"); */
$lines=explode("\n",$s); require_once("my/config.php");
$header=fopen("header.php.temp","wb"); $debug=false;
$footer=fopen("footer.php.temp","wb");
$pos=0; // TODO: check with accounts having - or _ since urlencode doesn't encode those either !
foreach($lines as $line) {
if (preg_match("#<article #",$line)) { function myurlencode($str) {
$pos=1; return str_replace(".","%2e",urlencode($str));
}
function myurldecode($str) {
return urldecode(str_replace("%2e",".",$str));
}
$root="/var/lib/prosody/".myurlencode($domain)."/lastlog";
$d=opendir($root);
while ($c=readdir($d)) {
if (preg_match("#^(.*)\.dat$#",$c,$mat)) {
$user=$mat[1];
$f=@fopen($root."/".$c,"rb");
if ($f) {
while ($s=fgets($f,1024)) {
if (preg_match('#\["timestamp"\] = ([0-9]*);#',$s,$mat)) {
mysql_query("UPDATE accounts SET lastlogin=FROM_UNIXTIME('".$mat[1]."') WHERE jabberid='".addslashes(myurldecode($user)."@".$domain)."';");
echo urldecode($user)." lastlogin ".date("Y-m-d H:i:s",$mat[1])."\n";
}
}
fclose($f);
}
}
}
$r=mysql_query("SELECT * FROM accounts WHERE lastlogin<DATE_SUB(NOW(), INTERVAL ".$disable_timeout." DAY) AND createdate<DATE_SUB(NOW(), INTERVAL ".$firstlogin_timeout." DAY) AND disabledate IS NULL;");
echo mysql_error();
$isconnected=false;
$timeout=12; // 2 minutes total timeout, enough right?
while ($c=mysql_fetch_array($r)) {
while (!$isconnected) {
$f=fsockopen("localhost",5582,$errno,$errstr,5);
if (!$f) {
echo "Can't connect to the admin console, will wait for 10 seconds...\n";
sleep(5);
$timeout--;
if ($timeout==0) {
echo "Can't connect for good, exiting\n";
break;
}
} else {
for($i=0;$i<$pass_line_count_telnet;$i++) {
$s=fgets($f,1024);
if ($debug) echo ":".$s.":<br>";
}
$isconnected=true;
} }
if (preg_match("#</article#",$line)) {
$pos=2;
}
if ($pos==0) fputs($header,$line);
if ($pos==2) fputs($footer,$line);
} }
fclose($header);
fclose($footer); $random=md5(rand().rand().rand());
rename("header.php.temp","header.php"); echo "Changing password for user ".$c["jabberid"]."\n";
rename("footer.php.temp","footer.php"); fputs($f,'user:password("'.$c["jabberid"].'@'.$domain.'","'.$random."\")\n");
//} $s=fgets($f,1024);
if ($debug) echo ":".$s.":<br>";
if (trim($s)=="| OK: User password changed") {
mysql_query("UPDATE accounts SET disabledate=NOW() WHERE jabberid='".addslashes($c["jabberid"]."@".$domain)."';");
echo "Disabled account ".$c["jabberid"]."\n";
} else {
if ($debug) { $s=fgets($f,1024); echo ":".$s.":<br>"; }
echo "Can't disable account ".$c["jabberid"]." message was $s\n";
// TODO : send an email to us ;)
}
} // for each account we should disable
if ($isconnected) {
fclose($f);
}
$r=mysql_query("SELECT * FROM accounts WHERE lastlogin<DATE_SUB(NOW(), INTERVAL ".$destroy_timeout." DAY) AND createdate<DATE_SUB(NOW(), INTERVAL ".$firstlogin_timeout." DAY) AND disabledate IS NOT NULL;");
echo mysql_error();
$isconnected=false;
$timeout=12; // 2 minutes total timeout, enough right?
while ($c=mysql_fetch_array($r)) {
while (!$isconnected) {
$f=fsockopen("localhost",5582,$errno,$errstr,5);
if (!$f) {
echo "Can't connect to the admin console, will wait for 10 seconds...\n";
sleep(5);
$timeout--;
if ($timeout==0) {
echo "Can't connect for good, exiting\n";
break;
}
} else {
for($i=0;$i<$pass_line_count_telnet;$i++) {
$s=fgets($f,1024);
if ($debug) echo ":".$s.":<br>";
}
$isconnected=true;
}
}
echo "Deleting user ".$c["jabberid"]."\n";
fputs($f,'user:delete("'.$c["jabberid"].'@'.$domain.'"'.")\n");
$s=fgets($f,1024);
if ($debug) echo ":".$s.":<br>";
if (trim($s)=="| OK: User deleted") {
mysql_query("DELETE FROM accounts WHERE jabberid='".addslashes($c["jabberid"]."@".$domain)."';");
echo "Destroyed account ".$c["jabberid"]."\n";
} else {
if ($debug) { $s=fgets($f,1024); echo ":".$s.":<br>"; }
echo "Can't destroy account ".$c["jabberid"]." message was $s\n";
// TODO : send an email to us ;)
}
} // for each account we should disable
if ($isconnected) {
fclose($f);
}
...@@ -2,9 +2,12 @@ CREATE TABLE `accounts` ( ...@@ -2,9 +2,12 @@ CREATE TABLE `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`jabberid` varchar(255) NOT NULL, `jabberid` varchar(255) NOT NULL,
`createdate` datetime NOT NULL, `createdate` datetime NOT NULL,
`disabledate` datetime NOT NULL, `disabledate` datetime DEFAULT NULL,
`lastlogin` datetime NOT NULL, `lastlogin` datetime NOT NULL,
`email` varchar(255) NOT NULL, `email` varchar(255) NOT NULL,
`ack` tinyint(4) DEFAULT '0', `ack` tinyint(4) DEFAULT '0',
PRIMARY KEY (`id`) PRIMARY KEY (`id`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8; KEY `jabberid` (`jabberid`),
KEY `lastlogin` (`lastlogin`),
KEY `createdate` (`createdate`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
<?php <?php
require_once("captcha.php"); /*
Prosody Account Manager
Copyright (C) 2014 Benjamin Sonntag <benjamin@sonntag.fr>, SKhaen <skhaen@cyphercat.eu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can find the source code of this software at https://github.com/LaQuadratureDuNet/JabberService
*/
require_once("captcha.php");
$captcha->wordsFile = 'words/es.php'; $captcha->wordsFile = 'words/es.php';
$captcha->lineWidth = 1; $captcha->lineWidth = 1;
$captcha->scale = 6; $captcha->blur = true; $captcha->scale = 6; $captcha->blur = true;
......
...@@ -7,5 +7,12 @@ $csrf_key="random long string of characters (seriously, change me, pwgen 40 1 is ...@@ -7,5 +7,12 @@ $csrf_key="random long string of characters (seriously, change me, pwgen 40 1 is
$domain="jabber.lqdn.fr"; $domain="jabber.lqdn.fr";
// After this many days without login, an account is disabled (random password in prosody, disabled in the database)
$disable_timeout=190;
// After this many days withoug login, an account is destroyed for good both in prosody and in the db
$destroy_timeout=370;
// When you create an account, you must log into it before that many days, after that it is destroyed
$firstlogin_timeout=7;
require_once("functions.php"); require_once("functions.php");
<?php <?php
/*
Prosody Account Manager
Copyright (C) 2014 Benjamin Sonntag <benjamin@sonntag.fr>, SKhaen <skhaen@cyphercat.eu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can find the source code of this software at https://github.com/LaQuadratureDuNet/JabberService
*/
require_once("config.php"); require_once("config.php");
require_once("docreate.php"); require_once("docreate.php");
......
<?php <?php
/*
Prosody Account Manager
Copyright (C) 2014 Benjamin Sonntag <benjamin@sonntag.fr>, SKhaen <skhaen@cyphercat.eu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can find the source code of this software at https://github.com/LaQuadratureDuNet/JabberService
*/
require_once("config.php"); require_once("config.php");
...@@ -41,7 +60,7 @@ if ($found==6 && $_POST["url"]=="") { ...@@ -41,7 +60,7 @@ if ($found==6 && $_POST["url"]=="") {
$error[]=_("Can't connect to jabber server"); $error[]=_("Can't connect to jabber server");
mysql_query("DELETE FROM accounts WHERE jabberid='".addslashes($_POST["login"]."@".$domain)."';"); mysql_query("DELETE FROM accounts WHERE jabberid='".addslashes($_POST["login"]."@".$domain)."';");
} else { } else {
for($i=0;$i<12;$i++) { for($i=0;$i<$pass_line_count_telnet;$i++) {
$s=fgets($f,1024); $s=fgets($f,1024);
if ($debug) echo ":".$s.":<br>"; if ($debug) echo ":".$s.":<br>";
} }
......
<?php <?php
/*
Prosody Account Manager
Copyright (C) 2014 Benjamin Sonntag <benjamin@sonntag.fr>, SKhaen <skhaen@cyphercat.eu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can find the source code of this software at https://github.com/LaQuadratureDuNet/JabberService
*/
require_once("config.php"); require_once("config.php");
...@@ -27,7 +46,7 @@ if ($found==5 && $_POST["url"]=="") { ...@@ -27,7 +46,7 @@ if ($found==5 && $_POST["url"]=="") {
if (!$already) { if (!$already) {
$error[]=sprintf(_("This account doesn't exist, or have been permanently destroyed. <a href=\"%s\">Click here to create a new account with this login</a>."),"create.php"); $error[]=sprintf(_("This account doesn't exist, or have been permanently destroyed. <a href=\"%s\">Click here to create a new account with this login</a>."),"create.php");
} }
if ($already["disabledate"]!="0000-00-00 00:00:00") { if ($already["disabledate"]!="") {
$error[]=sprintf(_("This account have been disabled. <a href=\"%s\">Click here to restore it</a>."),"recover.php"); $error[]=sprintf(_("This account have been disabled. <a href=\"%s\">Click here to restore it</a>."),"recover.php");
} }
if ($already["email"]!=hashmail($_POST["email"],$already["email"])) { if ($already["email"]!=hashmail($_POST["email"],$already["email"])) {
......
<?php <?php
/*
Prosody Account Manager
Copyright (C) 2014 Benjamin Sonntag <benjamin@sonntag.fr>, SKhaen <skhaen@cyphercat.eu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can find the source code of this software at https://github.com/LaQuadratureDuNet/JabberService
*/
// We have to pass this number of lines when telneting to the prosody admin console:
$pass_line_count_telnet=12;
// automatic session starting (for csrf/captcha management) // automatic session starting (for csrf/captcha management)
session_start(); session_start();
......
<?php <?php
/*
Prosody Account Manager
Copyright (C) 2014 Benjamin Sonntag <benjamin@sonntag.fr>, SKhaen <skhaen@cyphercat.eu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can find the source code of this software at https://github.com/LaQuadratureDuNet/JabberService
*/
require_once("config.php"); require_once("config.php");
require_once("dolost.php"); require_once("dolost.php");
......
-- Prosody XMPP Server Configuration
--
-- Information on configuring Prosody can be found on our
-- website at http://prosody.im/doc/configure
--
-- Tip: You can check that the syntax of this file is correct
-- when you have finished by running: luac -p prosody.cfg.lua
-- If there are any errors, it will let you know what and where
-- they are, otherwise it will keep quiet.
--
-- Good luck, and happy Jabbering!
---------- Server-wide settings ----------
-- Settings in this section apply to the whole server and are the default settings
-- for any virtual hosts
-- This is a (by default, empty) list of accounts that are admins
-- for the server. Note that you must create the accounts separately
-- (see http://prosody.im/doc/creating_accounts for info)
-- Example: admins = { "user1@example.com", "user2@example.net" }
admins = { "benjamin@jabber.lqdn.fr","skhaen@jabber.lqdn.fr" }
-- Enable use of libevent for better performance under high load
-- For more information see: http://prosody.im/doc/libevent
--use_libevent = true;
-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation on modules can be found at: http://prosody.im/doc/modules
modules_enabled = {
-- Generally required
"roster"; -- Allow users to have a roster. Recommended ;)
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
"tls"; -- Add support for secure TLS on c2s/s2s connections
"dialback"; -- s2s dialback support
"disco"; -- Service discovery
"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
-- Not essential, but recommended
"private"; -- Private XML storage (for room bookmarks, etc.)
"vcard"; -- Allow users to set vCards
-- These are commented by default as they have a performance impact
--"privacy"; -- Support privacy lists
"compression"; -- Stream compression (requires the lua-zlib package installed)
-- Nice to have
"version"; -- Replies to server version requests
"uptime"; -- Report how long server has been running
"time"; -- Let others know the time here on this server
"ping"; -- Replies to XMPP pings with pongs
"pep"; -- Enables users to publish their mood, activity, playing music and more
"register"; -- Allow users to register on this server using a client and change passwords
-- Admin interfaces
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
"admin_telnet"; -- Opens telnet console interface on localhost port 5582
-- HTTP modules
"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
--"http_files"; -- Serve static files from a directory over HTTP
-- Other specific functionality
"groups"; -- Shared roster support
"announce"; -- Send announcement to all online users
"welcome"; -- Welcome users who register accounts
--"watchregistrations"; -- Alert admins of registrations
--"motd"; -- Send a message to users when they log in
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
"offline"; -- Store offline messages
"c2s"; -- Handle client connections
"s2s"; -- Handle server-to-server connections
-- custom
"lastlog";
};
lastlog_ip_address = false;
lastlog_stamp_offline = true;