├── django_conch
├── __init__.py
├── models.py
├── management
│ ├── __init__.py
│ └── commands
│ │ ├── __init__.py
│ │ └── conch.py
└── checker.py
├── .gitignore
├── setup.py
├── UNLICENSE
└── README.md
/django_conch/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django_conch/models.py:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/django_conch/management/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/django_conch/management/commands/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | *.pyc
3 | .DS_Store
4 | dist
5 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup, find_packages
2 |
3 | setup(
4 | name='django-conch',
5 | version='0.0.1',
6 | description='Expose the Django shell as an SSH server.',
7 | author='Zachary Voase',
8 | author_email='z@zacharyvoase.com',
9 | url='https://github.com/zacharyvoase/django-conch',
10 | packages=find_packages(),
11 | install_requires=[
12 | 'Twisted>=12.3.0',
13 | ],
14 | )
15 |
--------------------------------------------------------------------------------
/django_conch/checker.py:
--------------------------------------------------------------------------------
1 | from twisted.cred import checkers, error, credentials
2 | from twisted.internet import defer
3 | from twisted.python import failure
4 | from zope.interface import implements
5 |
6 | from django.contrib.auth.models import User, check_password
7 |
8 |
9 | class DjangoSuperuserCredChecker:
10 | implements(checkers.ICredentialsChecker)
11 |
12 | credentialInterfaces = (credentials.IUsernamePassword,
13 | credentials.IUsernameHashedPassword)
14 |
15 | user_queryset = User.objects.filter(is_superuser=True)
16 |
17 | def passwordMatched(self, matched, user):
18 | if matched:
19 | return user.username
20 | return failure.Failure(error.UnauthorizedLogin())
21 |
22 | def requestAvatarId(self, credentials):
23 | try:
24 | user = self.user_queryset.get(username=credentials.username)
25 | return defer.maybeDeferred(
26 | check_password,
27 | credentials.password,
28 | user.password).addCallback(self.passwordMatched, user)
29 | except User.DoesNotExist:
30 | return defer.fail(error.UnauthorizedLogin())
31 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # django-conch
2 |
3 | Run an SSH server which provides direct access to the Django shell for
4 | superusers.
5 |
6 |
7 | ## Installation
8 |
9 | pip install django_conch
10 |
11 | Then just add `'django_conch'` to your `INSTALLED_APPS` setting. Done.
12 |
13 |
14 | ## Usage
15 |
16 | django-admin.py conch -p 1234
17 |
18 | This runs the SSH server on port 1234—it's best to execute this under your
19 | daemonization tool of choice.
20 |
21 | ssh -p 1234 username@localhost
22 |
23 | Run this to access the shell. All the models in your project will be imported
24 | into the top-level namespace for you.
25 |
26 |
27 | ## (Un)license
28 |
29 | This software is released under the [Unlicense](http://unlicense.org/).
30 |
31 | > This is free and unencumbered software released into the public domain.
32 | >
33 | > Anyone is free to copy, modify, publish, use, compile, sell, or
34 | > distribute this software, either in source code form or as a compiled
35 | > binary, for any purpose, commercial or non-commercial, and by any
36 | > means.
37 | >
38 | > In jurisdictions that recognize copyright laws, the author or authors
39 | > of this software dedicate any and all copyright interest in the
40 | > software to the public domain. We make this dedication for the benefit
41 | > of the public at large and to the detriment of our heirs and
42 | > successors. We intend this dedication to be an overt act of
43 | > relinquishment in perpetuity of all present and future rights to this
44 | > software under copyright law.
45 | >
46 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
47 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
48 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
49 | > IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
50 | > OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
51 | > ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
52 | > OTHER DEALINGS IN THE SOFTWARE.
53 | >
54 | > For more information, please refer to
55 |
--------------------------------------------------------------------------------
/django_conch/management/commands/conch.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from optparse import make_option
3 | import json
4 | import os
5 |
6 | from django.core.management.base import BaseCommand
7 | from django.db.models.loading import get_models, get_apps
8 | from twisted.application import internet, service
9 | from twisted.conch.insults import insults
10 | from twisted.conch.manhole import ColoredManhole
11 | from twisted.conch.manhole_ssh import ConchFactory, TerminalRealm
12 | from twisted.cred import portal
13 |
14 | from django_conch.checker import DjangoSuperuserCredChecker
15 |
16 |
17 | def make_namespace():
18 | ns = {}
19 | for app in get_apps():
20 | for model in get_models(app):
21 | ns[model.__name__] = model
22 | ns[model._meta.app_label + '_' + model._meta.object_name] = model
23 | return ns
24 |
25 |
26 | def make_service(args):
27 | checker = DjangoSuperuserCredChecker()
28 |
29 | def chainProtocolFactory():
30 | return insults.ServerProtocol(
31 | args['protocol_factory'],
32 | *args.get('protocol_args', ()),
33 | **args.get('protocol_kwargs', {}))
34 |
35 | realm = TerminalRealm()
36 | realm.chainedProtocolFactory = chainProtocolFactory
37 | ptl = portal.Portal(realm, [checker])
38 | f = ConchFactory(ptl)
39 | return internet.TCPServer(args['ssh_port'], f)
40 |
41 |
42 | application = service.Application("Django Secure Shell")
43 |
44 | if '_DJANGO_CONCH_CONFIG' in os.environ:
45 | config = json.loads(os.environ['_DJANGO_CONCH_CONFIG'])
46 | namespace = make_namespace()
47 | make_service({'protocol_factory': ColoredManhole,
48 | 'protocol_kwargs': {'namespace': namespace},
49 | 'ssh_port': config['ssh_port']}).setServiceParent(application)
50 |
51 |
52 | class Command(BaseCommand):
53 |
54 | help = 'Run a Django secure shell server on '
55 | option_list = BaseCommand.option_list + (
56 | make_option('-p', '--port', type='int',
57 | help='The port number on which to listen for SSH connections'),
58 | )
59 |
60 | def handle(self, *args, **kwargs):
61 | cmd = ['twistd', '-noy', __file__]
62 | os.environ['_DJANGO_CONCH_CONFIG'] = json.dumps({
63 | 'ssh_port': kwargs['port']})
64 | os.execvp('twistd', cmd)
65 |
--------------------------------------------------------------------------------