I'm looking for a way to let my python program handle authentication through pam.
I'm using http://code.google.com/p/web2py/source/browse/gluon/contrib/pam.py for this, which works out great as long as my python program runs as root which is not ideal to my opinion.
How can I make use of pam for username/password validation without requiring root privs?
short: use a proper Python PAM implementation, setup PAM properly.
long: In a sane PAM setup, you do not need root privileges. In the end this is one of the things PAM provides, privilege separation.
pam_unix has a way to check the password for you. Seems the PAM implementation of web2py (note, it's from some contrib subdirectory...) is not doing the right thing. Maybe your PAM setup is not correct, which is hard to tell without further information; this also depends heavily on operating system and flavour/distribution.
There are multiple PAM bindings for Python out there (unfortunately nothing in the standard library), use these instead. And for configuration, there are tons of tutorials, find the right one for your system.
old/wrong, don't do this: You do not need to be root, you only need to be able to read /etc/shadow. This file has usually group shadow with read only access. So you simply need to add the user that is running the PAM check in the shadow group.
groupadd <user> shadow should do the trick.
I think the pam module is your best choice, but you don't have to embed it into your program directly. You could write a simple service which binds to a port on localhost, or listens on a UNIX domain socket, and fills PAM requests for other processes on the same host. Then have your web2py application connect to it for user/password validation.
For example:
import asyncore
import pam
import socket
class Client(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('\r\n', 1)
self._buf = data
for req in reqs.split('\r\n'):
try:
user, passwd = req.split()
except:
self.send('bad\r\n')
else:
if pam.authenticate(user, passwd):
self.send('ok\r\n')
else:
self.send('fail\r\n')
def handle_close(self):
self.close()
class Service(asyncore.dispatcher_with_send):
def __init__(self, addr):
asyncore.dispatcher_with_send.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(addr)
self.listen(1)
def handle_accept(self):
conn, _ = self.accept()
Client(conn)
def main():
addr = ('localhost', 8317)
Service(addr)
try:
asyncore.loop()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
Usage:
% telnet localhost 8317
bob abc123
ok
larry badpass
fail
incomplete
bad
At the end I ended up using pexpect and trying to su - username.
It's a bit slow, but it works pretty good.
The below example isn't polished but you'll get the idea.
Cheers,
Jay
#!/usr/bin/python
import pexpect
def pam(username, password):
'''Accepts username and password and tried to use PAM for authentication'''
try:
child = pexpect.spawn('/bin/su - %s'%(username))
child.expect('Password:')
child.sendline(password)
result=child.expect(['su: Authentication failure',username])
child.close()
except Exception as err:
child.close()
print ("Error authenticating. Reason: "%(err))
return False
if result == 0:
print ("Authentication failed for user %s."%(username))
return False
else:
print ("Authentication succeeded for user %s."%(username))
return True
if __name__ == '__main__':
print pam(username='default',password='chandgeme')
Maybe python-pam can work for you.
Not if you use they usual system (unix style) login credentials. At some point the PAM library must read the shadow file which is only readable by root. However, if you use a PAM profile that authenticates with an alternate method, such as LDAP or a database, then it can work without needing root.
This is one reason I developed my own framework that runs different parts of the URL path space under different user credentials. The login part (only) can run as root to authenticate with PAM (system), other path subtree handlers run as different users.
I'm using the PyPAM module for this.
Related
In my tool the users can provide a mail backend using certain infos on a model and send their mails via the backend which gets created from those values. This all works, but I would love to have a quick check if the provided backend actually will work before using it. Using something like this check_mail_connection doesn't work as this actually returns False even though I entered valid connection parameters.
from django.core.mail import get_connection
class User(models.Model):
...
def get_mail_connection(self, fail_silently=False)
return get_connection(host=self.email_host,
port=self.email_port,
username=self.email_username,
password=self.email_password ... )
def check_mail_connection(self) -> bool:
from socket import error as socket_error
from smtplib import SMTP, SMTPConnectError
smtp = SMTP(host=self.email_host, port=self.email_port)
try:
smtp.connect()
return True
except SMTPConnectError:
return False
except socket_error:
return False
I don't want to send a test mail to confirm, as this can easily get lost or fail on a different part of the system. This feature is for sending out emails from the users mail servers, as I suspect most of my users have a mail server anyways and I basically offer white labeling and similar stuff to them.
You have the following line smtp.connect() in your code that attempts to make a connection. If you look at the documentation for smtplib the signature for this method is:
SMTP.connect(host='localhost', port=0)
Meaning you are trying to connect to localhost with port 25 (standard SMTP port). Of course there is no server listening there and you get a ConnectionRefusedError which you catch and return False. In fact you don't even need to call connect because the documentation states:
If the optional host and port parameters are given, the SMTP
connect() method is called with those parameters during
initialization.
Hence you can simply write:
def check_mail_connection(self) -> bool:
from smtplib import SMTP
try:
smtp = SMTP(host=self.email_host, port=self.email_port)
return True
except OSError:
return False
You can also simply use the open method of the email backend's instance rather than creating the SMTP instance and calling connect yourself:
def check_mail_connection(self) -> bool:
try:
email_backend = self.get_mail_connection()
silent_exception = email_backend.open() is None
email_backend.close()
return not silent_exception
except OSError:
return False
I have a few questions for you and would like for you to answer these questions before we can go further.
What type of OS are you running the server on?
What mail client and tutorial did you follow? Postfix?
Can a user on the server send local mail to another user on the server?
What ports are open and what type of security features do you have installed?
What did your logs say when the email failed?
Are you self hosting/ are you acting as the server admin?
(It's fine if this is your first time. Everyone had a first day.)
SSL and A FQDN isn't too important if your just sending mail out. The system will still work, you just won't be able to receive mail.
(I'm talking from the sense of making sure it at least will send an email. You should at least use SSL as it can be gotten for free.)
If you checked all of these things, there is a part of the mail client that you are using and it probably won't send mail out unless it has approval. There are a lot of variables.
All of these things matter or it wont work.
Sorry meant to make this as a comment. I'm not use to speaking on here.
I have created GuI in Visual Studio 2019.
There user will enter username and password and that i have to pass to python script. That when user will click on login button, python script will be triggered and output will be displayed.
My Tried python code is:
import paramiko
import time
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
hostname = input("Enter host IP address: ")
username = input("Enter SSH Username: ")
password = input("Enter SSH Password: ")
port = 22
ssh.connect(hostname, port, username, password, look_for_keys=False)
print("ssh login successfully")
#stdin,stdout,stderr = ssh.exec_command('show version')
#output = stdout.readlines()
#print(output)
Device_access = ssh.invoke_shell()
Device_access.send(b'environment no more \n')
Device_access.send(b'show version\n')
time.sleep(2)
output = Device_access.recv(65000)
print (output.decode('ascii'))
except:
print("error in connection due to wrong input entered")
But in this i am not getting how to link with input enter to Gui c# with python script. Please let me know how i can do.
Thanks in advance!
You could use arguments to call your Python Script.
Change the python script:
import paramiko
import time
import sys # Used to get arguments
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
hostname = sys.argv[1] # Skip 0th arg, since it is just the filename
username = sys.argv[2]
password = sys.argv[3]
port = 22
ssh.connect(hostname, port, username, password, look_for_keys=False)
print("ssh login successfully")
#stdin,stdout,stderr = ssh.exec_command('show version')
#output = stdout.readlines()
#print(output)
Device_access = ssh.invoke_shell()
Device_access.send(b'environment no more \n')
Device_access.send(b'show version\n')
time.sleep(2)
output = Device_access.recv(65000)
print (output.decode('ascii'))
except:
print("error in connection due to wrong input entered")
And change your C# code which calls the Script to something like this:
Process pythonScript = new Process();
pythonScript.StartInfo.FileName = "Your python script";
pythonScript.StartInfo.Arguments = $"{YouHostnameVar} {YouUsernameVar} {YourPasswordVar}"; // Start the script with the credentials as arguments
pythonScript.Start();
There are multiple approaches to incorporating a Python script with .NET C# code
I will try and give a basic overview, along with my suggestion, but ultimately, it will be up to you to figure out what works best.
IronPython
IronPython is an actual separate interpreter to translate Python code into the .NET Common Language Runtime (CLR). It works well for simple Python2 scripts that are not reliant on certain libraries.
Python.NET
Python.NET uses the normal Python interpreter. It simply provides a way to interface between Python scripts and .NET code.
System Diagnostics (My Suggestion)
The System Diagnostics C# tool allows you to run Python scripts as a system process. Not that this only runs the Python script. In order to transfer information between the Python script and the C# code, you will need some kind of shared file. I recommend setting up a folder where you save information used by both the C# and Python programs.
For a simple implementation of System Diagnostics, along with notes on the particular way System Diagnostics is being called, check out this: https://www.dotnetlovers.com/article/216/executing-python-script-from-c-sharp
EDIT Based on Paul Sütterlin Answer
As opposed to using a file to share information, Paul correctly points out that you can pass information as arguments. He also points out the simple process tool in C#, which is easier to set up than System Diagnostics. I recommend you read the article I linked to see which solution best suits you. System diagnostics gives you more options, but they do have to be configured.
I am trying to telnet into a Cisco Switch and run a couple of commands on it. I am able to check if the host doesn't exist, not sure how to check if the username or password is correct. This is what I got so far(This is part of my class)
def login(self):
if self.user_name and self.password:
try:
self.connection=telnetlib.Telnet(self.telnet_host)
try:
self.connection.read_until('sername:',1)
self.connection.write(self.user_name+'\r\n')
self.connection.read_until('assword:',1)
self.connection.write(self.password+'\r\n')
self.connection.read_until(self.prompt,1)
print "Connected"
self.loggedON=True
except EOFError:
print "Authentication to "+ self.telnet_host+" failed.\n"
return
except:
print "Can't connect to "+self.telnet_host+"\n"
return
else:
if not self.user_name:
self.user_name=raw_input("Username: ")
self.login()
else:
self.password=raw_input("Password: ")
self.login()
It will still say it is connected even if the wrong password or username.
You could also try Exscript:
from Exscript.util.start import quickstart
def do_something(conn):
conn.autoinit()
conn.execute('show version')
quickstart('telnet://localhost', do_something)
The quickstart() function asks the user for username and password (use start() if that is not what you want). Login failure (and other errors) are handeled automatically. You may also want to look at Exscript.util.start.
First of all, you shouldn't have a blanket try/except block like that. Catch exceptions more narrowly. Also, as others have commented, you might consider SNMP.
Having said that, if you push ahead with Telnet, you might as well just reuse someone else's code. I found this for example with a simple Google search.
I have a twisted application which now needs to monitor processes running on several boxes. The way I manually do is 'ssh and ps', now I'd like my twisted application to do. I have 2 options.
Use paramiko or leverage the power of twisted.conch
I really want to use twisted.conch but my research led me to believe that its primarily intended to create SSHServers and SSHClients. However my requirement is a simple remoteExecute(some_cmd)
I was able to figure out how to do this using paramiko but I didnt want to stickparamiko in my twisted app before looking at how to do this using twisted.conch
Code snippets using twisted on how to run remote_cmds using ssh would be highly appreciated. Thanks.
Followup - Happily, the ticket I referenced below is now resolved. The simpler API will be included in the next release of Twisted. The original answer is still a valid way to use Conch and may reveal some interesting details about what's going on, but from Twisted 13.1 and on, if you just want to run a command and handle it's I/O, this simpler interface will work.
It takes an unfortunately large amount of code to execute a command on an SSH using the Conch client APIs. Conch makes you deal with a lot of different layers, even if you just want sensible boring default behavior. However, it's certainly possible. Here's some code which I've been meaning to finish and add to Twisted to simplify this case:
import sys, os
from zope.interface import implements
from twisted.python.failure import Failure
from twisted.python.log import err
from twisted.internet.error import ConnectionDone
from twisted.internet.defer import Deferred, succeed, setDebugging
from twisted.internet.interfaces import IStreamClientEndpoint
from twisted.internet.protocol import Factory, Protocol
from twisted.conch.ssh.common import NS
from twisted.conch.ssh.channel import SSHChannel
from twisted.conch.ssh.transport import SSHClientTransport
from twisted.conch.ssh.connection import SSHConnection
from twisted.conch.client.default import SSHUserAuthClient
from twisted.conch.client.options import ConchOptions
# setDebugging(True)
class _CommandTransport(SSHClientTransport):
_secured = False
def verifyHostKey(self, hostKey, fingerprint):
return succeed(True)
def connectionSecure(self):
self._secured = True
command = _CommandConnection(
self.factory.command,
self.factory.commandProtocolFactory,
self.factory.commandConnected)
userauth = SSHUserAuthClient(
os.environ['USER'], ConchOptions(), command)
self.requestService(userauth)
def connectionLost(self, reason):
if not self._secured:
self.factory.commandConnected.errback(reason)
class _CommandConnection(SSHConnection):
def __init__(self, command, protocolFactory, commandConnected):
SSHConnection.__init__(self)
self._command = command
self._protocolFactory = protocolFactory
self._commandConnected = commandConnected
def serviceStarted(self):
channel = _CommandChannel(
self._command, self._protocolFactory, self._commandConnected)
self.openChannel(channel)
class _CommandChannel(SSHChannel):
name = 'session'
def __init__(self, command, protocolFactory, commandConnected):
SSHChannel.__init__(self)
self._command = command
self._protocolFactory = protocolFactory
self._commandConnected = commandConnected
def openFailed(self, reason):
self._commandConnected.errback(reason)
def channelOpen(self, ignored):
self.conn.sendRequest(self, 'exec', NS(self._command))
self._protocol = self._protocolFactory.buildProtocol(None)
self._protocol.makeConnection(self)
def dataReceived(self, bytes):
self._protocol.dataReceived(bytes)
def closed(self):
self._protocol.connectionLost(
Failure(ConnectionDone("ssh channel closed")))
class SSHCommandClientEndpoint(object):
implements(IStreamClientEndpoint)
def __init__(self, command, sshServer):
self._command = command
self._sshServer = sshServer
def connect(self, protocolFactory):
factory = Factory()
factory.protocol = _CommandTransport
factory.command = self._command
factory.commandProtocolFactory = protocolFactory
factory.commandConnected = Deferred()
d = self._sshServer.connect(factory)
d.addErrback(factory.commandConnected.errback)
return factory.commandConnected
class StdoutEcho(Protocol):
def dataReceived(self, bytes):
sys.stdout.write(bytes)
sys.stdout.flush()
def connectionLost(self, reason):
self.factory.finished.callback(None)
def copyToStdout(endpoint):
echoFactory = Factory()
echoFactory.protocol = StdoutEcho
echoFactory.finished = Deferred()
d = endpoint.connect(echoFactory)
d.addErrback(echoFactory.finished.errback)
return echoFactory.finished
def main():
from twisted.python.log import startLogging
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ClientEndpoint
# startLogging(sys.stdout)
sshServer = TCP4ClientEndpoint(reactor, "localhost", 22)
commandEndpoint = SSHCommandClientEndpoint("/bin/ls", sshServer)
d = copyToStdout(commandEndpoint)
d.addErrback(err, "ssh command / copy to stdout failed")
d.addCallback(lambda ignored: reactor.stop())
reactor.run()
if __name__ == '__main__':
main()
Some things to note about it:
It uses the new endpoint APIs introduced in Twisted 10.1. It's possible to do this directly on reactor.connectTCP, but I did it as an endpoint to make it more useful; endpoints can be swapped easily without the code that actually asks for a connection knowing.
It does no host key verification at all! _CommandTransport.verifyHostKey is where you would implement that. Take a look at twisted/conch/client/default.py for some hints about what kinds of things you might want to do.
It takes $USER to be the remote username, which you may want to be a parameter.
It probably only works with key authentication. If you want to enable password authentication, you probably need to subclass SSHUserAuthClient and override getPassword to do something.
Almost all of the layers of SSH and Conch are visible here:
_CommandTransport is at the bottom, a plain old protocol that implements the SSH transport protocol. It creates a...
_CommandConnection which implements the SSH connection negotiation parts of the protocol. Once that completes, a...
_CommandChannel is used to talk to a newly opened SSH channel. _CommandChannel does the actual exec to launch your command. Once the channel is opened it creates an instance of...
StdoutEcho, or whatever other protocol you supply. This protocol will get the output from the command you execute, and can write to the command's stdin.
See http://twistedmatrix.com/trac/ticket/4698 for progress in Twisted on supporting this with less code.
I need to have a python client that can discover queues on a restarted RabbitMQ server exchange, and then start up a clients to resume consuming messages from each queue. How can I discover queues from some RabbitMQ compatible python api/library?
There does not seem to be a direct AMQP-way to manage the server but there is a way you can do it from Python. I would recommend using a subprocess module combined with the rabbitmqctl command to check the status of the queues.
I am assuming that you are running this on Linux. From a command line, running:
rabbitmqctl list_queues
will result in:
Listing queues ...
pings 0
receptions 0
shoveled 0
test1 55199
...done.
(well, it did in my case due to my specific queues)
In your code, use this code to get output of rabbitmqctl:
import subprocess
proc = subprocess.Popen("/usr/sbin/rabbitmqctl list_queues", shell=True, stdout=subprocess.PIPE)
stdout_value = proc.communicate()[0]
print stdout_value
Then, just come up with your own code to parse stdout_value for your own use.
As far as I know, there isn't any way of doing this. That's nothing to do with Python, but because AMQP doesn't define any method of queue discovery.
In any case, in AMQP it's clients (consumers) that declare queues: publishers publish messages to an exchange with a routing key, and consumers determine which queues those routing keys go to. So it does not make sense to talk about queues in the absence of consumers.
You can add plugin rabbitmq_management
sudo /usr/lib/rabbitmq/bin/rabbitmq-plugins enable rabbitmq_management
sudo service rabbitmq-server restart
Then use rest-api
import requests
def rest_queue_list(user='guest', password='guest', host='localhost', port=15672, virtual_host=None):
url = 'http://%s:%s/api/queues/%s' % (host, port, virtual_host or '')
response = requests.get(url, auth=(user, password))
queues = [q['name'] for q in response.json()]
return queues
I'm using requests library in this example, but it is not significantly.
Also I found library that do it for us - pyrabbit
from pyrabbit.api import Client
cl = Client('localhost:15672', 'guest', 'guest')
queues = [q['name'] for q in cl.get_queues()]
Since I am a RabbitMQ beginner, take this with a grain of salt, but there's an interesting Management Plugin, which exposes an HTTP interface to "From here you can manage exchanges, queues, bindings, virtual hosts, users and permissions. Hopefully the UI is fairly self-explanatory."
http://www.rabbitmq.com/blog/2010/09/07/management-plugin-preview-release/
I use https://github.com/bkjones/pyrabbit. It's talks directly to RabbitMQ's mgmt plugin's API interface, and is very handy for interrogating RabbitMQ.
Management features are due in a future version of AMQP. So for now you will have to wait till for a new version that will come with that functionality.
I found this works for me, /els being my demo vhost name..
rabbitmqctl list_queues --vhost /els
pyrabbit didn't work so well for me; However, the Management Plugin itself has its own command line script that you can download from your own admin GUI and use later on (for example, I downloaded mine from
http://localhost:15672/cli/
for local use)
I would use simply this:
Just replace the user(default= guest), passwd(default= guest) and port with your values.
import requests
import json
def call_rabbitmq_api(host, port, user, passwd):
url = 'http://%s:%s/api/queues' % (host, port)
r = requests.get(url, auth=(user,passwd))
return r
def get_queue_name(json_list):
res = []
for json in json_list:
res.append(json["name"])
return res
if __name__ == '__main__':
host = 'rabbitmq_host'
port = 55672
user = 'guest'
passwd = 'guest'
res = call_rabbitmq_api(host, port, user, passwd)
print ("--- dump json ---")
print (json.dumps(res.json(), indent=4))
print ("--- get queue name ---")
q_name = get_queue_name(res.json())
print (q_name)
Referred from here: https://gist.github.com/hiroakis/5088513#file-example_rabbitmq_api-py-L2