[X2go-Commits] x2gobroker.git - build-main (branch) updated: 0.0.0.2

X2Go dev team git-admin at x2go.org
Thu Feb 21 19:52:06 CET 2013


The branch, build-main has been updated
       via  015ccfd30ce69e3cba7cad23c766f19eb7009ad6 (commit)
       via  d7d2af96575cdf775bf2626e70a55062e4d104ed (commit)
       via  cf5a5148589e8ab8832b3fde9521a3f838200120 (commit)
       via  8fcdf930eddf2dd32a665ff5736f15935920d9aa (commit)
       via  8030518e4bab147e1f7b03feae7c63eff1f1f17c (commit)
       via  93bd345a6cf4618b87d243c86988062c22ece0b4 (commit)
       via  2813264ea2724e15c3bd82722fb2eb5dc1447553 (commit)
       via  74acb0d029a9d94f01251274f939425b0bf9d6b3 (commit)
       via  6d09a1ccc82eab4b46e8133c8d6246f2c4761e69 (commit)
       via  9930f394ccd0ad73bccf3755248b89317227b89e (commit)
       via  c323c09128de3638fb05e26153a14e295a661511 (commit)
       via  8e5f855b236a1483ac6414d1a3782efcf124f2e0 (commit)
       via  9217c85255f9737bfe37a61b4a8457764e190b37 (commit)
       via  40b5a345b349598d4da6bb2492a8f29b7a202dc4 (commit)
       via  a03e031ce619f538ca1f14cd65c3a502ca49ec15 (commit)
       via  e5feb370b34cf26eeb68086377ea295132cb0f70 (commit)
       via  b5b429ae7e66d9194f189495f7a90d92d72ba20d (commit)
       via  858156403553f936da5eb1765a855c12991e8329 (commit)
       via  5e3b7181c636d462daa68aa83ec00a78554808be (commit)
       via  9b7afc801b779c468dffcec904513f8cd6302df9 (commit)
       via  d4b5e9d275a05d7628d00b48acf754c90c8b3bf7 (commit)
       via  d5ae323df36f2fab5dfe9ddfd8643dd9a98c817a (commit)
       via  64dc9fba445fcf69a7ed2d5f28180a112cb3fa91 (commit)
       via  8717014388ce389a78a590b6ef7bf80752f27fba (commit)
       via  a2812b2002642d90235c96462e993f6d5d667642 (commit)
       via  9999ded2eb06bb37af92e788dc4396d4885e9006 (commit)
       via  e1e748e3c3b804f4ddccce138ee12069004e816b (commit)
       via  f703baeb847d1ac9c877faee34f93e08e0af22d6 (commit)
       via  62675904dff3890b3cd24cc9d819d4040bd38530 (commit)
       via  7009b45598b63ddc76192f7fc87bc9a80b15e8bc (commit)
       via  0be15373a8d8833277f8dcdb7274f2e4d425721a (commit)
       via  9ed3a78cd49d01e4c056b8d93ae8b2f3709a00c4 (commit)
       via  169a3bb77b455408af57f65ab47ed1ea63df4287 (commit)
       via  1c5e41f4849a2c8273eb96bf09b5c9993a9c7a3f (commit)
       via  769d5c17f51f20e91f8bff1fb1830fba2ade4e05 (commit)
       via  9a385f3d881a59c8e589c3c373eeba8f66049d6e (commit)
       via  fbb1f8755341c5f318b3e3ee2602487005612e4e (commit)
       via  d9c17a236357d7939415afae5b420917f0e2f212 (commit)
       via  176c5d672d301ed401172f23f5dac5d765d7f2f6 (commit)
       via  bb450a2ea118c06895c26080e1a2fed06ceec5ae (commit)
       via  9f6eaa8a2b8d2901875d87f00d4deb6e5822f2ec (commit)
       via  23c2c59626fbd1c0d5b5cccc3db6387996e35a9c (commit)
       via  afc89fc0421a3061f9caa388e3f0de17a0b41575 (commit)
       via  e62340932ce2e795b38309453f8754cd500ecf4d (commit)
       via  961a085fe915a6b1e6c7809717fa894062bbf927 (commit)
       via  dd6e78151e15db52f78f2d8d9caf66a1ca018149 (commit)
       via  36cadfdd3a58b86b0c0cf79256c446e49a6626c9 (commit)
       via  5dfab3f0e3fd408d2fe24969dd554f9348b62e23 (commit)
       via  a9b66e7a546aebe2d75b7303faae4491c9eb0a15 (commit)
       via  441738987b1baad30e9195c811bce85b3cc6809e (commit)
       via  0aca8cade7297dbe428473bcb5ca4e00d4f4bc10 (commit)
       via  499a783d677c3610974fdffd48573ff5037194a5 (commit)
       via  b25ebf6134b77a3e18ba1f7883c27cd29cebe61b (commit)
       via  1fe7447ef8efa17ac2fd290cc449c6d1b9078edc (commit)
       via  43e490a1fa111dde08d7b05d73fe144c3ca24b52 (commit)
       via  58da6a1d6504def84dccbc9f3328cca439fefa3b (commit)
       via  f1ea959ac2800e88d57018b80b648f7ab48fc9a8 (commit)
       via  80fd7e03f7094ef1dcc3e61d9d4e30a5020ed78a (commit)
       via  b5df92c61e0c84ec314b29acc60e635d43f3b7b0 (commit)
       via  5ef433db9f943afd56b15351cceb2021184ed43e (commit)
       via  b03b7cbfea28b5d962eef483c7fd98661bf203e8 (commit)
       via  abebae8791274f6634adb39d930ae055feed5fbb (commit)
       via  2ce613977c17b31475dc3207652daa00a78f2537 (commit)
       via  d04c71a2781be037ebbb3bdc9441019409c9c2a8 (commit)
       via  bbe2c8ab570f8f3e4b0e54a79117b9d93488de40 (commit)
       via  fe7f06c112518bbac361c7e9f80ed721379a0e3b (commit)
       via  5231ba48183cc603a5193400884dd01b360491ba (commit)
       via  716736e53b9bf284d5bc196b708bc3b2362b50a4 (commit)
       via  d424e129de63d2fd187d1364bf5d2791d74571be (commit)
       via  dc84423e202eba6c1022821b24a25504ee1cab0c (commit)
       via  0e4b85183bd23e6561ba687bb693cf0f3612297d (commit)
       via  fba2b53e36f84b12f7dfcdfe971149415cae3019 (commit)
      from  6d343a60b4adb1041ba8cbef1839e8fd9ec09642 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 Makefile                                           |    6 +-
 debian/changelog                                   |   34 ++++
 debian/control                                     |  135 ++++++++++------
 debian/python-x2gobroker.default                   |   29 ++++
 debian/python-x2gobroker.dirs                      |    3 +-
 debian/python-x2gobroker.install                   |    1 +
 ...roker-agent.postrm => python-x2gobroker.postrm} |   11 +-
 debian/rules                                       |    4 +-
 debian/x2gobroker-agent.install                    |    3 +-
 debian/x2gobroker-agent.manpages                   |    1 +
 debian/x2gobroker-agent.postinst                   |   14 +-
 debian/x2gobroker-agent.postrm                     |   11 +-
 debian/x2gobroker-authservice.default              |   10 ++
 debian/x2gobroker-authservice.init                 |  105 +++++++++++++
 debian/x2gobroker-authservice.install              |    1 +
 debian/x2gobroker-authservice.manpages             |    1 +
 debian/x2gobroker-daemon.default                   |   30 ++--
 debian/x2gobroker-daemon.init                      |  114 +++++++++-----
 ...r-agent.postinst => x2gobroker-daemon.postinst} |   22 ++-
 ...roker-agent.postrm => x2gobroker-daemon.postrm} |   20 ++-
 debian/x2gobroker.install                          |    1 +
 debian/x2gobroker.manpages                         |    2 +
 etc/broker/x2gobroker-loggers.conf                 |   98 ++++++++++++
 etc/broker/x2gobroker-sessionprofiles.conf         |    5 +-
 etc/x2gobroker.conf                                |   47 +++++-
 lib/x2gobroker-agent.pl                            |   44 ++++++
 logrotate/x2gobroker                               |   13 ++
 man/man8/x2gobroker-authservice.8                  |   53 +++++++
 man/man8/x2gobroker-keygen.8                       |   48 ++++++
 man/man8/x2gobroker-pubkeyauthorizer.8             |   45 ++++++
 man/man8/x2gobroker.8                              |   58 +++++++
 sbin/x2gobroker                                    |   55 ++++++-
 sbin/x2gobroker-authservice                        |   86 ++++++++++
 sbin/x2gobroker-keygen                             |  143 +++++++++++++++++
 sbin/x2gobroker-pubkeyauthorizer                   |  144 +++++++++++++++++
 setup.py                                           |    7 +-
 test.py                                            |    1 +
 x2gobroker/__init__.py                             |    5 +-
 x2gobroker/agent.py                                |  165 ++++++++++++++++++++
 x2gobroker/authmechs/pam_authmech.py               |    4 +-
 x2gobroker/authservice.py                          |   99 ++++++++++++
 x2gobroker/brokers/base_broker.py                  |   86 +++++++++-
 x2gobroker/brokers/ldap_broker.py                  |    2 +-
 x2gobroker/brokers/zeroconf_broker.py              |    2 +-
 x2gobroker/config.py                               |    2 +-
 x2gobroker/defaults.py                             |   85 +++++++---
 x2gobroker/loggers.py                              |   72 +++++++++
 x2gobroker/tests/test_broker_base.py               |    2 +
 x2gobroker/tests/test_web_plain_base.py            |    3 -
 x2gobroker/web/extras.py                           |   57 +++++++
 x2gobroker/web/html.py                             |    2 +-
 x2gobroker/web/json.py                             |    3 +-
 x2gobroker/web/plain.py                            |   24 +--
 53 files changed, 1812 insertions(+), 206 deletions(-)
 create mode 100644 debian/python-x2gobroker.default
 copy debian/{x2gobroker-agent.postrm => python-x2gobroker.postrm} (70%)
 create mode 100644 debian/x2gobroker-agent.manpages
 create mode 100644 debian/x2gobroker-authservice.default
 create mode 100755 debian/x2gobroker-authservice.init
 create mode 100644 debian/x2gobroker-authservice.install
 create mode 100644 debian/x2gobroker-authservice.manpages
 mode change 100644 => 100755 debian/x2gobroker-daemon.init
 copy debian/{x2gobroker-agent.postinst => x2gobroker-daemon.postinst} (63%)
 copy debian/{x2gobroker-agent.postrm => x2gobroker-daemon.postrm} (61%)
 create mode 100644 debian/x2gobroker.manpages
 create mode 100644 etc/broker/x2gobroker-loggers.conf
 create mode 100644 logrotate/x2gobroker
 create mode 100644 man/man8/x2gobroker-authservice.8
 create mode 100644 man/man8/x2gobroker-keygen.8
 create mode 100644 man/man8/x2gobroker-pubkeyauthorizer.8
 create mode 100644 man/man8/x2gobroker.8
 create mode 100755 sbin/x2gobroker-authservice
 create mode 100755 sbin/x2gobroker-keygen
 create mode 100755 sbin/x2gobroker-pubkeyauthorizer
 create mode 100644 x2gobroker/agent.py
 create mode 100644 x2gobroker/authservice.py
 create mode 100644 x2gobroker/loggers.py
 create mode 100644 x2gobroker/web/extras.py

The diff of changes is:
diff --git a/Makefile b/Makefile
index 1293dab..07428f6 100755
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,10 @@ INSTALL_DIR=install -d -o root -g root -m 755
 INSTALL_FILE=install -o root -g root -m 644
 INSTALL_PROGRAM=install -o root -g root -m 755
 
+CC      ?= gcc
+CFLAGS  += -fPIE
+LDFLAGS += -pie
+
 RM_FILE=rm -f
 RM_DIR=rmdir -p --ignore-fail-on-non-empty
 
@@ -52,7 +56,7 @@ build: build-arch build-indep
 build-arch: build_setuidwrappers
 
 build_setuidwrappers:
-	gcc -fPIE -pie -o lib/x2gobroker-agent src/x2gobroker-agent.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o lib/x2gobroker-agent src/x2gobroker-agent.c
 
 build-indep:
 #	run setup.py build+install somewhere here...
diff --git a/debian/changelog b/debian/changelog
index e040920..f8f19b0 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,37 @@
+x2gobroker (0.0.0.2-0~x2go1) unstable; urgency=low
+
+  * New upstream version (0.0.0.2):
+    - Make CC, CFLAGS and LDFLAGS configurable through build system.
+    - Make host session option a (Python) list, not unicode/string.
+    - Add load balancing support.
+    - Add file logging support.
+    - Add logrotate configuration for x2gobroker log files.
+    - Make the daemon user and group ID configurable through
+      python-x2gobroker.default.
+    - Set log level to CRITICAL if running unit tests.
+    - Perform PAM authentication via an authentication service (the broker
+      runs as non-privileged user, the authentication service as root).
+    - To make SSH pubkey deployment easier, serve the broker's public SSH key(s)
+      under this URL http(s)://<broker_hostname>:<port>/pubkeys/.
+    - Add tool: x2gobroker-keygen. Generate pub/priv SSH keypair for the
+      system user x2gobroker.
+    - Add tool: x2gobroker-pubkeyauthorizer. Retrive broker's public SSH
+      keys and install them (on X2Go Servers with x2gobroker-agent installed).
+    - Add man pages for all executables in /usr/sbin.
+  * /debian/control:
+    + Add bin:package x2gobroker-agent.
+  * /debian/x2gobroker-daemon.init:
+    + Handle stale PID file and already running daemon.
+    + Handle different situation for X2GOBROKER_DAEMON_USER. Make sure
+      the getpass.getuser() function sees the correct effective UID.
+    + Make sure the unprivileged daemon user (x2gobroker) has access to the
+      PID file directory.
+  * postinst/postrm scripts:
+    + The user x2gobroker is required by x2gobroker-agent and x2gobroker-daemon.
+      So now both bin:packages provide that user account.
+
+ -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Thu, 21 Feb 2013 19:51:30 +0100
+
 x2gobroker (0.0.0.1-0~x2go1) unstable; urgency=low
 
   * Initial upstream version.
diff --git a/debian/control b/debian/control
index 2789bf7..6d78bbd 100644
--- a/debian/control
+++ b/debian/control
@@ -20,8 +20,10 @@ Depends:
  ${misc:Depends},
  ${python:Depends},
  python,
- python-x2gobroker (>= ${source:Version}), python-x2gobroker (<< ${source:Version}.1~),
+ python-argparse,
  python-setproctitle,
+ python-webpy (>= 1:0.37),
+ python-x2gobroker (>= ${source:Version}), python-x2gobroker (<< ${source:Version}.1~),
 Suggests:
  apache2 | httpd,
 Description: X2Go http(s) based session broker (executable)
@@ -45,12 +47,47 @@ Description: X2Go http(s) based session broker (executable)
  .
  This package contains the x2gobroker executable.
 
+Package: x2gobroker-authservice
+Architecture: all
+Depends:
+ python,
+ python-argparse,
+ python-setproctitle,
+ ${misc:Depends},
+ ${python:Depends},
+Suggests:
+ apache2 | httpd,
+Description: X2Go http(s) based session broker (PAM authentication service)
+ X2Go is a server based computing environment with
+    - session resuming
+    - low bandwidth support
+    - LDAP support
+    - client side mass storage mounting support
+    - client side printing support
+    - audio support
+    - authentication by smartcard and USB stick
+ .
+ The session broker is a server tool for X2Go that
+ tells your X2Go Client application in a terminal
+ server cluster what servers and session types are
+ most appropriate for the user in front of the
+ X2Go terminal.
+ .
+ A session broker is most useful in load balanced
+ X2Go server farms.
+ .
+ This package contains the authentication service
+ against the PAM system.
+
 Package: x2gobroker-daemon
 Architecture: all
 Depends:
  ${misc:Depends},
  ${python:Depends},
+ adduser,
  x2gobroker (>= ${source:Version}), x2gobroker (<< ${source:Version}.1~),
+Recommends:
+ x2gobroker-authservice,
 Suggests:
  apache2 | httpd,
 Description: X2Go http(s) based session broker (daemon)
@@ -138,50 +175,52 @@ Description: X2Go http(s) based session broker (Python modules)
  This package contains common files needed by all X2Go session
  brokers being package for this distribution.
 
-#Package: x2gobroker-agent
-#Architecture: any
-#Depends:
-# ${shlibs:Depends},
-# ${misc:Depends},
-# perl,
-# adduser,
-# x2goserver,
-#Description: X2Go http(s) based session broker (common files)
-# X2Go is a server based computing environment with
-#    - session resuming
-#    - low bandwidth support
-#    - LDAP support
-#    - client side mass storage mounting support
-#    - client side printing support
-#    - audio support
-#    - authentication by smartcard and USB stick
-# .
-# The session broker is a server tool for X2Go that
-# tells your X2Go Client application in a terminal
-# server cluster what servers and session types are
-# most appropriate for the user in front of the
-# X2Go terminal.
-# .
-# A session broker is most useful in load balanced
-# X2Go server farms.
-# .
-# This package contains a setuid agent command that
-# is required by the X2Go session broker. This package
-# has to be installed on all X2Go servers that shall get
-# managed via the X2Go session broker.
-# .
-# WARNING: This package installs a setuid wrapper
-# (/usr/lib/x2go/broker/x2gobroker-agent) on your system.
-# This setuid wrapper aims to be a secure replacement
-# for the deprecated suidperl exectuable that was removed
-# from Perl (>= 5.12).
-# .
-# This wrapper is only able to execute the Perl script
-# /usr/lib/x2go/broker/x2gobroker-agent.pl. For running
-# properly, x2gobroker-agent.pl needs setuid privileges.
-# .
-# If you hesitate to install this package, study the code
-# of the named wrapper and the named Perl script beforehand.
-# Note that the X2Go session broker will not work without this
-# x2gobroker-agent component installed on your to-be-managed
-# X2Go servers.
+Package: x2gobroker-agent
+Architecture: any
+Depends:
+ ${shlibs:Depends},
+ ${misc:Depends},
+ perl,
+ python,
+ adduser,
+ python-x2gobroker (>= ${source:Version}), python-x2gobroker (<< ${source:Version}.1~),
+ x2goserver,
+Description: X2Go http(s) based session broker (common files)
+ X2Go is a server based computing environment with
+    - session resuming
+    - low bandwidth support
+    - LDAP support
+    - client side mass storage mounting support
+    - client side printing support
+    - audio support
+    - authentication by smartcard and USB stick
+ .
+ The session broker is a server tool for X2Go that
+ tells your X2Go Client application in a terminal
+ server cluster what servers and session types are
+ most appropriate for the user in front of the
+ X2Go terminal.
+ .
+ A session broker is most useful in load balanced
+ X2Go server farms.
+ .
+ This package contains a setuid agent command that
+ is required by the X2Go session broker. This package
+ has to be installed on all X2Go servers that shall get
+ managed via the X2Go session broker.
+ .
+ WARNING: This package installs a setuid wrapper
+ (/usr/lib/x2go/broker/x2gobroker-agent) on your system.
+ This setuid wrapper aims to be a secure replacement
+ for the deprecated suidperl exectuable that was removed
+ from Perl (>= 5.12).
+ .
+ This wrapper is only able to execute the Perl script
+ /usr/lib/x2go/broker/x2gobroker-agent.pl. For running
+ properly, x2gobroker-agent.pl needs setuid privileges.
+ .
+ If you hesitate to install this package, study the code
+ of the named wrapper and the named Perl script beforehand.
+ Note that the X2Go session broker will not work without this
+ x2gobroker-agent component installed on your to-be-managed
+ X2Go servers.
diff --git a/debian/python-x2gobroker.default b/debian/python-x2gobroker.default
new file mode 100644
index 0000000..f34e313
--- /dev/null
+++ b/debian/python-x2gobroker.default
@@ -0,0 +1,29 @@
+# X2Go Broker Session Broker (common) configuration for Debian
+
+# The posix user/group ID the broker runs under (do not change!)
+# if you change those nonetheless, make sure that the log file
+# directory (default: /var/log/x2gobroker) and files in there are
+# writable by that user
+#X2GOBROKER_DAEMON_USER=x2gobroker
+#X2GOBROKER_DAEMON_GROUP=x2gobroker
+
+# Run X2Go Session Broker in debug mode, this will make the broker
+# available through http GET method calls (otherwise: POST method
+# only) and you will be able to test the broker through your web
+# browser (0=disable, 1=enable).
+#X2GOBROKER_DEBUG=0
+
+# Default X2Go Session Broker backend (available: zeroconf, inifile)
+#X2GOBROKER_DEFAULT_BACKEND=zeroconf
+
+# Path to the X2Go Session Broker's configuration file
+#X2GOBROKER_CONFIG=/etc/x2go/x2gobroker.conf
+
+# Path to the X2Go Session Broker's session profiles file (when using the inifile backend)
+#X2GOBROKER_SESSIONPROFILES=/etc/x2go/broker/x2gobroker-sessionprofiles.conf
+
+# Path to the X2Go Session Broker's agent command
+#X2GOBROKER_AGENT_CMD=/usr/lib/x2go/x2gobroker-agent
+
+# The unix socket file for communication between the broker and the authentication service.
+#X2GOBROKER_AUTHSERVICE_SOCKET=/run/x2gobroker/x2gobroker-authservice.socket
diff --git a/debian/python-x2gobroker.dirs b/debian/python-x2gobroker.dirs
index 2fd303e..2396e1d 100644
--- a/debian/python-x2gobroker.dirs
+++ b/debian/python-x2gobroker.dirs
@@ -1,2 +1,3 @@
 /etc/x2go
-/etc/pam.d
\ No newline at end of file
+/etc/pam.d
+/var/log/x2gobroker
\ No newline at end of file
diff --git a/debian/python-x2gobroker.install b/debian/python-x2gobroker.install
index c8bd09a..91aabf6 100644
--- a/debian/python-x2gobroker.install
+++ b/debian/python-x2gobroker.install
@@ -1,4 +1,5 @@
 etc/x2gobroker.conf etc/x2go/
 etc/broker etc/x2go/
+logrotate/x2gobroker etc/logrotate.d/
 pam/x2gobroker etc/pam.d/
 usr/
diff --git a/debian/x2gobroker-agent.postrm b/debian/python-x2gobroker.postrm
similarity index 70%
copy from debian/x2gobroker-agent.postrm
copy to debian/python-x2gobroker.postrm
index d703d32..22abe93 100755
--- a/debian/x2gobroker-agent.postrm
+++ b/debian/python-x2gobroker.postrm
@@ -18,15 +18,10 @@ set -e
 
 case "$1" in
 	purge)
-		if dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent >/dev/null; then
-			dpkg-statoverride --remove /usr/lib/x2go/x2gobroker-agent
+		if dpkg-statoverride --list /var/log/x2gobroker >/dev/null; then
+			dpkg-statoverride --remove /var/log/x2gobroker
 		fi
 
-		# remove user/group x2gobroker from system
-		getent passwd x2gobroker >/dev/null && deluser x2gobroker
-		getent group x2gobroker >/dev/null && delgroup x2gobroker
-		getent group x2gobroker >/dev/null && delgroup x2gobroker
-
 		;;
 	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
 		;;
@@ -41,4 +36,4 @@ esac
 
 #DEBHELPER#
 
-exit 0
\ No newline at end of file
+exit 0
diff --git a/debian/rules b/debian/rules
index 787fc72..997158b 100755
--- a/debian/rules
+++ b/debian/rules
@@ -21,8 +21,6 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 export DEB_CFLAGS_MAINT_APPEND = -Wall
 include /usr/share/dpkg/buildflags.mk
 
-#DEB_PERL_PACKAGES=libx2go-broker-perl
-#DEB_PERL_CLEAN_TARGET=realclean
-#include /usr/share/cdbs/1/class/perl-makemaker.mk
 include /usr/share/cdbs/1/rules/debhelper.mk
+include /usr/share/cdbs/1/class/makefile.mk
 include /usr/share/cdbs/1/class/python-distutils.mk
diff --git a/debian/x2gobroker-agent.install b/debian/x2gobroker-agent.install
index 2ad94cd..a9bf1d8 100644
--- a/debian/x2gobroker-agent.install
+++ b/debian/x2gobroker-agent.install
@@ -1 +1,2 @@
-lib/x2gobroker-agent* usr/lib/x2go/
\ No newline at end of file
+lib/x2gobroker-agent* usr/lib/x2go/
+sbin/x2gobroker-pubkeyauthorizer usr/sbin/
\ No newline at end of file
diff --git a/debian/x2gobroker-agent.manpages b/debian/x2gobroker-agent.manpages
new file mode 100644
index 0000000..747905f
--- /dev/null
+++ b/debian/x2gobroker-agent.manpages
@@ -0,0 +1 @@
+man/man8/x2gobroker-pubkeyauthorizer.8
\ No newline at end of file
diff --git a/debian/x2gobroker-agent.postinst b/debian/x2gobroker-agent.postinst
index 95faece..3f7c1ed 100755
--- a/debian/x2gobroker-agent.postinst
+++ b/debian/x2gobroker-agent.postinst
@@ -33,11 +33,21 @@ case "$1" in
 		fi
 		if ! getent passwd x2gobroker >/dev/null; then
 			echo "Creating x2gobroker user." >&2
-			adduser --system --no-create-home \
+			adduser --system \
 			        --disabled-password --disabled-login \
-			        --shell /bin/false --group --home /dev/null x2gobroker
+			        --shell /bin/bash --group --home /var/lib/x2gobroker x2gobroker
 		else
 			echo "User x2gobroker already exists." >&2
+			# make sure all settings are appropriate
+			if getent passwd x2gobroker | grep /dev/null 1>/dev/null 2>/dev/null; then
+				mkdir -p /var/lib/x2gobroker
+				usermod --home /var/lib/x2gobroker x2gobroker
+			fi
+			if getent passwd x2gobroker | grep /bin/false 1>/dev/null 2>/dev/null; then
+				usermod --shell /bin/bash x2gobroker
+			fi
+			# make sure the home directory belongs to x2gouser:x2gouser
+			chown x2gobroker:x2gobroker /var/lib/x2gobroker -Rf
 		fi
 
 		# the x2gobroker-agent has to be installed setuid root
diff --git a/debian/x2gobroker-agent.postrm b/debian/x2gobroker-agent.postrm
index d703d32..a9dc429 100755
--- a/debian/x2gobroker-agent.postrm
+++ b/debian/x2gobroker-agent.postrm
@@ -22,10 +22,13 @@ case "$1" in
 			dpkg-statoverride --remove /usr/lib/x2go/x2gobroker-agent
 		fi
 
-		# remove user/group x2gobroker from system
-		getent passwd x2gobroker >/dev/null && deluser x2gobroker
-		getent group x2gobroker >/dev/null && delgroup x2gobroker
-		getent group x2gobroker >/dev/null && delgroup x2gobroker
+		if [ ! -d /usr/share/doc/x2gobroker-daemon ]; then
+			# remove user/group x2gobroker from system (if not in use by x2gobroker-daemon
+			getent passwd x2gobroker >/dev/null && deluser x2gobroker
+			getent group x2gobroker >/dev/null && delgroup x2gobroker
+			getent group x2gobroker >/dev/null && delgroup x2gobroker
+			rm -Rfv /var/lib/x2gobroker
+		fi
 
 		;;
 	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
diff --git a/debian/x2gobroker-authservice.default b/debian/x2gobroker-authservice.default
new file mode 100644
index 0000000..017b75c
--- /dev/null
+++ b/debian/x2gobroker-authservice.default
@@ -0,0 +1,10 @@
+# X2Go Session Broker (PAM Authentication Service) configuration for Debian
+
+# For PAM authentication the X2Go Session Broker needs its authentication
+# service. The session broker itself runs as a non-privileged user (see below)
+# whereas the authentication service must run as super-user root.
+#
+# If you do not use PAM as authentication mechanism with the X2Go Session Broker,
+# you can disable the authentication service here.
+START_AUTHSERVICE=true
+
diff --git a/debian/x2gobroker-authservice.init b/debian/x2gobroker-authservice.init
new file mode 100755
index 0000000..c00c800
--- /dev/null
+++ b/debian/x2gobroker-authservice.init
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Start the X2Go Session Broker PAM Authentication Service
+#
+# Copyright © 2012 Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+# Distributable under the terms of the GNU AGPL version 2.
+#
+### BEGIN INIT INFO
+# Provides:          x2gobroker-authservice
+# Required-Start:    $remote_fs $syslog
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: X2Go Session Broker PAM Authentication Service
+# Description:       PAM authentication service for X2Go Session Broker
+### END INIT INFO
+#
+
+set -eu
+
+AUTHSERVICE=/usr/sbin/x2gobroker-authservice
+test -d /run && RUNDIR=/run || RUNDIR=/var/run
+PIDFILE_AUTHSERVICE=$RUNDIR/x2gobroker/x2gobroker-authservice.pid
+DEBIANCONFIG_COMMON=/etc/default/python-x2gobroker
+DEBIANCONFIG_AUTHSERVICE=/etc/default/x2gobroker-authservice
+
+test -x "$AUTHSERVICE" || exit 0
+
+START_AUTHSERVICE=false
+X2GOBROKER_DEBUG=0
+X2GOBROKER_DAEMON_USER='x2gobroker'
+X2GOBROKER_DAEMON_GROUP='x2gobroker'
+X2GOBROKER_AUTHSERVICE_SOCKET="$RUNDIR/x2gobroker/x2gobroker-authservice.socket"
+test -f $DEBIANCONFIG_COMMON && . $DEBIANCONFIG_COMMON
+test -f $DEBIANCONFIG_AUTHSERVICE && . $DEBIANCONFIG_AUTHSERVICE
+
+if ! getent passwd $X2GOBROKER_DAEMON_USER 1>/dev/null 2>/dev/null; then
+	X2GOBROKER_DAEMON_USER=nobody
+fi
+if ! getent group $X2GOBROKER_DAEMON_GROUP 1>/dev/null 2>/dev/null; then
+	X2GOBROKER_DAEMON_USER=nogroup
+fi
+
+# create PID directory
+mkdir -p $RUNDIR/x2gobroker
+chown $X2GOBROKER_DAEMON_USER:$X2GOBROKER_DAEMON_GROUP $RUNDIR/x2gobroker
+chmod 0770 $RUNDIR/x2gobroker
+
+export X2GOBROKER_DEBUG
+export X2GOBROKER_DAEMON_USER
+export X2GOBROKER_DAEMON_GROUP
+export X2GOBROKER_AUTHSERVICE_SOCKET
+
+. /lib/lsb/init-functions
+
+is_true()
+{
+	case "${1:-}" in
+		[Yy]es|[Yy]|1|[Tt]|[Tt]rue) return 0;;
+		*) return 1;
+	esac
+}
+
+case "${1:-}" in
+	start)
+		if [ -f $PIDFILE_AUTHSERVICE ]; then
+			if ps -u root | grep $(basename $AUTHSERVICE) 1>/dev/null 2>/dev/null; then
+				log_warning_msg "X2Go Broker Authentication Service already running"
+			else
+				log_warning_msg "X2Go Broker Authentication Service: stale PID file ($PIDFILE_AUTHSERVICE). Delete it manually!"
+			fi
+			START_AUTHSERVICE=no
+		fi
+		if is_true $START_AUTHSERVICE; then
+			set +e
+			# once we are here, we have to make sure the authservice.socket does not exist
+			rm -f $X2GOBROKER_AUTHSERVICE_SOCKET
+			# and now we can start the auth service
+			log_daemon_msg "Starting X2Go Broker Authentication Service" "$(basename $AUTHSERVICE)"
+			start-stop-daemon -b -m -S -p $PIDFILE_AUTHSERVICE -x $AUTHSERVICE -- -s $X2GOBROKER_AUTHSERVICE_SOCKET -o root -g $X2GOBROKER_DAEMON_GROUP -p 0660
+			set -e
+			log_end_msg $?
+		fi
+	;;
+	stop)
+		if [ -f $PIDFILE_AUTHSERVICE ] ; then
+			log_daemon_msg "Stopping X2Go Broker Authentication Service" "$(basename $AUTHSERVICE)"
+			set +e
+			start-stop-daemon -K -p $PIDFILE_AUTHSERVICE
+			rm -f $PIDFILE_AUTHSERVICE
+			log_end_msg $?
+			set -e
+		fi
+	;;
+	restart|reload|force-reload)
+		${0:-} stop
+		${0:-} start
+	;;
+	*)
+		echo "Usage: ${0:-} {start|stop|restart|reload|force-reload}" >&2
+		exit 1
+		;;
+esac
+
+exit 0
diff --git a/debian/x2gobroker-authservice.install b/debian/x2gobroker-authservice.install
new file mode 100644
index 0000000..03fb37c
--- /dev/null
+++ b/debian/x2gobroker-authservice.install
@@ -0,0 +1 @@
+sbin/x2gobroker-authservice usr/sbin/
\ No newline at end of file
diff --git a/debian/x2gobroker-authservice.manpages b/debian/x2gobroker-authservice.manpages
new file mode 100644
index 0000000..8b080d0
--- /dev/null
+++ b/debian/x2gobroker-authservice.manpages
@@ -0,0 +1 @@
+man/man8/x2gobroker-authservice.8
\ No newline at end of file
diff --git a/debian/x2gobroker-daemon.default b/debian/x2gobroker-daemon.default
index 47ccb43..bff0410 100644
--- a/debian/x2gobroker-daemon.default
+++ b/debian/x2gobroker-daemon.default
@@ -1,30 +1,20 @@
 # X2Go Session Broker configuration for Debian
 
 # Uncomment to enable the X2Go Session Broker standalone daemon
-START_DAEMON=true
+START_BROKER=true
 
-# run XGo Session Broker in debug mode, this will make the broker
-# available through http GET method calls (otherwise: POST method
-# only) and you will be able to test the broker through your web
-# browser (0=disable, 1=enable).
-#X2GOBROKER_DEBUG=0
-
-# bind standalone daemon to this address:port
+# Bind standalone daemon to this address:port
 #DAEMON_BIND_ADDRESS=127.0.0.1:8080
 
-# default X2Go Session Broker backend (available: zeroconf, inifile)
-#X2GOBROKER_DEFAULT_BACKEND=zeroconf
-
-# path to the X2Go Session Broker's configuration file
-#X2GOBROKER_CONFIG=/etc/x2go/x2gobroker.conf
-
-# path to the X2Go Session Broker's session profiles file (when using the inifile backend)
-#X2GOBROKER_SESSIONPROFILES=/etc/x2go/broker/x2gobroker-sessionprofiles.conf
-
-# path to the X2Go Session Broker's agent command
-#X2GOBROKER_AGENT_CMD=/usr/lib/x2go/x2gobroker-agent
 
-### enable SSL, you will have to create your own SSL certificates ###
+##########################################################
+###                                                    ###
+### Enable SSL Support                                 ###
+###   o You have to create your own SSL certificates   ###
+###   o You have to actively uncomment the below SSL   ###
+###     relevant line to enable https:// in x2gobroker ###
+###                                                    ###
+##########################################################
 
 # SSL certificate file
 #X2GOBROKER_SSL_CERTFILE=/etc/x2go/broker/ssl/broker.crt
diff --git a/debian/x2gobroker-daemon.init b/debian/x2gobroker-daemon.init
old mode 100644
new mode 100755
index 0ee9aed..0a8b90a
--- a/debian/x2gobroker-daemon.init
+++ b/debian/x2gobroker-daemon.init
@@ -7,7 +7,7 @@
 #
 ### BEGIN INIT INFO
 # Provides:          x2gobroker-daemon
-# Required-Start:    $remote_fs $syslog
+# Required-Start:    $remote_fs $syslog x2gobroker-authservice
 # Required-Stop:     $remote_fs $syslog
 # Default-Start:     2 3 4 5
 # Default-Stop:      0 1 6
@@ -19,28 +19,53 @@
 set -eu
 
 DAEMON=/usr/sbin/x2gobroker
-test -d /run && RUNDIR=/run/ || RUNDIR=/var/run
-PIDFILE=$RUNDIR/x2gobroker-daemon.pid
-DEBIANCONFIG=/etc/default/x2gobroker-daemon
+test -d /run && RUNDIR=/run || RUNDIR=/var/run
+PIDFILE_BROKER=$RUNDIR/x2gobroker/x2gobroker-daemon.pid
+DEBIANCONFIG_COMMON=/etc/default/python-x2gobroker
+DEBIANCONFIG_DAEMON=/etc/default/x2gobroker-daemon
 
 test -x "$DAEMON" || exit 0
 
-START_DAEMON=false
+START_BROKER=false
 DAEMON_BIND_ADDRESS=127.0.0.1:8080
 X2GOBROKER_DEBUG=0
+X2GOBROKER_DAEMON_USER='x2gobroker'
+X2GOBROKER_DAEMON_GROUP='x2gobroker'
 X2GOBROKER_DEFAULT_BACKEND="zeroconf"
 X2GOBROKER_CONFIG="/etc/x2go/x2gobroker.conf"
 X2GOBROKER_SESSIONPROFILES="/etc/x2go/broker/x2gobroker-sessionprofiles.conf"
 X2GOBROKER_AGENT_CMD="/usr/lib/x2go/x2gobroker-agent"
+X2GOBROKER_AUTHSERVICE_SOCKET="$RUNDIR/x2gobroker/x2gobroker-authservice.socket"
 X2GOBROKER_SSL_CERTFILE=
 X2GOBROKER_SSL_KEYFILE=
-test -f $DEBIANCONFIG && . $DEBIANCONFIG
+test -f $DEBIANCONFIG_COMMON && . $DEBIANCONFIG_COMMON
+test -f $DEBIANCONFIG_DAEMON && . $DEBIANCONFIG_DAEMON
 
-export X2GOBROKER_DEBUG=0
-export X2GOBROKER_CONFIG="/etc/x2go/x2gobroker.conf"
+if ! getent passwd $X2GOBROKER_DAEMON_USER 1>/dev/null 2>/dev/null; then
+	X2GOBROKER_DAEMON_USER=nobody
+fi
+if ! getent group $X2GOBROKER_DAEMON_GROUP 1>/dev/null 2>/dev/null; then
+	X2GOBROKER_DAEMON_GROUP=nogroup
+fi
+
+# create PID directory
+mkdir -p $RUNDIR/x2gobroker
+chown $X2GOBROKER_DAEMON_USER:$X2GOBROKER_DAEMON_GROUP $RUNDIR/x2gobroker
+chmod 0770 $RUNDIR/x2gobroker
+
+# mend user ID variables when --chuid $X2GOBROKER_DAEMON_USER is used with start-stop-daemon
+export LOGNAME=$X2GOBROKER_DAEMON_USER
+export USER=$X2GOBROKER_DAEMON_USER
+export USERNAME=$X2GOBROKER_DAEMON_USER
+
+export X2GOBROKER_DEBUG
+export X2GOBROKER_DAEMON_USER
+export X2GOBROKER_DAEMON_GROUP
+export X2GOBROKER_CONFIG
 export X2GOBROKER_DEFAULT_BACKEND
-export X2GOBROKER_SESSIONPROFILES="/etc/x2go/broker/x2gobroker-sessionprofiles.conf"
+export X2GOBROKER_SESSIONPROFILES
 export X2GOBROKER_AGENT_CMD
+export X2GOBROKER_AUTHSERVICE_SOCKET
 export X2GOBROKER_SSL_CERTFILE
 export X2GOBROKER_SSL_KEYFILE
 
@@ -48,41 +73,48 @@ export X2GOBROKER_SSL_KEYFILE
 
 is_true()
 {
-  case "${1:-}" in
-    [Yy]es|[Yy]|1|[Tt]|[Tt]rue) return 0;;
-    *) return 1;
-  esac
+	case "${1:-}" in
+		[Yy]es|[Yy]|1|[Tt]|[Tt]rue) return 0;;
+		*) return 1;
+	esac
 }
 
 case "${1:-}" in
-  start)
-    if is_true $START_DAEMON; then
-      log_daemon_msg "Starting X2Go Session Broker standalone daemon" "x2gobroker"
-      mkdir -p $RUNDIR
-      set +e
-      start-stop-daemon -b -m -S -p $PIDFILE -x $DAEMON -- -b $DAEMON_BIND_ADDRESS
-      log_end_msg $?
-      set -e
-    fi
-    ;;
-  stop)
-    if [ -f $PIDFILE ] ; then
-      log_daemon_msg "Stopping X2Go Session Broker standalone daemon" "x2gobroker"
-      set +e
-      start-stop-daemon -K -p $PIDFILE
-      rm -f $PIDFILE
-      log_end_msg $?
-      set -e
-    fi
-    ;;
-  restart|reload|force-reload)
-    ${0:-} stop
-    ${0:-} start
-    ;;
-  *)
-    echo "Usage: ${0:-} {start|stop|restart|reload|force-reload}" >&2
-    exit 1
-    ;;
+	start)
+		if [ -f $PIDFILE_BROKER ]; then
+			if ps -u $X2GOBROKER_DAEMON_USER | grep $(basename $DAEMON) 1>/dev/null 2>/dev/null; then
+				log_warning_msg "X2Go Session Broker already running"
+			else
+				log_warning_msg "X2Go Session Broker: stale PID file ($PIDFILE_BROKER). Delete it manually!"
+			fi
+			START_BROKER=no
+		fi
+		if is_true $START_BROKER; then
+			log_daemon_msg "Starting X2Go Session Broker standalone daemon" "$(basename $DAEMON)"
+			set +e
+			start-stop-daemon --chuid $X2GOBROKER_DAEMON_USER -b -m -S -p $PIDFILE_BROKER -x $DAEMON -- -b $DAEMON_BIND_ADDRESS
+			log_end_msg $?
+			set -e
+		fi
+	;;
+	stop)
+		if [ -f $PIDFILE_BROKER ] ; then
+			log_daemon_msg "Stopping X2Go Session Broker standalone daemon" "$(basename $DAEMON)"
+			set +e
+			start-stop-daemon -K -p $PIDFILE_BROKER
+			rm -f $PIDFILE_BROKER
+			log_end_msg $?
+			set -e
+		fi
+	;;
+	restart|reload|force-reload)
+		${0:-} stop
+		${0:-} start
+	;;
+	*)
+		echo "Usage: ${0:-} {start|stop|restart|reload|force-reload}" >&2
+		exit 1
+		;;
 esac
 
 exit 0
diff --git a/debian/x2gobroker-agent.postinst b/debian/x2gobroker-daemon.postinst
similarity index 63%
copy from debian/x2gobroker-agent.postinst
copy to debian/x2gobroker-daemon.postinst
index 95faece..b37c2f5 100755
--- a/debian/x2gobroker-agent.postinst
+++ b/debian/x2gobroker-daemon.postinst
@@ -1,5 +1,5 @@
 #!/bin/sh
-# postinst script for x2gobroker-agent
+# postinst script for x2gobroker-daemon
 #
 # see: dh_installdeb(1)
 
@@ -33,16 +33,26 @@ case "$1" in
 		fi
 		if ! getent passwd x2gobroker >/dev/null; then
 			echo "Creating x2gobroker user." >&2
-			adduser --system --no-create-home \
+			adduser --system \
 			        --disabled-password --disabled-login \
-			        --shell /bin/false --group --home /dev/null x2gobroker
+			        --shell /bin/bash --group --home /var/lib/x2go x2gobroker
 		else
 			echo "User x2gobroker already exists." >&2
+			# make sure all settings are appropriate
+			if getent passwd x2gobroker | grep /dev/null 1>/dev/null 2>/dev/null; then
+				mkdir -p /var/lib/x2gobroker
+				usermod --home /var/lib/x2gobroker x2gobroker
+			fi
+			if getent passwd x2gobroker | grep /bin/false 1>/dev/null 2>/dev/null; then
+				usermod --shell /bin/bash x2gobroker
+			fi
+			# make sure the home directory belongs to x2gouser:x2gouser
+			chown x2gobroker:x2gobroker /var/lib/x2gobroker -Rf
 		fi
 
-		# the x2gobroker-agent has to be installed setuid root
-		if ! dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent >/dev/null; then
-		    dpkg-statoverride --add --update root x2gobroker 4750 /usr/lib/x2go/x2gobroker-agent
+		# the x2gobroker-daemon needs special permissions on its log directory
+		if ! dpkg-statoverride --list /var/log/x2gobroker >/dev/null; then
+			dpkg-statoverride --add --update x2gobroker adm 2750 /var/log/x2gobroker
 		fi
 
 		;;
diff --git a/debian/x2gobroker-agent.postrm b/debian/x2gobroker-daemon.postrm
similarity index 61%
copy from debian/x2gobroker-agent.postrm
copy to debian/x2gobroker-daemon.postrm
index d703d32..42f3752 100755
--- a/debian/x2gobroker-agent.postrm
+++ b/debian/x2gobroker-daemon.postrm
@@ -1,5 +1,5 @@
 #! /bin/sh
-# postrm script for x2gobroker-agent
+# postrm script for x2gobroker-daemon
 #
 # see: dh_installdeb(1)
 # summary of how this script can be called:
@@ -18,14 +18,18 @@ set -e
 
 case "$1" in
 	purge)
-		if dpkg-statoverride --list /usr/lib/x2go/x2gobroker-agent >/dev/null; then
-			dpkg-statoverride --remove /usr/lib/x2go/x2gobroker-agent
+
+		if dpkg-statoverride --list /var/log/x2gobroker >/dev/null; then
+			dpkg-statoverride --remove /var/log/x2gobroker
 		fi
 
-		# remove user/group x2gobroker from system
-		getent passwd x2gobroker >/dev/null && deluser x2gobroker
-		getent group x2gobroker >/dev/null && delgroup x2gobroker
-		getent group x2gobroker >/dev/null && delgroup x2gobroker
+		if [ ! -d /usr/share/doc/x2gobroker-agent ]; then
+			# remove user/group x2gobroker from system (only if not in use by x2gobroker-agent
+			getent passwd x2gobroker >/dev/null && deluser x2gobroker
+			getent group x2gobroker >/dev/null && delgroup x2gobroker
+			getent group x2gobroker >/dev/null && delgroup x2gobroker
+			rm -Rf /var/lib/x2gobroker
+		fi
 
 		;;
 	remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
@@ -41,4 +45,4 @@ esac
 
 #DEBHELPER#
 
-exit 0
\ No newline at end of file
+exit 0
diff --git a/debian/x2gobroker.install b/debian/x2gobroker.install
index fac20e4..693dad3 100644
--- a/debian/x2gobroker.install
+++ b/debian/x2gobroker.install
@@ -1 +1,2 @@
 sbin/x2gobroker usr/sbin/
+sbin/x2gobroker-keygen usr/sbin/
\ No newline at end of file
diff --git a/debian/x2gobroker.manpages b/debian/x2gobroker.manpages
new file mode 100644
index 0000000..569dc33
--- /dev/null
+++ b/debian/x2gobroker.manpages
@@ -0,0 +1,2 @@
+man/man8/x2gobroker.8
+man/man8/x2gobroker-keygen.8
diff --git a/etc/broker/x2gobroker-loggers.conf b/etc/broker/x2gobroker-loggers.conf
new file mode 100644
index 0000000..87d7dca
--- /dev/null
+++ b/etc/broker/x2gobroker-loggers.conf
@@ -0,0 +1,98 @@
+# This file is part of the  X2Go Project - http://www.x2go.org
+# Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2011-2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# WARNING: only modify this file if you _exactly_ know what you are doing!!!
+
+[loggers]
+keys=root,broker,authservice,access,error
+
+[logger_root]
+level=NOTSET
+handlers=stderrHandler
+
+[handlers]
+keys=stderrHandler,brokerFileHandler,authserviceFileHandler,accessFileHandler,errorFileHandler
+
+[formatters]
+keys=brokerFormatter,authserviceFormatter,accessFormatter,errorFormatter
+
+[handler_stderrHandler]
+class=StreamHandler
+args=(sys.stderr,)
+
+[logger_broker]
+level=DEBUG
+handlers=brokerFileHandler
+qualname=broker
+propagate=0
+
+[handler_brokerFileHandler]
+class=FileHandler
+formatter=brokerFormatter
+args=('/var/log/x2gobroker/broker.log',)
+
+[formatter_brokerFormatter]
+format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
+datefmt=
+
+[logger_authservice]
+level=DEBUG
+handlers=authserviceFileHandler
+qualname=authservice
+propagate=0
+
+[handler_authserviceFileHandler]
+class=FileHandler
+formatter=authserviceFormatter
+args=('/var/log/x2gobroker/authservice.log',)
+
+[formatter_authserviceFormatter]
+format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
+datefmt=
+
+[logger_access]
+level=DEBUG
+handlers=accessFileHandler
+qualname=access
+propagate=0
+
+[handler_accessFileHandler]
+class=FileHandler
+formatter=accessFormatter
+args=('/var/log/x2gobroker/access.log',)
+
+[formatter_accessFormatter]
+format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
+datefmt=
+
+[logger_error]
+level=DEBUG
+handlers=errorFileHandler
+qualname=error
+propagate=0
+
+[handler_errorFileHandler]
+class=FileHandler
+formatter=accessFormatter
+args=('/var/log/x2gobroker/error.log',)
+
+[formatter_errorFormatter]
+format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
+datefmt=
diff --git a/etc/broker/x2gobroker-sessionprofiles.conf b/etc/broker/x2gobroker-sessionprofiles.conf
index 8b332a3..d797697 100644
--- a/etc/broker/x2gobroker-sessionprofiles.conf
+++ b/etc/broker/x2gobroker-sessionprofiles.conf
@@ -55,7 +55,7 @@ sshport=22
 setdpi=0
 pack=16m-jpeg
 
-## 
+##
 ## pool-A (staff servers)
 ##
 ## The pool-A contains three X2Go Servers (server-A, server-B and server-C).
@@ -144,8 +144,7 @@ acl-any-order=deny-allow
 [pool-C-XFCE]
 user=
 # no load balancing support, yet
-#host=s-E1.pool-c.domain.local,s-E2.pool-c.domain.local,s-E3.pool-c.domain.local,s-E4.pool-c.domain.local,s-E5.pool-c.domain.local,s-E6.pool-c.domain.local
-host=s-E1.pool-c.domain.local
+host=s-E1.pool-c.domain.local,s-E2.pool-c.domain.local,s-E3.pool-c.domain.local,s-E4.pool-c.domain.local,s-E5.pool-c.domain.local,s-E6.pool-c.domain.local
 name=XFCE - pool-C
 command=XFCE
 acl-users-allow=testuser-A,testuser-B
diff --git a/etc/x2gobroker.conf b/etc/x2gobroker.conf
index 0999376..b512cf4 100644
--- a/etc/x2gobroker.conf
+++ b/etc/x2gobroker.conf
@@ -88,7 +88,51 @@
 # on large deployments it is recommended to ignore primary group memberships
 # traversing into all user accounts for primary group detection can be quite
 # CPU intensive on the X2Go Broker server.
-#ignore-primary-group-memberships = True
+#ignore-primary-group-memberships = true
+
+# default X2Go Broker Agent query mode:
+#
+# The X2Go Broker Agent is needed for multi-server sites configured for
+# load balancing. Multi-server sites require a setup that uses the
+# PostgreSQL X2Go session DB backend. The X2Go Broker Agent has to be installed
+# on the local system (mode: LOCAL) or on all X2Go Servers (mode: SSH) in a
+# multi-server farm.
+#
+# So, there are two query modes for the X2GO Broker Agent: LOCAL and SSH.
+#
+#    LOCAL - This LOCAL mode only works for _one_ configured multi-server farm.
+#            If the locally installed X2Go Session Broker is to server many different
+#            multi-server farms, then the LOCAL mode will not work!!!
+#
+#            How it works: Assume that the local system has an X2Go Broker Agent
+#            that knows about the multi-server setup. This means: X2Go Server has
+#            to be installed locally and the X2Go Server has to be configured to
+#            use the multi-server farms PostgreSQL session DB backend.
+#
+#            The local system that is running the broker does not necessarily have
+#            to be a real application server. It only has to be aware of running/suspended
+#            sessions within the X2Go multi-server farm setup.
+#
+#            A typical use-case is X2Go on top of a Debian Edu Terminal-Server farm:
+#
+#              TJENER -> PostgreSQL DB, X2Go Server, X2Go Session Broker + Broker Agent
+#              TS01 - TS0X -> X2Go Server configured to use the PostgreSQL DB on TJENER
+#
+#    SSH   - The more generic approach, but also more complex. It allows that the broker
+#            on this system may serve for many different X2Go Server multi-server setups.
+#
+#            With the SSH agent query mode, the X2Go Session Broker will query one of the X2Go
+#            Servers in the targeted multi-server setup (through SSH). The SSH authentication
+#            is done by a system user account (normally UID=x2gobroker) and SSH pub/priv
+#            key authentication has to be configured to make this work.
+#
+#            All X2Go Servers in a multi-server farm need the X2Go Broker Agent installed,
+#            whereas this local system running the X2Go Session Broker does not need a
+#            local X2Go Broker Agent at all.
+
+# The agent query mode can be configured on a per-broker-backend basis, the below value is
+# the default.
+#default-agent-query-mode=LOCAL
 
 ###
 ### BACKEND section
@@ -136,3 +180,4 @@
 #host-search-filter = (&(objectClass=ipHost)(serial=X2GoServer)(cn=*))
 #group-search-filter = (&(objectClass=posifxGroup)(cn=*))
 #starttls = false
+#agent-query-mode = SSH
diff --git a/lib/x2gobroker-agent.pl b/lib/x2gobroker-agent.pl
index 6a64bbf..3155837 100755
--- a/lib/x2gobroker-agent.pl
+++ b/lib/x2gobroker-agent.pl
@@ -98,6 +98,50 @@ if($mode eq 'listsessions')
 	exec ("/bin/su - $uid -c \"x2golistsessions --all-servers\"");
 }
 
+if( ($mode eq 'findbusyservers_by_sessionstats') || ($mode eq 'findbusyservers'))
+{
+
+	# Normally the session broker setup knows about all servers,
+	# make sure your configuration of X2Go Session Broker is correct and
+	# lists all available servers.
+
+	# The findbusyservers algorithm only returns servers that are currently
+	# in use (i.e. have running or suspended sessions on them). So the
+	# result may be empty or contain a server list not containing all
+	# available servers.
+
+	# The logic of findbusyservers is this, then:
+	#   1. if no server is returned, any of the configured servers is best server
+	#   2. if some servers are returned, a best server is one that is not returned
+	#   3. if all configured servers are returned, than evaluate the usage value
+	#      (e.g. 90:server1, 20:server2, 10:server3 -> best server is server3)
+
+	# The above interpretation has to be handled by the broker implementation
+	# calling »x2gobroker-agent findbusyservers«.
+
+	InitX2GoUser($uid, $uidNumber, $gidNumber, $home);
+	print "OK\n";
+	my $busy_servers = `/bin/su - $uid -c \"x2gogetservers\"`;
+
+	my %server_load = ();
+	my $num_sessions = 0;
+	foreach (split('\n', $busy_servers))
+	{
+		my ($hostname, $num_users) = split(' ', $_);
+		$server_load{$hostname} = $num_users;
+		$num_sessions += $num_users;
+	}
+
+	# render the output result
+	my @result;
+	for my $hostname ( keys %server_load ) {
+		my $available = $server_load{$hostname}/$num_sessions*100;
+		push @result, sprintf '%1$d:%2$s', $available, $hostname;
+	}
+	print join("\n", sort @result);
+	print "\n";
+}
+
 
 if($mode eq 'getservers')
 {
diff --git a/logrotate/x2gobroker b/logrotate/x2gobroker
new file mode 100644
index 0000000..4e8c689
--- /dev/null
+++ b/logrotate/x2gobroker
@@ -0,0 +1,13 @@
+/var/log/x2gobroker/*.log {
+	weekly
+	missingok
+	rotate 52
+	compress
+	delaycompress
+	notifempty
+	create 640 x2gobroker adm
+	sharedscripts
+	postrotate
+		/etc/init.d/x2gobroker restart > /dev/null
+	endscript
+}
diff --git a/man/man8/x2gobroker-authservice.8 b/man/man8/x2gobroker-authservice.8
new file mode 100644
index 0000000..59a3938
--- /dev/null
+++ b/man/man8/x2gobroker-authservice.8
@@ -0,0 +1,53 @@
+'\" -*- coding: utf-8 -*-
+.if \n(.g .ds T< \\FC
+.if \n(.g .ds T> \\F[\n[.fam]]
+.de URL
+\\$2 \(la\\$1\(ra\\$3
+..
+.if \n(.g .mso www.tmac
+.TH x2gobroker-authservice 8 "Feb 2013" "Version 0.0.1.y" "X2Go Session Broker"
+.SH NAME
+x2gobroker-authservice \- PAM authentication service for X2Go Session Broker
+.SH SYNOPSIS
+'nh
+.fi
+.ad l
+\fBx2gobroker-authservice\fR \kx
+.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
+'in \n(.iu+\nxu
+[
+\fIoptions\fR
+]
+'in \n(.iu-\nxu
+.ad b
+'hy
+.SH DESCRIPTION
+\fBx2gobroker-authservice\fR is a PAM authentication service for X2Go Session Broker. Whereas
+the X2Go Session Broker runs as a non-privileged user (standalone daemon mode) or as the
+also non-privileged httpd server's system user (WSGI mode), an authentication against PAM
+requires root privileges in most cases (esp. for pam_unix.so).
+.PP
+Thus, the PAM authentication has been moved into a separate service. The communication
+between X2Go Session Broker and PAM Authentication Service is handled through a
+unix domain socket file ($RUNDIR/x2gobroker/x2gobroker-authservice.socket).
+.PP
+This command is normally started through an init script.
+.SH COMMON OPTIONS
+\fBx2gobroker-authservice\fR accepts the following common options:
+.TP
+\*(T<\fB\-h, \-\-help\fR\*(T>
+Display a help with all available command line options and exit.
+.TP
+\*(T<\fB\-s <AUTHSOCKET>, \-\-socket <AUTHSOCKET>\fR\*(T>
+File name of the unix domain socket file used for communication between broker and authentication service.
+\*(T<\fB\-o <OWNER>, \-\-owner <OWNER>\fR\*(T>
+User ownership of the \fI<AUTHSOCKET>\fR file.
+\*(T<\fB\-g <GROUP>, \-\-group <GROUP>\fR\*(T>
+Group ownership of the \fI<AUTHSOCKET>\fR file.
+\*(T<\fB\-p <PERMISSIONS>, \-\-permissions <PERMISSIONS>\fR\*(T>
+Set these file permissions for the \fI<AUTHSOCKET>\fR file. Use numerical permissions (e.g. 0640).
+.SH "FILES"
+$RUNDIR/x2gobroker/x2gobroker-authservice.socket
+.SH AUTHOR
+This manual has been written for the X2Go project by
+Mike Gabriel <mike.gabriel at das-netzwerkteam.de>.
diff --git a/man/man8/x2gobroker-keygen.8 b/man/man8/x2gobroker-keygen.8
new file mode 100644
index 0000000..b17b5d2
--- /dev/null
+++ b/man/man8/x2gobroker-keygen.8
@@ -0,0 +1,48 @@
+'\" -*- coding: utf-8 -*-
+.if \n(.g .ds T< \\FC
+.if \n(.g .ds T> \\F[\n[.fam]]
+.de URL
+\\$2 \(la\\$1\(ra\\$3
+..
+.if \n(.g .mso www.tmac
+.TH x2gobroker-keygen 8 "Feb 2013" "Version 0.0.1.y" "X2Go Session Broker"
+.SH NAME
+x2gobroker-keygen \- Generate SSH keys for X2Go Session Broker
+.SH SYNOPSIS
+'nh
+.fi
+.ad l
+\fBx2gobroker-keygen\fR \kx
+.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
+'in \n(.iu+\nxu
+[
+\fIoptions\fR
+]
+'in \n(.iu-\nxu
+.ad b
+'hy
+.SH DESCRIPTION
+\fBx2gobroker-keygen\fR generates a SSH pub/priv key pair and makes it usable through
+X2Go Session Broker.
+.PP
+This command has to be execute once per broker installation and requires root privileges.
+.SH COMMON OPTIONS
+\fBx2gobroker-keygen\fR accepts the following common options:
+.TP
+\*(T<\fB\-h, \-\-help\fR\*(T>
+Display a help with all available command line options and exit.
+.TP
+.TP
+\*(T<\fB\-t {RSA,DSA}, \-\-key_type {RSA,DSA}\fR\*(T>
+SSH pub/priv key type (allowed values: RSA, DSA).
+.TP
+\*(T<\fB\-f, \-\-force\fR\*(T>
+Re-generate SSH pub/priv key pair and enforce overwriting of existing key pair files.
+
+WARNING: you will loose previously create key files when this option is used.
+.SH "FILES"
+~x2gobroker/.ssh/*
+.PP
+.SH AUTHOR
+This manual has been written for the X2Go project by
+Mike Gabriel <mike.gabriel at das-netzwerkteam.de>.
diff --git a/man/man8/x2gobroker-pubkeyauthorizer.8 b/man/man8/x2gobroker-pubkeyauthorizer.8
new file mode 100644
index 0000000..4602ce8
--- /dev/null
+++ b/man/man8/x2gobroker-pubkeyauthorizer.8
@@ -0,0 +1,45 @@
+'\" -*- coding: utf-8 -*-
+.if \n(.g .ds T< \\FC
+.if \n(.g .ds T> \\F[\n[.fam]]
+.de URL
+\\$2 \(la\\$1\(ra\\$3
+..
+.if \n(.g .mso www.tmac
+.TH x2gobroker-pubkeyauthorizer 8 "Feb 2013" "Version 0.0.1.y" "X2Go Session Broker"
+.SH NAME
+x2gobroker-pubkeyauthorizer \- Retrieve public SSH keys from an X2Go Session Broker
+.SH SYNOPSIS
+'nh
+.fi
+.ad l
+\fBx2gobroker-pubkeyauthorizer\fR \kx
+.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
+'in \n(.iu+\nxu
+[
+\fIoptions\fR
+]
+'in \n(.iu-\nxu
+.ad b
+'hy
+.SH DESCRIPTION
+\fBx2gobroker-pubkeyauthorizer\fR retrieves the X2Go Session Broker's public SSH key(s) and
+adds it to ~x2gobroker/.ssh/authorized_keys.
+.PP
+This command has to be executed once on each X2Go Server that is to become a member of a muli-node
+X2Go server farm. The execution of this command requires root-privileges.
+.PP
+.SH COMMON OPTIONS
+\fBx2gobroker-pubkeyauthorizer\fR accepts the following common options:
+.TP
+\*(T<\fB\-h, \-\-help\fR\*(T>
+Display a help with all available command line options and exit.
+.TP
+\*(T<\fB\-b <BROKER_URL>, \-\-broker-url <BROKER_URL>\fR\*(T>
+The URL of the X2Go Session Broker that we want to retrieve public keys from.
+The common pattern for this URL is http(s)://<broker_hostname>:<port>/pubkeys/, but may differ
+depending on your X2Go Session Broker setup.
+.SH "FILES"
+~x2gobroker/.ssh/authorized_keys
+.SH AUTHOR
+This manual has been written for the X2Go project by
+Mike Gabriel <mike.gabriel at das-netzwerkteam.de>.
diff --git a/man/man8/x2gobroker.8 b/man/man8/x2gobroker.8
new file mode 100644
index 0000000..8811c2b
--- /dev/null
+++ b/man/man8/x2gobroker.8
@@ -0,0 +1,58 @@
+'\" -*- coding: utf-8 -*-
+.if \n(.g .ds T< \\FC
+.if \n(.g .ds T> \\F[\n[.fam]]
+.de URL
+\\$2 \(la\\$1\(ra\\$3
+..
+.if \n(.g .mso www.tmac
+.TH x2gobroker 8 "Feb 2013" "Version 0.0.1.y" "X2Go Session Broker"
+.SH NAME
+x2gobroker \- Session Broker for X2Go
+.SH SYNOPSIS
+'nh
+.fi
+.ad l
+\fBx2gobroker\fR \kx
+.if (\nx>(\n(.l/2)) .nr x (\n(.l/5)
+'in \n(.iu+\nxu
+[
+\fIoptions\fR
+]
+'in \n(.iu-\nxu
+.ad b
+'hy
+.SH DESCRIPTION
+\fBx2gobroker\fR is a Web.py based implementation of the X2Go Session Broker API.
+.PP
+This command is normally executed either through an init script or via the WSGI module in your httpd server.
+.PP
+See the included README and TODO for further information on \fBx2gobroker\fR.
+.SH COMMON OPTIONS
+\fBx2gobroker\fR accepts the following common options:
+.TP
+\*(T<\fB\-h, \-\-help\fR\*(T>
+Display a help with all available command line options and exit.
+.TP
+\*(T<\fB\-d, \-\-debug\fR\*(T>
+Enable debugging code. This also makes http\'s POST method available as GET method, which then allows it to test the
+broker API through a normal web browser.
+.TP
+\*(T<\fB\-d, \-\-config FILENAME\fR\*(T>
+Specify an alternative configuration file name, default is: \fI/etc/x2go/x2gobroker.conf\fR.
+.SH DAEMON OPTIONS
+\fBx2gobroker\fR accepts the following daemon options:
+.TP
+\*(T<\fB\-b, \-\-bind ADDRESS:PORT\fR\*(T>
+The [address:]port that the Web.py http-engine will bind to (default: 127.0.0.1:8080).
+.SH "FILES"
+/etc/x2go/x2gobroker.conf, /etc/x2go/broker/* (configuration files)
+.PP
+/etc/default/x2gobroker-daemon (environment for X2Go Session Broker when run
+as standalone daemon)
+.PP
+/var/log/x2gobroker/* (log files of X2Go Session Broker)
+.SH "SEE ALSO"
+\fB/usr/share/doc/x2gobroker\fR
+.SH AUTHOR
+This manual has been written for the X2Go project by
+Mike Gabriel <mike.gabriel at das-netzwerkteam.de>.
diff --git a/sbin/x2gobroker b/sbin/x2gobroker
index 5c1fdd8..846a7e5 100755
--- a/sbin/x2gobroker
+++ b/sbin/x2gobroker
@@ -1,4 +1,5 @@
 #!/usr/bin/env python
+# -*- coding: utf-8 -*-
 
 # This file is part of the  X2Go Project - http://www.x2go.org
 # Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
@@ -20,10 +21,11 @@
 # Free Software Foundation, Inc.,
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
-import sys
 import os
+import sys
 import argparse
 import socket
+import logging
 import web
 from web.wsgiserver import CherryPyWSGIServer
 
@@ -33,6 +35,35 @@ except ImportError:
     sys.path.insert(0, os.path.join(os.getcwd(), '..'))
     import x2gobroker.defaults
 
+from x2gobroker import __VERSION__
+from x2gobroker import __AUTHOR__
+from x2gobroker.loggers import logger_broker, logger_access, logger_error
+
+logger_broker.info('X2Go Session Broker ({version}), written by {author}'.format(version=__VERSION__, author=__AUTHOR__))
+logger_broker.info('Setting up the broker\'s environment...')
+logger_broker.info('  X2GOBROKER_DEBUG: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DEBUG))
+logger_broker.info('  X2GOBROKER_CONFIG: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_CONFIG))
+logger_broker.info('  X2GOBROKER_AGENT_CMD: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_AGENT_CMD))
+logger_broker.info('  X2GOBROKER_AUTHSERVICE_SOCKET: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_AUTHSERVICE_SOCKET))
+logger_broker.info('  X2GOBROKER_DEFAULT_BACKEND: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DEFAULT_BACKEND))
+logger_broker.info('  X2GOBROKER_SSL_CERTFILE: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE))
+logger_broker.info('  X2GOBROKER_SSL_KEYFILE: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE))
+
+# raise log level to DEBUG if requested...
+if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE:
+    logger_broker.setLevel(logging.DEBUG)
+    logger_access.setLevel(logging.DEBUG)
+    logger_error.setLevel(logging.DEBUG)
+
+# check effective UID the broker runs as and complain appropriately...
+if x2gobroker.defaults.X2GOBROKER_USER != x2gobroker.defaults.X2GOBROKER_DAEMON_USER and os.geteuid() != 0:
+    logger_broker.warn('X2Go Session Broker has been started interactively by user {username}, better run as user {daemon_username}.'.format(username=x2gobroker.defaults.X2GOBROKER_USER, daemon_username=x2gobroker.defaults.X2GOBROKER_DAEMON_USER))
+    logger_broker.info('Automatically switching to DEBUG mode due to interactive launch of this application.')
+elif x2gobroker.defaults.X2GOBROKER_DAEMON_USER != 'root' and os.geteuid() == 0:
+    logger_broker.warn('X2Go Session Broker should not be run as root, better run as user {daemon_username}.'.format(daemon_username=x2gobroker.defaults.X2GOBROKER_DAEMON_USER))
+elif os.geteuid() == 0:
+    logger_broker.warn('X2Go Session Broker should not be run as root, better run as non-privileged user')
+
 # parse-in potential command line options
 cmdline_args = None
 if __name__ == "__main__":
@@ -59,13 +90,20 @@ if __name__ == "__main__":
 
     if cmdline_args.config_file is not None:
         x2gobroker.defaults.X2GOBROKER_CONFIG = cmdline_args.config_file
-    x2gobroker.defaults.X2GOBROKER_DEBUG = cmdline_args.debug
+
+    # override X2GOBROKER_DEBUG=0 in os.environ with the command line switch
+    if cmdline_args.debug:
+        x2gobroker.defaults.X2GOBROKER_DEBUG = cmdline_args.debug
+
+    # some people just give the port but prepend a colon, webpy does not like this, so we strip if off
+    cmdline_args.bind = cmdline_args.bind.lstrip(':')
 
 
 # import classes serving the different web.py URLs
-from x2gobroker.web.plain import *
-#from x2gobroker.web.json import *
-#from x2gobroker.web.html import *
+import x2gobroker.web.plain
+#import x2gobroker.web.json
+#import x2gobroker.web.html
+import x2gobroker.web.extras
 
 # switch on https:// mode
 if x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE and x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE:
@@ -73,9 +111,10 @@ if x2gobroker.defaults.X2GOBROKER_SSL_CERTFILE and x2gobroker.defaults.X2GOBROKE
     CherryPyWSGIServer.ssl_private_key = x2gobroker.defaults.X2GOBROKER_SSL_KEYFILE
 
 # define the web.py URLs
-urls = ( '/plain/(.*)', 'X2GoBrokerWebPlain',
-#         '/json/(.*)', 'X2GoBrokerWebJson',
-#         '/html/(.*)', 'X2GoBrokerWebHtml',
+urls = ( '/plain/(.*)', 'x2gobroker.web.plain.X2GoBrokerWeb',
+#         '/json/(.*)', 'x2gobroker.web.json.X2GoBrokerWeb',
+#         '/html/(.*)', 'x2gobroker.web.html.X2GoBrokerWeb',
+          '/pubkeys/', 'x2gobroker.web.extras.X2GoBrokerPubKeyService',
        )
 
 # run the web.py standalone daemon...
diff --git a/sbin/x2gobroker-authservice b/sbin/x2gobroker-authservice
new file mode 100755
index 0000000..e17271e
--- /dev/null
+++ b/sbin/x2gobroker-authservice
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of the  X2Go Project - http://www.x2go.org
+# Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2011-2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import sys
+import setproctitle
+import argparse
+import logging
+
+try:
+    import x2gobroker.defaults
+except ImportError:
+    sys.path.insert(0, os.path.join(os.getcwd(), '..'))
+    import x2gobroker.defaults
+
+import x2gobroker.authservice
+
+PROG_NAME = os.path.basename(sys.argv[0])
+PROG_OPTIONS = sys.argv[1:]
+setproctitle.setproctitle("%s %s" % (PROG_NAME, " ".join(PROG_OPTIONS)))
+
+from x2gobroker import __VERSION__
+from x2gobroker import __AUTHOR__
+from x2gobroker.loggers import logger_authservice
+
+# raise log level to DEBUG if requested...
+if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE:
+    logger_authservice.setLevel(logging.DEBUG)
+
+logger_authservice.info('X2Go Session Broker ({version}), written by {author}'.format(version=__VERSION__, author=__AUTHOR__))
+logger_authservice.info('Setting up the PAM authentication service\'s environment...')
+logger_authservice.info('  X2GOBROKER_DEBUG: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DEBUG))
+logger_authservice.info('  X2GOBROKER_AUTHSERVICE_SOCKET: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_AUTHSERVICE_SOCKET))
+
+# check effective UID the broker runs as and complain appropriately...
+if os.geteuid() != 0:
+    logger_authservice.warn('X2Go Session Broker\'s PAM authentication service should run with root privileges to guarantee proper access to all PAM modules.')
+
+if __name__ == '__main__':
+
+    common_options = [
+        {'args':['-s','--socket-file'], 'default': x2gobroker.authservice.X2GOBROKER_AUTHSERVICE_SOCKET, 'metavar': 'AUTHSOCKET', 'help': 'socket file for AuthService communication', },
+        {'args':['-o','--owner'], 'default': 'root', 'help': 'owner of the AuthService socket file', },
+        {'args':['-g','--group'], 'default': 'root', 'help': 'group ownership of the AuthService socket file', },
+        {'args':['-p','--permissions'], 'default': '0660', 'help': 'set these file permissions for the AuthService socket file', },
+
+    ]
+    p = argparse.ArgumentParser(description='X2Go Session Broker (PAM Authentication Service)',\
+                                formatter_class=argparse.RawDescriptionHelpFormatter, \
+                                add_help=True, argument_default=None)
+    p_common = p.add_argument_group('common parameters')
+
+    for (p_group, opts) in ( (p_common, common_options), ):
+        for opt in opts:
+            args = opt['args']
+            del opt['args']
+            p_group.add_argument(*args, **opt)
+
+    cmdline_args = p.parse_args()
+
+    socket_file = cmdline_args.socket_file
+    x2gobroker.authservice.AuthService(socket_file, owner=cmdline_args.owner, group_owner=cmdline_args.group, permissions=cmdline_args.permissions)
+    try:
+        x2gobroker.authservice.loop()
+    except KeyboardInterrupt:
+        pass
diff --git a/sbin/x2gobroker-keygen b/sbin/x2gobroker-keygen
new file mode 100755
index 0000000..2d3c037
--- /dev/null
+++ b/sbin/x2gobroker-keygen
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of the  X2Go Project - http://www.x2go.org
+# Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2011-2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import sys
+import setproctitle
+import argparse
+import logging
+import binascii
+import paramiko
+
+from pwd import getpwnam
+from grp import getgrnam
+
+try:
+    import x2gobroker.defaults
+except ImportError:
+    sys.path.insert(0, os.path.join(os.getcwd(), '..'))
+    import x2gobroker.defaults
+
+supported_key_types = ('RSA', 'DSA')
+
+PROG_NAME = os.path.basename(sys.argv[0])
+PROG_OPTIONS = sys.argv[1:]
+setproctitle.setproctitle("%s %s" % (PROG_NAME, " ".join(PROG_OPTIONS)))
+
+from x2gobroker import __VERSION__
+from x2gobroker import __AUTHOR__
+from x2gobroker.loggers import logger_broker, logger_error
+
+if os.geteuid() == 0:
+    # propagate msgs for  the broker logger to the root logger (i.e. to stderr)
+    logger_broker.propagate = 1
+    logger_error.propagate = 1
+
+# raise log level to DEBUG if requested...
+if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE:
+    logger_broker.setLevel(logging.DEBUG)
+
+logger_broker.info('X2Go Session Broker ({version}), written by {author}'.format(version=__VERSION__, author=__AUTHOR__))
+logger_broker.info('Setting up the key generator\'s environment...')
+logger_broker.info('  X2GOBROKER_DEBUG: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DEBUG))
+logger_broker.info('  X2GOBROKER_DAEMON_USER: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DAEMON_USER))
+logger_broker.info('  X2GOBROKER_DAEMON_GROUP: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DAEMON_GROUP))
+
+# check effective UID the broker runs as and complain appropriately...
+if os.geteuid() != 0:
+    logger_error.error('X2Go Session Broker\'s key generator has to run with root privileges. Exiting...')
+    sys.exit(-1)
+
+if __name__ == '__main__':
+
+    common_options = [
+        {'args':['-t','---key-type'], 'default': 'RSA', 'help': 'Choose a key type for the X2Go Session Broker pub/priv SSH key pair (available: RSA, DSA).', },
+        {'args':['-f','--force'], 'default': False, 'action': 'store_true', 'help': 'Enforce the creation of a public/private key pair. WARNING: This will overwrite earlier created keys.', },
+    ]
+    p = argparse.ArgumentParser(description='X2Go Session Broker (Key Generator)',\
+                                formatter_class=argparse.RawDescriptionHelpFormatter, \
+                                add_help=True, argument_default=None)
+    p_common = p.add_argument_group('common parameters')
+
+    for (p_group, opts) in ( (p_common, common_options), ):
+        for opt in opts:
+            args = opt['args']
+            del opt['args']
+            p_group.add_argument(*args, **opt)
+
+    cmdline_args = p.parse_args()
+
+    if cmdline_args.key_type.upper() not in supported_key_types:
+        logger_error.error(u'Unknown key type »{key_type}«. Possible key types are RSA and DSA. Exiting...'.format(key_type=cmdline_args.key_type.upper()))
+        sys.exit(-2)
+
+    broker_uid = x2gobroker.defaults.X2GOBROKER_DAEMON_USER
+    broker_uidnumber = getpwnam(broker_uid).pw_uid
+    broker_gid = x2gobroker.defaults.X2GOBROKER_DAEMON_GROUP
+    broker_gidnumber = getgrnam(broker_gid).gr_gid
+    broker_home = x2gobroker.defaults.X2GOBROKER_HOME
+
+    if not os.path.exists(broker_home):
+        logger_error.error('The home directory {home} of user {user} does not exists. Cannot continue. Exiting...'.format(home=broker_home, user=broker_uid))
+        sys.exit(-2)
+
+    logger_broker.info('Creating pub/priv key pair for X2Go Session Broker...')
+    if not os.path.exists('{home}/.ssh'.format(home=broker_home)):
+        os.mkdir('{home}/.ssh'.format(home=broker_home))
+        os.chown('{home}/.ssh'.format(home=broker_home), broker_uidnumber, broker_gidnumber)
+        os.chmod('{home}/.ssh'.format(home=broker_home), 0750)
+        logger_broker.info('  Created {home}/.ssh'.format(home=broker_home))
+
+    # generate key pair
+    if cmdline_args.key_type.upper() == 'RSA':
+        key = paramiko.RSAKey.generate(2048)
+        id_file = 'id_rsa'
+    elif cmdline_args.key_type.upper() == 'DSA':
+        key = paramiko.DSSKey.generate(1024)
+        id_file = 'id_dsa'
+
+    logger_broker.info('  The {key_type} key has been generated, fingerprint: {fingerprint}'.format(key_type=cmdline_args.key_type.upper(), fingerprint=binascii.hexlify(key.get_fingerprint())))
+
+    if os.path.exists('{home}/.ssh/{id_file}'.format(home=broker_home, id_file=id_file)) and not cmdline_args.force:
+        logger_broker.error('  Private key {home}/.ssh/{id_file} exists. Use --force to overwrite the file and regenerate a new key pair. Exiting...'.format(home=broker_home, id_file=id_file))
+        sys.exit(-3)
+    elif os.path.exists('{home}/.ssh/{id_file}'.format(home=broker_home, id_file=id_file)):
+        logger_broker.warn('  WARNING: you requested to overwrite existing key files!!!')
+
+    key.write_private_key_file('{home}/.ssh/{id_file}'.format(home=broker_home, id_file=id_file))
+    os.chown('{home}/.ssh/{id_file}'.format(home=broker_home, id_file=id_file), broker_uidnumber, broker_gidnumber)
+    os.chmod('{home}/.ssh/{id_file}'.format(home=broker_home, id_file=id_file), 0600)
+    logger_broker.info('  Private key written to file {key_file}'.format(key_file='{home}/.ssh/{id_file}'.format(home=broker_home, id_file=id_file)))
+
+    pubkey_file = open('{home}/.ssh/{id_file}.pub'.format(home=broker_home, id_file=id_file),'w')
+    if id_file == 'id_rsa':
+        pubkey_file.write("ssh-rsa " +key.get_base64())
+    elif id_file == 'id_dsa':
+        pubkey_file.write("ssh-dss " +key.get_base64())
+    pubkey_file.close()
+    os.chown('{home}/.ssh/{id_file}.pub'.format(home=broker_home, id_file=id_file), broker_uidnumber, broker_gidnumber)
+    os.chmod('{home}/.ssh/{id_file}.pub'.format(home=broker_home, id_file=id_file), 0644)
+    logger_broker.info('  Public key written to file {key_file}'.format(key_file='{home}/.ssh/{id_file}.pub'.format(home=broker_home, id_file=id_file)))
+
+    logger_broker.info('Key file generation has been successful!')
+
diff --git a/sbin/x2gobroker-pubkeyauthorizer b/sbin/x2gobroker-pubkeyauthorizer
new file mode 100755
index 0000000..66ad1ce
--- /dev/null
+++ b/sbin/x2gobroker-pubkeyauthorizer
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of the  X2Go Project - http://www.x2go.org
+# Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2011-2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import sys
+import setproctitle
+import argparse
+import logging
+import binascii
+import paramiko
+import urllib
+
+from pwd import getpwnam
+from grp import getgrnam
+
+try:
+    import x2gobroker.defaults
+except ImportError:
+    sys.path.insert(0, os.path.join(os.getcwd(), '..'))
+    import x2gobroker.defaults
+
+PROG_NAME = os.path.basename(sys.argv[0])
+PROG_OPTIONS = sys.argv[1:]
+setproctitle.setproctitle("%s %s" % (PROG_NAME, " ".join(PROG_OPTIONS)))
+
+from x2gobroker import __VERSION__
+from x2gobroker import __AUTHOR__
+from x2gobroker.loggers import logger_broker, logger_error
+
+if os.geteuid() == 0:
+    # propagate msgs for  the broker logger to the root logger (i.e. to stderr)
+    logger_broker.propagate = 1
+    logger_error.propagate = 1
+
+# raise log level to DEBUG if requested...
+if x2gobroker.defaults.X2GOBROKER_DEBUG and not x2gobroker.defaults.X2GOBROKER_TESTSUITE:
+    logger_broker.setLevel(logging.DEBUG)
+
+logger_broker.info('X2Go Session Broker ({version}), written by {author}'.format(version=__VERSION__, author=__AUTHOR__))
+logger_broker.info('Setting up the »PubKey Authorizer«\'s environment...')
+logger_broker.info('  X2GOBROKER_DEBUG: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DEBUG))
+logger_broker.info('  X2GOBROKER_DAEMON_USER: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DAEMON_USER))
+logger_broker.info('  X2GOBROKER_DAEMON_GROUP: {value}'.format(value=x2gobroker.defaults.X2GOBROKER_DAEMON_GROUP))
+
+# check effective UID the broker runs as and complain appropriately...
+if os.geteuid() != 0:
+    logger_error.error('X2Go Session Broker\'s »PubKey Authorizer« has to run with root privileges. Exiting...')
+    sys.exit(-1)
+
+if __name__ == '__main__':
+
+    common_options = [
+        {'args':['-t','--broker-url'], 'default': None, 'help': 'The URL of the X2Go Session Broker that we want to retrieve public keys from. The common pattern for this URL is http(s)://<broker_hostname>:<port>/pubkeys/.', },
+    ]
+    p = argparse.ArgumentParser(description='X2Go Session Broker (PubKey Installer)',\
+                                formatter_class=argparse.RawDescriptionHelpFormatter, \
+                                add_help=True, argument_default=None)
+    p_common = p.add_argument_group('common parameters')
+
+    for (p_group, opts) in ( (p_common, common_options), ):
+        for opt in opts:
+            args = opt['args']
+            del opt['args']
+            p_group.add_argument(*args, **opt)
+
+    print
+    cmdline_args = p.parse_args()
+
+    if cmdline_args.broker_url is None:
+        logger_error.error('Cannot proceed without having an URL specified. Use --broker-url as cmdline parameter. Exiting...')
+        sys.exit(-2)
+
+    broker_uid = x2gobroker.defaults.X2GOBROKER_DAEMON_USER
+    broker_uidnumber = getpwnam(broker_uid).pw_uid
+    broker_gid = x2gobroker.defaults.X2GOBROKER_DAEMON_GROUP
+    broker_gidnumber = getgrnam(broker_gid).gr_gid
+    broker_home = x2gobroker.defaults.X2GOBROKER_HOME
+
+    if not os.path.exists(broker_home):
+        logger_error.error('The home directory {home} of user {user} does not exists. Cannot continue. Exiting...'.format(home=broker_home, user=broker_uid))
+        sys.exit(-2)
+
+    logger_broker.info('Authorizing access to this X2Go server for X2Go Session Broker at URL {url}'.format(url=cmdline_args.broker_url))
+
+
+    if not os.path.exists('{home}/.ssh'.format(home=broker_home)):
+        os.mkdir('{home}/.ssh'.format(home=broker_home))
+        os.chown('{home}/.ssh'.format(home=broker_home), broker_uidnumber, broker_gidnumber)
+        os.chmod('{home}/.ssh'.format(home=broker_home), 0750)
+        logger_broker.info('  Created {home}/.ssh'.format(home=broker_home))
+
+    # FIXME: this probably needs some sanity checks(?)
+    tmpfile_name, httpmsg = urllib.urlretrieve(cmdline_args.broker_url)
+
+    tmpfile = open(tmpfile_name, 'rb')
+    new_pubkeys = [ k for k in tmpfile.read().split('\n') if k ]
+    logger_broker.info('  Found {i} public keys at URL {url}'.format(i=len(new_pubkeys), url=cmdline_args.broker_url))
+    tmpfile.close()
+
+    try:
+        read_authorized_keys = open('{home}/.ssh/authorized_keys'.format(home=broker_home), 'rb')
+        already_authorized_keys = read_authorized_keys.read().split('\n')
+        read_authorized_keys.close()
+    except IOError:
+        already_authorized_keys = []
+
+    append_authorized_keys = open('{home}/.ssh/authorized_keys'.format(home=broker_home), 'ab')
+
+    i = 0
+    for new_pubkey in new_pubkeys:
+        i += 1
+        if new_pubkey not in already_authorized_keys:
+            append_authorized_keys.write('{k}\n'.format(k=new_pubkey))
+            logger_broker.info('  Adding new public key (counter={i}) to {authorized_keys}.'.format(i=i, authorized_keys='{home}/.ssh/authorized_keys'.format(home=broker_home)))
+        else:
+            logger_broker.warning('  Skipping new public key (counter={i}), already in {authorized_keys}.'.format(i=i, authorized_keys='{home}/.ssh/authorized_keys'.format(home=broker_home)))
+
+    append_authorized_keys.close()
+
+    # set proper file permissions
+    os.chown('{home}/.ssh/authorized_keys'.format(home=broker_home), broker_uidnumber, broker_gidnumber)
+    os.chmod('{home}/.ssh/authorized_keys'.format(home=broker_home), 0644)
+
+    logger_broker.info('Completed successfully: X2Go Session Broker\'s PubKey Authorizer.'.format(url=cmdline_args.broker_url))
diff --git a/setup.py b/setup.py
index 8745c3f..f6d9aba 100755
--- a/setup.py
+++ b/setup.py
@@ -28,15 +28,18 @@ import os
 for line in file(os.path.join('x2gobroker', '__init__.py')).readlines():
     if (line.startswith('__VERSION__')):
         exec(line.strip())
+for line in file(os.path.join('x2gobroker', '__init__.py')).readlines():
+    if (line.startswith('__AUTHOR__')):
+        exec(line.strip())
 MODULE_VERSION = __VERSION__
-
+MODULE_AUTHOR = __AUTHOR__
 
 setup(
     name = "x2gobroker",
     version = MODULE_VERSION,
     description = "X2Go Session Broker",
     license = 'AGPLv3+',
-    author = 'Mike Gabriel',
+    author = MODULE_AUTHOR,
     url = 'http://www.x2go.org',
     packages = find_packages('.'),
     package_dir = {'': '.'},
diff --git a/test.py b/test.py
index 353cd16..b04e9ee 100755
--- a/test.py
+++ b/test.py
@@ -25,5 +25,6 @@ import os
 
 if __name__ == "__main__":
     os.environ.update({'X2GOBROKER_DEBUG': "1"})
+    os.environ.update({'X2GOBROKER_TESTSUITE': "1"})
     os.chdir(os.path.join('x2gobroker', 'tests',))
     os.system('python ./runalltests.py')
diff --git a/x2gobroker/__init__.py b/x2gobroker/__init__.py
index ad8c1e4..d9172cc 100644
--- a/x2gobroker/__init__.py
+++ b/x2gobroker/__init__.py
@@ -18,6 +18,5 @@
 # Free Software Foundation, Inc.,
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
-__VERSION__ = '0.0.0.1'
-
-
+__VERSION__ = '0.0.0.2'
+__AUTHOR__ = 'Mike Gabriel (X2Go Project) <mike.gabriel at das-netzwerkteam.de>'
diff --git a/x2gobroker/agent.py b/x2gobroker/agent.py
new file mode 100644
index 0000000..600a361
--- /dev/null
+++ b/x2gobroker/agent.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+# Copyright (C) 2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import subprocess
+import paramiko
+
+# X2Go Broker modules
+import x2gobroker.defaults
+from x2gobroker.loggers import logger_error
+
+
+def call_local_broker_agent(username, mode):
+    """\
+    Launch X2Go Broker Agent locally and process its output.
+
+    @param username: run the broker agent for this user
+    @type username: C{unicode}
+    @param mode: execution mode of the broker (listsessions, getservers, etc.)
+    @type mode: C{unicode}
+
+    """
+    cmd_line = [
+        '{x2gobroker_agent_binary}'.format(x2gobroker_agent_binary=x2gobroker.defaults.X2GOBROKER_AGENT_CMD),
+        '{username}'.format(username=username),
+        '{mode}'.format(mode=mode),
+    ]
+
+    agent_process = subprocess.Popen(cmd_line,
+                                     stdin=None,
+                                     stdout=subprocess.PIPE,
+                                     stderr=subprocess.STDOUT,
+                                     shell=False,
+    )
+
+    result = agent_process.stdout.read().split('\n')
+    if result[0].startswith('OK'):
+        return [ r for r in result[1:] if r ]
+
+def call_remote_broker_agent(username, mode, remote_agent):
+    """\
+    Launch remote X2Go Broker Agent via SSH and process its output.
+
+    @param username: run the broker agent for this user
+    @type username: C{unicode}
+    @param mode: execution mode of the broker (listsessions, getservers, etc.)
+    @type mode: C{unicode}
+    @param remote_agent: information about the remote agent that is to be called.
+    @type remote_agent: C{dict}
+
+    """
+    cmd_line = [
+        '{x2gobroker_agent_binary}'.format(x2gobroker_agent_binary=x2gobroker.defaults.X2GOBROKER_AGENT_CMD),
+        '{username}'.format(username=username),
+        '{mode}'.format(mode=mode),
+    ]
+
+    remote_username = x2gobroker.defaults.X2GOBROKER_USER
+    remote_hostname = remote_agent[u'hostname']
+    remote_port = int(remote_agent[u'port'])
+
+    # now, connect and use paramiko Client to negotiate SSH2 across the connection
+    try:
+        client = paramiko.SSHClient()
+        client.load_system_host_keys()
+        client.set_missing_host_key_policy(paramiko.WarningPolicy)
+        client.connect(remote_hostname, remote_port, remote_username, look_for_keys=True, allow_agent=True)
+
+        result = []
+        ssh_transport = client.get_transport()
+        if ssh_transport.is_authenticated():
+            cmd = ' '.join(cmd_line)
+            cmd = 'sh -c \"{cmd}\"'.format(cmd=cmd)
+            (stdin, stdout, stderr) = client.exec_command(cmd)
+            result = stdout.read().split('\n')
+        client.close()
+        if result and result[0].startswith('OK'):
+            return [ r for r in result[1:] if r ]
+    except paramiko.SSHException:
+        logger_error.error('could not connect to remote X2Go Broker Agent (user: {user}, hostname: {hostname}, port: {port}'.format(user=remote_username, hostname=remote_hostname, port=remote_port))
+
+def list_sessions(username, query_mode='LOCAL', remote_agent=None):
+    """\
+    Query X2Go Broker Agent for a session list for a given username.
+
+    @param username: run the query on behalf of this username
+    @type username: C{unicode}
+    @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+    @type query_mode: C{unicode}
+    @param remote_agent: information about the remote agent that is to be called.
+    @type remote_agent: C{dict}
+
+    """
+    if query_mode.upper() == u'LOCAL':
+        return call_local_broker_agent(username, mode='listsessions')
+    else:
+        return call_remote_broker_agent(username, mode='listsessions', remote_agent=remote_agent)
+
+def find_busy_servers(username, query_mode='LOCAL', remote_agent=None):
+    """\
+    Query X2Go Broker Agent for a list of  servers with running
+    and/or suspended sessions and a percentage that tells about
+    the busy-state of the server.
+
+    The result is independent from the username given.
+
+    @param username: run the query on behalf of this username
+    @type username: C{unicode}
+    @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+    @type query_mode: C{unicode}
+    @param remote_agent: information about the remote agent that is to be called.
+    @type remote_agent: C{dict}
+
+    """
+    if query_mode.upper() == u'LOCAL':
+        server_list = call_local_broker_agent(username, mode='findbusyservers')
+    else:
+        server_list = call_remote_broker_agent(username, mode='findbusyservers', remote_agent=remote_agent)
+
+    server_usage = {}
+
+    if server_list:
+        for server_item in server_list:
+            if ':' in server_item:
+                usage, server = server_item.split(':')
+                server_usage.update({ server: int(usage) })
+
+    return server_usage
+
+def get_servers(username, query_mode='LOCAL', remote_agent=None):
+    """\
+    Query X2Go Broker Agent for the list of currently used servers.
+
+    The result is independent from the username given.
+
+    @param username: run the query on behalf of this username
+    @type username: C{unicode}
+    @param query_mode: query mode used when calling X2Go Broker Agent (C{LOCAL} or C{SSH})
+    @type query_mode: C{unicode}
+    @param remote_agent: information about the remote agent that is to be called.
+    @type remote_agent: C{dict}
+
+    """
+    if query_mode.upper() == u'LOCAL':
+        return call_local_broker_agent(username, mode='getservers')
+    else:
+        return call_local_broker_agent(username, mode='getservers', remote_agent=remote_agent)
+
diff --git a/x2gobroker/authmechs/pam_authmech.py b/x2gobroker/authmechs/pam_authmech.py
index fca5ec0..b46d1a2 100644
--- a/x2gobroker/authmechs/pam_authmech.py
+++ b/x2gobroker/authmechs/pam_authmech.py
@@ -18,7 +18,7 @@
 # Free Software Foundation, Inc.,
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
-import pam
+import x2gobroker.authservice
 
 class X2GoBrokerAuthMech(object):
 
@@ -26,7 +26,7 @@ class X2GoBrokerAuthMech(object):
 
         # do a simple PAM authentication against the PAM service ,,x2gobroker''
         if username and password:
-            if pam.authenticate(username, password, service="x2gobroker"):
+            if x2gobroker.authservice.authenticate(username, password, service="x2gobroker"):
                 return True
 
         return False
diff --git a/x2gobroker/authservice.py b/x2gobroker/authservice.py
new file mode 100644
index 0000000..fa7ce84
--- /dev/null
+++ b/x2gobroker/authservice.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+
+# This file is part of the  X2Go Project - http://www.x2go.org
+# Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2011-2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import asyncore
+import pam
+import socket
+
+from pwd import getpwnam
+from grp import getgrnam
+
+from loggers import logger_authservice
+
+logger_authservice.info('X2Go Session Broker PAM Authentication Service: Setting up the broker\'s environment...')
+if os.environ.has_key('X2GOBROKER_AUTHSERVICE_SOCKET'):
+    X2GOBROKER_AUTHSERVICE_SOCKET=os.environ['X2GOBROKER_AUTHSERVICE_SOCKET']
+else:
+    X2GOBROKER_AUTHSERVICE_SOCKET="/var/run/x2gobroker-authservice.socket"
+logger_authservice.info('  X2GOBROKER_AUTHSERVICE_SOCKET: {value}'.format(value=X2GOBROKER_AUTHSERVICE_SOCKET))
+
+
+class AuthClient(asyncore.dispatcher_with_send):
+
+    def __init__(self, sock):
+        asyncore.dispatcher_with_send.__init__(self, sock)
+        self._buf = ''
+
+    def handle_read(self):
+        data = self._buf + self.recv(1024)
+        if not data:
+            self.close()
+            return
+        reqs, data = data.rsplit('\n', 1)
+        self._buf = data
+        for req in reqs.split('\n'):
+            try:
+                user, passwd, service = req.split()
+            except:
+                self.send('bad\n')
+            else:
+                if pam.authenticate(user, passwd, service):
+                    self.send('ok\n')
+                else:
+                    self.send('fail\n')
+
+    def handle_close(self):
+        self.close()
+
+
+class AuthService(asyncore.dispatcher_with_send):
+
+    def __init__(self, socketfile, owner='root', group_owner='root', permissions='0660'):
+        asyncore.dispatcher_with_send.__init__(self)
+        self.create_socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.set_reuse_addr()
+        self.bind(socketfile)
+        os.chown(socketfile, getpwnam(owner).pw_uid, getgrnam(group_owner).gr_gid)
+        os.chmod(socketfile, int(permissions, 8))
+        self.listen(1)
+
+    def handle_accept(self):
+        conn, _ = self.accept()
+        AuthClient(conn)
+
+
+def loop():
+    asyncore.loop()
+
+
+def authenticate(username, password, service="x2gobroker"):
+    s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+    s.connect(X2GOBROKER_AUTHSERVICE_SOCKET)
+    s.send('{username} {password} {service}\n'.format(username=username, password=password, service=service))
+    result = s.recv(1024)
+    s.close()
+    if result.startswith('ok'):
+        logger_authservice.info('authentication against PAM service »{service}« succeeded for user »{username}«'.format(username=username, service=service))
+        return True
+    logger_authservice.info('authentication against service »{service}« failed for user »{username}«'.format(username=username, service=service))
+    return False
diff --git a/x2gobroker/brokers/base_broker.py b/x2gobroker/brokers/base_broker.py
index 7da6336..9e811c0 100644
--- a/x2gobroker/brokers/base_broker.py
+++ b/x2gobroker/brokers/base_broker.py
@@ -29,10 +29,14 @@ import types
 import copy
 import uuid
 import netaddr
+import random
 
 # X2Go Broker modules
 import x2gobroker.config
 import x2gobroker.defaults
+import x2gobroker.agent
+
+from x2gobroker.loggers import logger_broker, logger_error
 
 class X2GoBroker(object):
     """\
@@ -57,9 +61,10 @@ class X2GoBroker(object):
         X2Go Client with an intermediate session broker.
 
         """
-        if config_file is None: config_file = x2gobroker.defaults.X2GOBROKER_CONFIG
+        self.config_file = config_file
+        if self.config_file is None: self.config_file = x2gobroker.defaults.X2GOBROKER_CONFIG
         if config_defaults is None: config_defaults = x2gobroker.defaults.X2GOBROKER_CONFIG_DEFAULTS
-        self.config = x2gobroker.config.X2GoBrokerConfigFile(config_files=config_file, defaults=config_defaults)
+        self.config = x2gobroker.config.X2GoBrokerConfigFile(config_files=self.config_file, defaults=config_defaults)
 
         self._dynamic_authid_map = {}
         self._client_address = None
@@ -284,7 +289,6 @@ class X2GoBroker(object):
             _acls_clients_allow = copy.deepcopy(_acls[u'acl-clients-allow'])
             _acls_clients_deny = copy.deepcopy(_acls[u'acl-clients-deny'])
 
-            _addr = self.get_client_address()
             _allow_client = False
             _deny_client = False
 
@@ -412,12 +416,35 @@ class X2GoBroker(object):
         _auth_mech = ""
         if self.config.has_value('global', 'default-auth-mech'):
             _default_auth_mech = self.config.get_value('global', 'default-auth-mech').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found default-auth-mech in global config section: {value}'.format(value=_default_auth_mech))
 
         if self.config.has_value(self.backend_name, 'auth-mech'):
             _auth_mech = self.config.get_value(self.backend_name, 'auth-mech').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_authentication_mechanism(): found auth-mech in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_auth_mech))
 
         return unicode(_auth_mech) or unicode(_default_auth_mech)
 
+    def get_agent_query_mode(self):
+        """\
+        Get the agent query mode (LOCAL or SSH, normally) that is configured for this
+        X2Go Session Broker instance.
+
+        @return: agent query mode
+        @rtype: C{unicode}
+
+        """
+        _default_agent_query_mode = "LOCAL"
+        _agent_query_mode = ""
+        if self.config.has_value('global', 'default-agent-query-mode'):
+            _default_agent_query_mode = self.config.get_value('global', 'default-agent-query-mode').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found default-agent-query-mode in global config section: {value}'.format(value=_default_agent_query_mode))
+
+        if self.config.has_value(self.backend_name, 'agent-query-mode'):
+            _agent_query_mode = self.config.get_value(self.backend_name, 'agent-query-mode').lower()
+            logger_broker.debug('base_broker.X2GoBroker.get_agent_query_mode(): found agent-query-mode in backend config section »{backend}«: {value}. This one has precendence over the default value.'.format(backend=self.backend_name, value=_agent_query_mode))
+
+        return unicode(_agent_query_mode) or unicode(_default_agent_query_mode)
+
     def get_userdb_service(self):
         """\
         Get the name of the backend being used for retrieving user information from the
@@ -611,6 +638,7 @@ class X2GoBroker(object):
         ### This is handled through the config file, normally /etc/x2go/x2gobroker.conf
 
         if not self.config.get_value('global', 'check-credentials'):
+            logger_broker.debug('base_broker.X2GoBroker.check_access(): access is granted without checking credentials, prevent this in {configfile}'.format(configfile=self.config_file))
             return True
 
         ### IMPLEMENT YOUR AUTHENTICATION LOGIC IN THE self._do_authenticate(**kwargs) METHOD
@@ -618,6 +646,7 @@ class X2GoBroker(object):
 
         access = False
         access = self._do_authenticate(username=username, password=password)
+        logger_broker.debug('base_broker.X2GoBroker.check_access(): result of authentication check is: {access}'.format(access=access))
 
         ### HANDLING OF DYNAMIC AUTHENTICATION ID HASHES
 
@@ -685,7 +714,7 @@ class X2GoBroker(object):
 
         return list_of_profiles
 
-    def select_session(self, profile_id):
+    def select_session(self, profile_id, username):
         """\
         Start/resume a session by selecting a profile name offered by the X2Go client.
 
@@ -693,16 +722,61 @@ class X2GoBroker(object):
         broker.
 
         @param profile_id: the selected profile ID. This matches one of the dictionary keys offered by the C{list_profiles} method
-        @type profile_id: C{dict}
+        @type profile_id: C{unicode}
+        @param username: specify username that this operation runs for
+        @type username: C{unicode}
 
         """
         profile = self.get_profile(profile_id)
 
+        # if we have more than one server, pick one server randomly for X2Go Broker Agent queries
+        server_list = profile[u'host']
+        agent_query_mode = self.get_agent_query_mode()
+
+        if len(server_list) >= 2:
+
+            remote_agent = None
+            if agent_query_mode.upper() == u'SSH':
+                random.shuffle(server_list)
+                remote_agent_server = server_list[0]
+                remote_agent_port = profile[u'sshport']
+                remote_agent = {u'hostname': remote_agent_server, u'port': remote_agent_port, }
+
+            busy_servers = x2gobroker.agent.find_busy_servers(username=username, query_mode=agent_query_mode, remote_agent = remote_agent)
+
+            for server in server_list:
+                if server not in busy_servers.keys():
+                    busy_servers[server] = 0
+
+            busy_server_list = [ (load, server) for server, load in busy_servers.items() ]
+            busy_server_list.sort()
+            print busy_server_list
+
+            best_server = busy_server_list[0][1]
+
+        else:
+            best_server = server_list[0]
+
         selected_session = {
-            'server': profile[u'host'],
+            'server': best_server,
             'port': profile[u'sshport'],
         }
 
+        # do some load balancing if more than one server is configured
+        if len(server_list) >= 2 and username:
+
+            session_list = x2gobroker.agent.list_sessions(username=username, query_mode=agent_query_mode, remote_agent=remote_agent)
+            if session_list:
+
+                # if resuming, always select the first session in the list, there should only be one suspended session
+                server_name = session_list[0].split('|')[3]
+                session_info = session_list[0]
+
+                selected_session.update({
+                    'server': server_name,
+                    'session_info': session_info,
+                })
+
         return selected_session
 
     def change_password(self, new='', old=''):
diff --git a/x2gobroker/brokers/ldap_broker.py b/x2gobroker/brokers/ldap_broker.py
index 61857d8..8f631ab 100644
--- a/x2gobroker/brokers/ldap_broker.py
+++ b/x2gobroker/brokers/ldap_broker.py
@@ -25,7 +25,7 @@ L{ldap.X2GoBroker} class - a production X2GoBroker implementations that uses LDA
 __NAME__ = 'x2gobroker-pylib'
 
 # modules
-import x2gobroker.base_broker as base
+import x2gobroker.base
 
 class X2GoBroker(x2gobroker.base.X2GoBroker):
     """\
diff --git a/x2gobroker/brokers/zeroconf_broker.py b/x2gobroker/brokers/zeroconf_broker.py
index ad21567..5970659 100644
--- a/x2gobroker/brokers/zeroconf_broker.py
+++ b/x2gobroker/brokers/zeroconf_broker.py
@@ -76,7 +76,7 @@ class X2GoBroker(base.X2GoBroker):
         }
         return list_of_profiles
 
-    def select_session(self, profile_id):
+    def select_session(self, profile_id, username=None):
 
         selectprofile_output = {
             'server': 'localhost',
diff --git a/x2gobroker/config.py b/x2gobroker/config.py
index 962b33c..974ad73 100644
--- a/x2gobroker/config.py
+++ b/x2gobroker/config.py
@@ -99,7 +99,7 @@ class X2GoBrokerConfigFile(object):
             self.write()
 
     def __repr__(self):
-        result = 'X2goConfigFile('
+        result = 'X2GoConfigFile('
         for p in dir(self):
             if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue
             result += p + '=' + str(self.__dict__[p]) + ','
diff --git a/x2gobroker/defaults.py b/x2gobroker/defaults.py
index 63bc254..6c377c4 100644
--- a/x2gobroker/defaults.py
+++ b/x2gobroker/defaults.py
@@ -22,45 +22,88 @@
 # modules
 import os
 import uuid
+import getpass
+import socket
 
-# the home directory of the user that the daemon/cgi runs as
-X2GOBROKER_HOME = os.path.normpath(os.path.expanduser('~'))
+import logging
+from loggers import logger_broker, logger_access, logger_error, X2GOBROKER_DAEMON_USER
 
-if os.environ.has_key('X2GOBROKER_CONFIG'):
-    X2GOBROKER_CONFIG=os.environ['X2GOBROKER_CONFIG']
-else:
-    X2GOBROKER_CONFIG="/etc/x2go/x2gobroker.conf"
+X2GOBROKER_USER =  getpass.getuser()
 
-if os.environ.has_key('X2GOBROKER_SESSIONPROFILES'):
-    X2GOBROKER_SESSIONPROFILES=os.environ['X2GOBROKER_SESSIONPROFILES']
+if os.environ.has_key('X2GOBROKER_DAEMON_GROUP'):
+    X2GOBROKER_DAEMON_GROUP=os.environ['X2GOBROKER_DAEMON_GROUP']
 else:
-    X2GOBROKER_SESSIONPROFILES="/etc/x2go/broker/x2gobroker-sessionprofiles.conf"
+    X2GOBROKER_DAEMON_GROUP="x2gobroker"
+
+###
+### dynamic default values, influencable through os.environ...
+###
 
 if os.environ.has_key('X2GOBROKER_DEBUG'):
-    X2GOBROKER_DEBUG=( os.environ['X2GOBROKER_DEBUG'].lower() in ('1', 'on', 'true', 'yes', ) )
+    X2GOBROKER_DEBUG = ( os.environ['X2GOBROKER_DEBUG'].lower() in ('1', 'on', 'true', 'yes', ) )
+else:
+    X2GOBROKER_DEBUG = False
+if os.environ.has_key('X2GOBROKER_TESTSUITE'):
+    X2GOBROKER_TESTSUITE = ( os.environ['X2GOBROKER_TESTSUITE'].lower() in ('1', 'on', 'true', 'yes', ) )
+else:
+    X2GOBROKER_TESTSUITE = False
+
+# enforce debugging for interactive usage
+if X2GOBROKER_USER != X2GOBROKER_DAEMON_USER:
+    X2GOBROKER_DEBUG = True
+
+# drop og level to CRITICAL if we are running the unittests...
+if X2GOBROKER_TESTSUITE:
+    logger_broker.setLevel(logging.CRITICAL)
+    logger_access.setLevel(logging.CRITICAL)
+    logger_error.setLevel(logging.CRITICAL)
+
+if os.environ.has_key('X2GOBROKER_CONFIG'):
+    X2GOBROKER_CONFIG = os.environ['X2GOBROKER_CONFIG']
 else:
-    X2GOBROKER_DEBUG=False
+    X2GOBROKER_CONFIG = "/etc/x2go/x2gobroker.conf"
+
+X2GOBROKER_SESSIONPROFILES = "/etc/x2go/broker/x2gobroker-sessionprofiles.conf"
 
 if os.environ.has_key('X2GOBROKER_AGENT_CMD'):
-    X2GOBROKER_AGENT_CMD=os.environ['X2GOBROKER_AGENT_CMD']
+    X2GOBROKER_AGENT_CMD = os.environ['X2GOBROKER_AGENT_CMD']
+else:
+    X2GOBROKER_AGENT_CMD = "/usr/lib/x2go/x2gobroker-agent"
+
+if os.environ.has_key('X2GOBROKER_AUTHSERVICE_SOCKET'):
+    X2GOBROKER_AUTHSERVICE_SOCKET=os.environ['X2GOBROKER_AUTHSERVICE_SOCKET']
 else:
-    X2GOBROKER_AGENT_CMD="/usr/lib/x2go/x2gobroker-agent"
+    X2GOBROKER_AUTHSERVICE_SOCKET="/var/run/x2gobroker-authservice.socket"
 
 if os.environ.has_key('X2GOBROKER_DEFAULT_BACKEND'):
-    X2GOBROKER_DEFAULT_BACKEND=os.environ['X2GOBROKER_DEFAULT_BACKEND']
+    X2GOBROKER_DEFAULT_BACKEND = os.environ['X2GOBROKER_DEFAULT_BACKEND']
 else:
-    X2GOBROKER_DEFAULT_BACKEND="zeroconf"
+    X2GOBROKER_DEFAULT_BACKEND = "zeroconf"
 
 if os.environ.has_key('X2GOBROKER_SSL_CERTFILE'):
-    X2GOBROKER_SSL_CERTFILE=os.environ['X2GOBROKER_SSL_CERTFILE']
+    X2GOBROKER_SSL_CERTFILE = os.environ['X2GOBROKER_SSL_CERTFILE']
 else:
-    X2GOBROKER_SSL_CERTFILE=""
+    X2GOBROKER_SSL_CERTFILE = ""
 
 if os.environ.has_key('X2GOBROKER_SSL_KEYFILE'):
-    X2GOBROKER_SSL_KEYFILE=os.environ['X2GOBROKER_SSL_KEYFILE']
+    X2GOBROKER_SSL_KEYFILE = os.environ['X2GOBROKER_SSL_KEYFILE']
 else:
-    X2GOBROKER_SSL_KEYFILE=""
+    X2GOBROKER_SSL_KEYFILE = ""
 
+###
+### static / hard-coded defaults
+###
+
+if socket.gethostname().find('.') >= 0:
+    X2GOBROKER_HOSTNAME = socket.gethostname()
+else:
+    X2GOBROKER_HOSTNAME = socket.gethostbyaddr(socket.gethostname())[0]
+
+
+X2GOBROKER_SESSIONPROFILES = "/etc/x2go/broker/x2gobroker-sessionprofiles.conf"
+
+# the home directory of the user that the daemon/cgi runs as
+X2GOBROKER_HOME = os.path.normpath(os.path.expanduser('~{broker_uid}'.format(broker_uid=X2GOBROKER_DAEMON_USER)))
 
 # defaults for X2Go Sessino Broker configuration file
 X2GOBROKER_CONFIG_DEFAULTS = {
@@ -77,6 +120,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
         u'default-user-db': u'libnss',
         u'default-group-db': u'libnss',
         u'ignore-primary-group-memberships': True,
+        u'default-agent-query-mode': u'LOCAL',
     },
     'zeroconf': {
         u'enable': True,
@@ -103,6 +147,7 @@ X2GOBROKER_CONFIG_DEFAULTS = {
         u'host-search-filter': u'(&(objectClass=ipHost)(serial=X2GoServer)(cn=*))',
         u'group-search-filter': u'(&(objectClass=posifxGroup)(cn=*))',
         u'starttls': False,
+        u'agent-query-mode': u'SSH',
     },
 }
 
@@ -139,6 +184,8 @@ X2GOBROKER_SESSIONPROFILE_DEFAULTS = {
         u'sshport': 22,
         u'setdpi': 0,
         u'pack': u'16m-jpeg',
+        u'user': '',
+        u'host': [ u'localhost', ],
         u'acl-users-allow': [],
         u'acl-users-deny': [],
         u'acl-users-order': '',
diff --git a/x2gobroker/loggers.py b/x2gobroker/loggers.py
new file mode 100644
index 0000000..5be50f3
--- /dev/null
+++ b/x2gobroker/loggers.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+# Copyright (C) 2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+import os
+import sys
+import getpass
+import logging
+import logging.config
+
+# normally this would go into defaults.py, however, we do not want to create a dependency loop between loggers.py and defaults.py...
+if os.environ.has_key('X2GOBROKER_DAEMON_USER'):
+    X2GOBROKER_DAEMON_USER=os.environ['X2GOBROKER_DAEMON_USER']
+else:
+    X2GOBROKER_DAEMON_USER="x2gobroker"
+if os.environ.has_key('X2GOBROKER_LOGCONFIG'):
+    X2GOBROKER_LOGCONFIG=os.environ['X2GOBROKER_LOGCONFIG']
+else:
+    X2GOBROKER_LOGCONFIG="/etc/x2go/broker/x2gobroker-loggers.conf"
+
+# standalone daemon mode (x2gobroker-daemon) or interactive mode (called from the cmdline)?
+if getpass.getuser() in (X2GOBROKER_DAEMON_USER, 'root'):
+
+    # we run in standalone daemon mode, so let's use the system configuration for logging
+    logging.config.fileConfig(X2GOBROKER_LOGCONFIG)
+
+    # create loggers
+    logger_broker = logging.getLogger('broker')
+    logger_authservice = logging.getLogger('authservice')
+    logger_access = logging.getLogger('access')
+    logger_error = logging.getLogger('error')
+
+else:
+    logger_root = logging.getLogger()
+    stderr_handler = logging.StreamHandler(sys.stderr)
+    stderr_handler.setFormatter(logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt=''))
+
+    # all loggers stream to stderr...
+    logger_root.addHandler(stderr_handler)
+
+    logger_broker = logging.getLogger('broker')
+    logger_broker.addHandler(stderr_handler)
+    logger_broker.propagate = 0
+
+    logger_authservice = logging.getLogger('authservice')
+    logger_authservice.addHandler(stderr_handler)
+    logger_authservice.propagate = 0
+
+    logger_access = logging.getLogger('access')
+    logger_access.addHandler(stderr_handler)
+    logger_access.propagate = 0
+
+    logger_error = logging.getLogger('error')
+    logger_error.addHandler(stderr_handler)
+    logger_error.propagate = 0
diff --git a/x2gobroker/tests/test_broker_base.py b/x2gobroker/tests/test_broker_base.py
index 057647d..0bda18e 100644
--- a/x2gobroker/tests/test_broker_base.py
+++ b/x2gobroker/tests/test_broker_base.py
@@ -304,6 +304,8 @@ check-credentials = false
             'sshport': 22,
             'setdpi': 0,
             'pack': '16m-jpeg',
+            'user': '',
+            'host': [u'localhost'],
         }
         _profile = base_backend.get_profile_defaults()
         self.assertEqual(len(_expected_profile.keys()), len(_profile.keys()))
diff --git a/x2gobroker/tests/test_web_plain_base.py b/x2gobroker/tests/test_web_plain_base.py
index fd5019c..a31850f 100644
--- a/x2gobroker/tests/test_web_plain_base.py
+++ b/x2gobroker/tests/test_web_plain_base.py
@@ -30,8 +30,6 @@ from x2gobroker.web.plain import *
 urls = ( '/plain/(.*)', 'X2GoBrokerWebPlain',)
 app = web.application(urls, globals())
 
-x2gobroker.defaults.X2GOBROKER_CONFIG_DEFAULTS.update({'base': {'enable': True, 'auth-mech': 'pam', }, })
-
 class TestX2GoBrokerWebPlainBase(unittest.TestCase):
 
     ### TEST RESPONSE: is enabled?
@@ -110,7 +108,6 @@ auth-mech = testsuite
 
 
 def test_suite():
-    x2gobroker.defaults.X2GOBROKER_CONFIG_DEFAULTS.update({'base': {'enable': True, 'auth-mech': 'pam', }, })
     from unittest import TestSuite, makeSuite
     suite = TestSuite()
     suite.addTest(makeSuite(TestX2GoBrokerWebPlainBase))
diff --git a/x2gobroker/web/extras.py b/x2gobroker/web/extras.py
new file mode 100644
index 0000000..728aaec
--- /dev/null
+++ b/x2gobroker/web/extras.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+
+# This file is part of the  X2Go Project - http://www.x2go.org
+# Copyright (C) 2011-2012 by Oleksandr Shneyder <oleksandr.shneyder at obviously-nice.de>
+# Copyright (C) 2011-2012 by Heinz-Markus Graesing <heinz-m.graesing at obviously-nice.de>
+# Copyright (C) 2012 by Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
+#
+# X2Go Session Broker 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.
+#
+# X2Go Session Broker 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, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# modules
+import os.path
+import paramiko
+import x2gobroker.defaults
+
+class X2GoBrokerPubKeyService:
+
+    http_header_items = {
+        'Content-Type': 'text/plain; charset=utf-8',
+        'Expires': '+1h',
+    }
+
+    def _gen_http_header(self):
+
+        for http_header_item in self.http_header_items.keys():
+            web.header(http_header_item, self.http_header_items[http_header_item])
+
+    def GET(self):
+
+        output = ""
+
+        broker_home = x2gobroker.defaults.X2GOBROKER_HOME
+
+        if os.path.exists('{home}/.ssh/id_rsa.pub'.format(home=broker_home)):
+            pubkey = paramiko.RSAKey(filename='{home}/.ssh/id_rsa'.format(home=broker_home))
+            output += 'ssh-rsa {pubkey} {user}@{hostname}\n'.format(pubkey=str(pubkey.get_base64()), user=x2gobroker.defaults.X2GOBROKER_DAEMON_USER, hostname=x2gobroker.defaults.X2GOBROKER_HOSTNAME)
+
+        if os.path.exists('{home}/.ssh/id_dsa.pub'.format(home=broker_home)):
+            pubkey = paramiko.DSSKey(filename='{home}/.ssh/id_dsa'.format(home=broker_home))
+            output += 'ssh-dss {pubkey} {user}@{hostname}\n'.format(pubkey=str(pubkey.get_base64()), user=x2gobroker.defaults.X2GOBROKER_DAEMON_USER, hostname=x2gobroker.defaults.X2GOBROKER_HOSTNAME)
+
+        if not output:
+            return web.notfound()
+
+        return output
diff --git a/x2gobroker/web/html.py b/x2gobroker/web/html.py
index a85a1ff..1a0b1db 100644
--- a/x2gobroker/web/html.py
+++ b/x2gobroker/web/html.py
@@ -26,7 +26,7 @@ import web
 # Python X2Go Broker modules
 from x2gobroker.defaults import X2GOBROKER_DEFAULT_BACKEND as _X2GOBROKER_DEFAULT_BACKEND
 
-class X2GoBrokerWebHtml:
+class X2GoBrokerWeb:
 
     http_header_items = {
         'Content-Type': 'text/html; charset=utf-8',
diff --git a/x2gobroker/web/json.py b/x2gobroker/web/json.py
index b879b77..ef35a0f 100644
--- a/x2gobroker/web/json.py
+++ b/x2gobroker/web/json.py
@@ -21,9 +21,8 @@
 # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # modules
-import web
 
-class X2GoBrokerWebJson:
+class X2GoBrokerWeb:
 
     # MUSIC OF THE FUTURE
     pass
\ No newline at end of file
diff --git a/x2gobroker/web/plain.py b/x2gobroker/web/plain.py
index b6c5f83..07c36b2 100644
--- a/x2gobroker/web/plain.py
+++ b/x2gobroker/web/plain.py
@@ -25,10 +25,11 @@ import web
 import types
 
 # Python X2Go Broker modules
-from x2gobroker.defaults import X2GOBROKER_DEFAULT_BACKEND as _X2GOBROKER_DEFAULT_BACKEND
-from x2gobroker.defaults import X2GOBROKER_DEBUG as _X2GOBROKER_DEBUG
+import x2gobroker.defaults
 
-class X2GoBrokerWebPlain:
+from x2gobroker.loggers import logger_broker, logger_error
+
+class X2GoBrokerWeb:
 
     http_header_items = {
         'Content-Type': 'text/plain; charset=utf-8',
@@ -41,14 +42,15 @@ class X2GoBrokerWebPlain:
             web.header(http_header_item, self.http_header_items[http_header_item])
 
     def GET(self, backend):
-        if _X2GOBROKER_DEBUG:
+        if x2gobroker.defaults.X2GOBROKER_DEBUG:
+            logger_broker.warn('GET http request detected, if unwanted: disable X2GOBROKER_DEBUG')
             return self.POST(backend)
         return web.notfound()
 
     def POST(self, backend):
 
         if not backend:
-            backend = _X2GOBROKER_DEFAULT_BACKEND
+            backend = x2gobroker.defaults.X2GOBROKER_DEFAULT_BACKEND
         else:
             backend = backend.rstrip('/')
 
@@ -58,14 +60,15 @@ class X2GoBrokerWebPlain:
         exec("import x2gobroker.brokers.{backend}_broker".format(backend=backend))
         exec("broker_backend = x2gobroker.brokers.{backend}_broker.X2GoBroker()".format(backend=backend))
         global_config = broker_backend.get_global_config()
-        backend_config = broker_backend.get_backend_config()
 
         # set the client address for the broker backend
         ip = web.ctx.env.get('HTTP_X_FORWARDED_FOR', web.ctx.get('ip', ''))
         if ip:
+            logger_broker.info('client address is {address}'.format(address=ip))
             broker_backend.set_client_address(ip)
-        elif not _X2GOBROKER_DEBUG:
+        elif not x2gobroker.defaults.X2GOBROKER_DEBUG:
             # if the client IP is not set, we pretend to have nothing on offer
+            logger_error.error('client could not provide an IP address, pretending: 404 Not Found')
             return web.notfound()
 
         # if the broker backend is disabled in the configuration, pretend to have nothing on offer
@@ -89,6 +92,7 @@ class X2GoBrokerWebPlain:
         output = ''
         self._gen_http_header()
 
+        logger_broker.debug ('username: {username}, password: {password}, task: {task}, profile_id: {profile_id}'.format(username=username, password='XXXXX', task=task, profile_id=profile_id))
         if broker_backend.check_access(username=username, password=password, authid=authid):
 
             ###
@@ -142,7 +146,7 @@ class X2GoBrokerWebPlain:
 
                 if profile_id:
 
-                    profile_info = broker_backend.select_session(profile_id=profile_id)
+                    profile_info = broker_backend.select_session(profile_id=profile_id, username=username)
                     if profile_info.has_key('server'):
                         output += "SERVER:"
                         output += profile_info['server']
@@ -152,8 +156,8 @@ class X2GoBrokerWebPlain:
                         if profile_info.has_key('authentication_key'):
                             output += ""
                         if profile_info.has_key('session_info'):
-                            output += "SESSION_INFOS:\n"
-                            output += "\n".join(profile_info['session_info']) + "\n"
+                            output += "SESSION_INFO:"
+                            output += profile_info['session_info'] + "\n"
 
             return output
 


hooks/post-receive
-- 
x2gobroker.git (HTTP(S) Session broker for X2Go)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "x2gobroker.git" (HTTP(S) Session broker for X2Go).




More information about the x2go-commits mailing list