summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@nomic.retiolum>2013-01-24 23:40:05 +0100
committertv <tv@nomic.retiolum>2013-01-24 23:40:05 +0100
commit245a815267928096ea18d9d49cd395b87a7af175 (patch)
tree1aaba258abf30eafec4dd29b84edd7c1a5d4e842
parent17ad911ae984843d467f8492b0644227715f203a (diff)
services: minimal services.txt-over-ssh provider (twisted)
-rw-r--r--services/Makefile14
-rw-r--r--services/authorized_keys1
-rw-r--r--services/checkers.py25
-rw-r--r--services/services.txt2
-rw-r--r--services/test.py108
5 files changed, 150 insertions, 0 deletions
diff --git a/services/Makefile b/services/Makefile
new file mode 100644
index 00000000..a68f095d
--- /dev/null
+++ b/services/Makefile
@@ -0,0 +1,14 @@
+help:;@cat Makefile
+
+export authorized_keys_file := authorized_keys
+export services_file := services.txt
+export host_key_file := test.key
+
+test-client:
+ ssh localhost -p 1337 2>/dev/null
+
+test-server:
+ python test.py
+
+$(host_key_file):
+ ssh-keygen -t rsa -P '' -f $@
diff --git a/services/authorized_keys b/services/authorized_keys
new file mode 100644
index 00000000..dcb8bfeb
--- /dev/null
+++ b/services/authorized_keys
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7YrLdnXDRU2XEdZDu1BmgiT0Vaxplf3bfvSm+5o3g4AcR2yCv7h2D633c9uA0gq52EJ3V5m8B1ZcxqA0zqDptKwx+ZTMUGDls7StH5xpJyk9j5gf8DzyDLQPQG2IYszCH+8esKjo3BOFxfey8NaX+k6gvQsG3lyV0PjLvvIy4gDuMn6dPZfVAlwNYFOUNgwpku3W3A0d+UFyVjt3/sgZxM+8C3y6QE1gwT5/NfBbHM5vaEqjHcVq1ui+7a4iOXFGKkZDcd7EX6cQZSbCzZL7sZ0OmB1WpAsDCvIXfzX1YfNA0sso7ldSF6ZUGNgwEk1LootnQlCK/dfbM+i62SZ+1 tv@iiso
diff --git a/services/checkers.py b/services/checkers.py
new file mode 100644
index 00000000..dbfe1323
--- /dev/null
+++ b/services/checkers.py
@@ -0,0 +1,25 @@
+
+import base64, binascii
+from twisted.python.filepath import FilePath
+from twisted.conch.checkers import SSHPublicKeyDatabase
+
+
+class PublicKeyChecker(SSHPublicKeyDatabase):
+
+ def __init__(self, filename):
+ self.filepath = FilePath(filename)
+
+ def getAuthorizedKeysFiles(self, credentials):
+ return [self.filepath]
+
+ def checkKey(self, credentials):
+ for line in self.filepath.open():
+ parts = line.split()
+ if len(parts) < 2:
+ continue
+ try:
+ if base64.decodestring(parts[1]) == credentials.blob:
+ return True
+ except binascii.Error:
+ continue
+ return False
diff --git a/services/services.txt b/services/services.txt
new file mode 100644
index 00000000..a2b97670
--- /dev/null
+++ b/services/services.txt
@@ -0,0 +1,2 @@
+# this is a comment
+TODO declare proper services format
diff --git a/services/test.py b/services/test.py
new file mode 100644
index 00000000..06340a54
--- /dev/null
+++ b/services/test.py
@@ -0,0 +1,108 @@
+#! /usr/bin/env python
+
+from os import environ as env
+
+authorized_keys_file = env.get('authorized_keys_file', '/dev/null')
+services_file = env.get('services_file', '/dev/null')
+host_key_file = env.get('host_key_file', '/dev/null')
+host_key_pub_file = host_key_file + '.pub'
+
+
+from checkers import PublicKeyChecker
+from twisted.conch.avatar import ConchUser
+from twisted.conch.ssh.connection import SSHConnection
+from twisted.conch.ssh.factory import SSHFactory
+from twisted.conch.ssh.keys import Key
+from twisted.conch.ssh.session import SSHSession, ISession, wrapProtocol
+from twisted.conch.ssh.userauth import SSHUserAuthServer
+from twisted.cred.error import UnauthorizedLogin
+from twisted.cred.portal import IRealm, Portal
+from twisted.internet.protocol import Protocol
+from twisted.internet.reactor import listenTCP, run
+from twisted.python.components import registerAdapter
+from zope.interface import implements
+
+from twisted.python.log import startLogging
+from sys import stderr
+startLogging(stderr)
+
+
+class MyRealm:
+ implements(IRealm)
+
+ def requestAvatar(self, avatarId, mind, *interfaces):
+ return interfaces[0], MyUser(), lambda: None
+
+
+class MyUser(ConchUser):
+ def __init__(self):
+ ConchUser.__init__(self)
+ self.channelLookup.update({ 'session': SSHSession })
+
+
+class MySession:
+
+ def __init__(self, avatar):
+ pass
+
+ def getPty(self, term, windowSize, attrs):
+ pass
+
+ def execCommand(self, proto, cmd):
+ raise Exception("no executing commands")
+
+ def openShell(self, trans):
+ ep = MyProtocol()
+ ep.makeConnection(trans)
+ trans.makeConnection(wrapProtocol(ep))
+
+ def eofReceived(self):
+ pass
+
+ def closed(self):
+ pass
+
+
+registerAdapter(MySession, MyUser, ISession)
+
+
+def slurpTextfile(filename):
+ file = open(filename, 'r')
+ try:
+ return file.read()
+ finally:
+ file.close()
+
+class MyProtocol(Protocol):
+ def connectionMade(self):
+ data = slurpTextfile(services_file).replace('\n', '\r\n')
+ self.transport.write(data)
+ self.transport.loseConnection()
+
+ #def dataReceived(self, data):
+ # if data == '\r':
+ # data = '\r\n'
+ # elif data == '\x03': #^C
+ # self.transport.loseConnection()
+ # return
+ # self.transport.write(data)
+
+
+class MyFactory(SSHFactory):
+ privateKeys = {
+ 'ssh-rsa': Key.fromFile(filename=host_key_file)
+ }
+ publicKeys = {
+ 'ssh-rsa': Key.fromFile(filename=host_key_pub_file)
+ }
+ services = {
+ 'ssh-userauth': SSHUserAuthServer,
+ 'ssh-connection': SSHConnection
+ }
+
+if __name__ == '__main__':
+ portal = Portal(MyRealm())
+ portal.registerChecker(PublicKeyChecker(authorized_keys_file))
+ MyFactory.portal = portal
+ listenTCP(1337, MyFactory())
+ run()