Authenticate from Linux to Windows SQL Server with pyodbc

29,265

Solution 1

I ended up using the pymssql library which basically is pyodbc on top of the FreeTDS driver. It worked out of the box.

Weird how I had such a hard time discovering this library..

Solution 2

You must obtain a Kerberos ticket for this to work. Your example doesn't specify whether your Linux system is set up to authenticate via Kerberos or whether you have previously obtained a Kerberos ticket before your code hits your connection string.

If your Linux system is set up to authenticate via Kerberos, then as a proof of concept you can obtain a Kerberos ticket using kinit from the command line. Here's what works for me in python3 running in Ubuntu on Windows via the WSL. The python code:

#!/usr/bin/env python

# minimal example using Kerberos auth
import sys
import re
import pyodbc

driver='{ODBC Driver 17 for SQL Server}'
server = sys.argv[1]
database = sys.argv[2]

# trusted_connection uses kerberos ticket and ignores UID and PASSWORD in connection string
# https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/using-integrated-authentication?view=sql-server-ver15

try:
    cnxn = pyodbc.connect(driver=driver, server=server, database=database, trusted_connection='yes')
    cursor = cnxn.cursor()
except pyodbc.Error as ex:
    msg = ex.args[1]
    if re.search('No Kerberos', msg):
        print('You must login using kinit before using this script.')
        exit(1)
    else:
        raise

# Sample select query
cursor.execute("SELECT @@version;")
row = cursor.fetchone()
while row:
    print(row[0])
    row = cursor.fetchone()
print('success')

This tells you if you don't have a ticket. Since it uses a ticket you don't have to specify a user or password in the script. It will ignore both.

Now we run it:

user@localhost:~# kdestroy # make sure there are no active tickets
kdestroy: No credentials cache found while destroying cache

user@localhost:~# python pyodbc_sql_server_test.py tcp:dbserver.example.com mydatabase
You must login using kinit before using this script.

user@localhost:~# kinit
Password for [email protected]:

user@localhost:~# python pyodbc_sql_server_test.py tcp:dbserver.example.com mydatabase
Microsoft SQL Server 2016 (SP2-GDR) (KB4505220) - 13.0.5101.9 (X64)
        Jun 15 2019 23:15:58
        Copyright (c) Microsoft Corporation
        Enterprise Edition (64-bit) on Windows Server 2016 Datacenter 10.0 <X64> (Build 14393: )

success

user@localhost:~#

You may also have success obtaining a Kerberos ticket from python code that runs before you make this connection but that is beyond the scope of this answer. A search for python Kerberos modules might point you toward a solution.

It also appears possible to set up the Linux system so that as soon as a user logs in it automatically obtains a Kerberos ticket that can be passed to other processes. That is also outside of the scope of this answer but a search for automatic Kerberos ticket upon Linux login may yield some clues.

Solution 3

I find two ways for same task. I have MSSQL server with AD auth.

You can use JVM. Load and install JAVA https://www.oracle.com/technetwork/java/javase/downloads/jre8-downloads-2133155.html. Also install JPype1 version 0.6.3 pip install JPype==0.6.3. Version above 0.6.3 won't work correct

import jaydebeapi
import pandas as pd
driver_name = "net.sourceforge.jtds.jdbc.Driver"
connection_url="jdbc:jtds:sqlserver://<server>:<port>/<database name>"
connection_properties = {
"domain": "<domain name>",
"user": "<username>",
"password": "<pwd>"}
jar_path =  <path to jsds>"/jtds-1.3.1.jar"
CONN = jaydebeapi.connect(driver_name, connection_url, connection_properties, jar_path)
sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS"
df = pd.read_sql(sql, CONN)

This version was too slow for me.

Also You can use pyodbc via FreeTDS. To create a FreeTDS connection Install FreeTDS on your Linux apt-get install tdsodbc freetds-bin, configure FreeTDS /etc/odbcinst.ini like this:

[FreeTDS]
Description=FreeTDS
Driver=/usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so
Setup=/usr/lib/x86_64-linux-gnu/odbc/libtdsS.so

and turn it on odbcinst -i -d -f /etc/odbcinst.ini

After that, you can use pyodbc

import pandas as pd
import pyodbc    
CONN =pyodbc.connect('DRIVER={FreeTDS};'
                                  'Server=<server>;'
                                  'Database=<database>;'
                                  'UID=<domain name>\\<username>;'
                                  'PWD=<password>;'
                                  'TDS_Version=8.0;'
                                  'Port=1433;')
sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS"
df = pd.read_sql(sql, CONN)

It's works much faster

Solution 4

I was trying to do the same thing and after reading the OPs answer I tested out pymssql and noticed that it worked with just the below:

pymssql.connect(server='myserver', user='domain\username', password='password', database='mydb')

After realizing that that was all pymssql needed I went back to pyodbc and was able to get it working with:

pyodbc.connect("DRIVER={FreeTDS};SERVER=myserver;PORT=1433;DATABASE=mydb;UID=domain\username;PWD=password;TDS_Version=8.0")

I just wanted to thank you for posting this as it helped me so greatly!!!! :)

Share:
29,265

Related videos on Youtube

Esser420
Author by

Esser420

Updated on May 20, 2021

Comments

  • Esser420
    Esser420 about 3 years

    I am trying to connect from a linux machine to a windows SQL Server with pyodbc.

    I do have a couple of constraints:

    • Need to log on with a windows domain account
    • Need to use python3
    • Need to do it from Linux to Windows
    • Need to connect to a specific instance

    I set up the environment as described by microsoft and have it working (I can import pyodbc and use the configured mussel driver).

    I am not familiar with Windows domain authentication and what not, so there is where my problem is.

    My connection string:

    DRIVER={ODBC Driver 17 for SQL Server};SERVER=myserver.mydomain.com;PORT=1433;DATABASE=MyDatabase;Domain=MyCompanyDomain;Instance=MyInstance;UID=myDomainUser;PWD=XXXXXXXX;Trusted_Connection=yes;Integrated_Security=SSPI
    

    Supposedly one should use "Trusted_Connection" to use the Windows domain authentication instead of directly authenticating with the SQL server.

    The error I get when running pyodbc.connect(connString):

    pyodbc.Error: ('HY000', '[HY000] [unixODBC][Microsoft][ODBC Driver 17 for SQL Server]SSPI Provider: No Kerberos credentials available (851968) (SQLDriverConnect)')
    

    From other sources I read this should work on Windows as this code would use the credentials of the currently logged in user.

    My question is how can I connect to a Windows SQL Server instance from Linux using Windows Domain credentials.

    • Gord Thompson
      Gord Thompson about 6 years
      I'm fairly certain that Microsoft's ODBC driver for Linux (msodbcsql) only supports Kerberos for connecting to a SQL Server instance using Windows credentials. If you don't have the appropriate Kerberos setup then you might be able to use FreeTDS ODBC instead, since it is able to use the older NTLMv2 protocol (if the SQL Server will accept it).
    • Esser420
      Esser420 about 6 years
      Thanks! I'll give it a go with the FreeTDS driver
  • Esser420
    Esser420 about 6 years
    Thanks for the suggestion (I would definitely prefer using SQL authentication but there is company politics involved :P)
  • eatmeimadanish
    eatmeimadanish about 6 years
    Then don't run your server in linux, and use a microsoft box instead, make sure you explain to them the licensing costs and the management costs. Sometimes when you start escalating costs based on policy, they quickly change their tune.
  • Gourgandine
    Gourgandine over 4 years
    Unfortunately the pymssql project is discontinued and you should consider using pyodbc
  • poleguy
    poleguy about 4 years
    Centos 7: yum install freetds freetds-devel; Driver=/usr/lib64/libtdsodbc.so Setup=/usr/lib64/libtdsS.so
  • Luis Lezcano Airaldi
    Luis Lezcano Airaldi almost 4 years
    Thanks for this. Do you know if I obtain a ticket for a another user (different from the one running the process) with kinit, the ODBC driver will use that ticket when connecting? Or is there a way to specify a custom user? Since UID and PWD options will be ignored
  • benrifkah
    benrifkah almost 4 years
    @LuisLezcanoAiraldi Good question. I imagine pyodbc would try whatever ticket it found regardless of whether the user in the ticket matched the login user, but that is purely a guess. There are more mysteries though: Apparently it's possible to have multiple active tickets for different domains/users. I don't know what pyodbc would do in this case.
  • José Cousiño
    José Cousiño over 3 years
    As stated in the project page: "Recent Changes Version 2.1.5 - 2020-09-17 - Mikhail Terekhov General Revert deprecation" Source: pypi.org/project/pymssql It is truly the best answer to this issue.