Impersonating IIS DefaultAppPool in standalone application - c#

Description of my problem sounds somewhat complicated, what makes me think that my approach is flawed, so I will also appreciate any better idea.
Short description:
Given connection string to MSSQL 2008 DB and website name deployed on IIS6, I want to verify programatically whether website is able to connect to database.
Long description:
I have MSSQL Server database, let's call it portal_db.
I have an application deployed on IIS6, called portal. I can access it by url http://localhost/portal . In Web.config file I specified connection string to my database, which look like: "server=(local)\SQLEXPRESS;trusted_connection=yes;database=portal_db"
Web application is accessing database using System.Data.SqlClient.SqlConnection, without any wrappers, ORMs, mappings, anything.
Website is configured to run in appool PortalAppPool. It's using ApplicationPoolIdentity as a security context.
It is not possible to easily modify web application code (particularly the way it accesses database)
When my web application tries to connect to database it either succeeds or fails, depending on whether user IIS APPPOOL\PortalAppPool is configured in MSSQL database. That's a part which I understand, but when deploying my app I often forget to create new user/login in db for apppool virtual account. So what I want to do, is to verify from separate, standalone, console app (preferably written in C#, but not necessarily), whether my web application can access database, in following way:
Read connection string from Web.config
Read app pool identity settings (managed to do this by Directory Services API)
Impersonate identity with credentials defined on app pool (using impersonation class I found here: http://platinumdogs.me/2008/10/30/net-c-impersonation-with-network-credentials/ which uses ideas found in many other places, including MSDN)
Open SqlConnection with connection string read from Web.config
It boils down to following snippet:
using (new Impersonator("IIS APPPOOL\\PortalAppPool", "", ""))
{
SqlConnection conn = new SqlConnection(databaseConnectString);
conn.Open();
}
Everything works very well, when my app pool security context is set to any other value than AppPoolIdentity - specific user, local system, etc. When I change credentials passed to Impersonator to my user's name and password, I get desired result (exception when I have no login mapping in database, and everything is OK when I add one). But I just seem to not be able to impersonate IIS APPPOOLS\PortalAppPool virtual account - just have no idea what parameters should be passed to LogonUser - I would not be surprised if it would not be even possible. Maybe I am focused on impersonation approach too much (I am using it to access registry keys and services of other users and it works good), and maybe there is some better way.
If you have any other, better ideas, or need some more explanation to this problem, please let me know.

I don't think you can impersonate a virtual account (IIS service account). They are special service accounts setup mainly for IIS security. They are for local services only and cannot be attached to any domains. Virtual accounts in Windows Server 2008 R2 and Windows 7 are "managed local accounts" that provide the following features to simplify service administration:
No password management is required.
The ability to access the network with a computer identity in a domain environment.
You cannot "Log into" a virtual account, they are used by windows for security purposes:
Some light reading if you have time:
This gives a brief overview of MSAs and Virtual Accounts
The differences between MSAs and Virtual Accounts
The dirty details on each and how to manage them
To solve your original problem, you could build an app that could do the same logic but check the sql server if it has the correct users setup instead of simply trying to login with the account.

Related

Azure App Service can't access SQL Server - Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'

I have an app service in Azure operating as an API for a system I'm designing. As the API is responsible for accessing the database directly, I obviously don't want to be storing connection strings containing credentials anywhere if possible, so am looking to use Managed Identities to grant the App Service access to the database (also hosted on Azure).
Within the Azure portal, I've enabled System-Assigned Identity within the Settings section of the App Service, then given the service the role of owner of the SQL Server via SQL Server -> Access Control -> Role Assignments-> Add.
As I understand it, Active Directory Users shouldn't even come into this as they are user-assigned identities rather than system-assigned identities, and take more setting up (or storing their credentials in the connection string).
As for the code, it's pretty much a carbon copy of this >> https://github.com/medhatelmasry/JwtAuthentication, the only differences being that I've added
services.BuildServiceProvider().GetService<ApplicationDbContext>().Database.Migrate();
to the end of the ConfigureServices method within Startup.cs, and added the below to the constructor of ApplicationDbContext as per Microsoft's instructions:
var conn = (System.Data.SqlClient.SqlConnection)Database.GetDbConnection();
conn.AccessToken = (new Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;
When attempting to run this service in Azure, however, I get an exception when calling services.BuildServiceProvider().GetService<ApplicationDbContext>().Database.Migrate();:
Login failed for user 'NT AUTHORITY\ANONYMOUS LOGON'
I've tried StackOverflow, MSDN, Azure Help, Pluralsight and whatever random forums turn up from Google, and not managed to find an answer on any of them. I've only just got through a whole week of staying up until stupid o'clock every day trying to fix connection string configurations only to find Azure was changing the name of the connection string parameter that I was giving it and not saying a word about it (and nothing in any Microsoft documentation about it either).
Azure is becoming a serious pain in my ass, I haven't even started adding endpoints to the API yet, let alone creating an actual application to use it, this is ridiculous.
Eventually found the answer here >> https://learn.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/tutorial-windows-vm-access-sql#create-a-contained-user-in-the-database-that-represents-the-vms-system-assigned-identity
The App Service was indeed set as an owner of the server, but hadn't had a user provisioned on the database, so my problem was resolved by logging into the database via SSMS and running:
CREATE USER [My App Service Name] FROM EXTERNAL PROVIDER
then:
ALTER ROLE db_owner ADD MEMBER [My App Service Name]
However, I removed the ownership role of the App Service on the server's Access Control (IAM) page, and am still able to connect successfully, not sure why that is but this is probably just a lack of SQL user knowledge on my part. It actually suits me as at the moment my App Service has a provisioned SQL user with db_owner role assigned on the database itself, but not on the overall server.
From my understanding you have to go through the prerequisite process of creating, enabling and allowing Azure AD users and also setting SQL Admin to an Azure AD user.
There's a pretty comprehensive guide here including creating, accessing and using tokens for Managed Identities Tutorial: Secure Azure SQL Database connection from App Service using a managed identity

Window authentication in sql server and asp.net

I am using Window Authentication for sql database for my asp.net web application.but after i deploy my website to another computer I'm unable to connect to the database.Is there any property for window authentication to set user name and password in web.config file.
Does this sound like your scenario?
IIS web app built on local machine
Web app uses windows authentication
Web app talks to database also on local machine
Web app users also have valid database logins
Web app users credentials are passed to database via trusted connection
Everything works great. Then you move it to a different environment where the database and IIS are no longer on the same server and then you cannot get the database to recognize the credentials IIS is passing in?
If so, then welcome to my hell a few months ago. The problem is that once the two processes are not on the same box, they don't trust each other any more. Network administration permissions are required to get the two process to trust each other across server boundaries. Unfortunately, I didn't blog about it but the general steps were
mark the IIS service account as trustworthy (Active directory setting)
set the service principal name (SPN) to either the netbios name or FQDN
something had to be set on both boxes to indicate they could trust each other
Sorry for the vague recollection and as I wasn't the one with credentials to fix anything, I only got to observe them clicking the buttons. The starting article on How to connect to SQL Server using Windows Authentication got us fairly far down the track.
This deals with the fact that on your developer box, you (or the anon user? or the user the develper web service spins up as?) is trusted by the local instance of SQL. But, when you deploy, the user ASP.NET runs under is not trusted.
Options
As George mentioned, go with a SQL logon, not windows
Set up a proper trust between the web server and SQL Server
Impersonate a user for the data context (this can get complex, btw)
You are most likely using a trusted connection string, e.g.
Data Source=myServerAddress;Initial
Catalog=myDataBase;Integrated
Security=SSPI;
...as you are using windows authentication. You need to use a standard connection string. e.g.
SQL Server 2008:
Data Source=myServerAddress;Initial
Catalog=myDataBase;User
Id=myUsername;Password=myPassword;
Just be sure to set up a non-windows authentication user within SQL Server.
Authentication mode should be set in the web.config. Also, check if anonymous authentication is disabled or not.
<authentication mode="Windows"/>

How to ensure DB security for a Windows Forms application?

The basic setup is classic - you're creating a Windows Forms application that connects to a DB and does all kinds of enterprise-y stuff. Naturally, such an application will have many users with different access rights in the DB, and each with their own login name and password.
So how do you implement this? One way is to create a DB login for every application user, but that's a pretty serious thing to do, which even requires admin rights on the DB server, etc. If the DB server hosts several applications, the admins are quite likely not to be happy with this.
In the web world typically one creates his own "Users" table which contains all the necessary info, and uses one fixed DB login for all interaction. That is all nice for a web app, but a windows forms can't hide this master login information, negating security altogether. (It can try to hide, but all such attempts are easily broken with a bit of effort).
So... is there some middle way? Perhaps logging in with a fixed login, and then elevating priviledges from a special stored procedure which checks the username and password?
Added: OK, so integrated authentication and windows groups seem to be a fair choice in most situations, so I accepted the relevant answer. Still, if anyone can come up with a non-integrated authentication solution, they'll get an upvote from me.
For WinForms use Windows groups. No passwords are needed because the credentials are inferred from the Windows login using your application.
This is best practice
Basically:
The user belongs to a group (assumes single domain)
Group is a login in the SQL Instance
SQL login maps to a database user
DB User belongs to a DB role
Role has object permissions
It's worth reading up first before having someone try to capture all the information here
Edit:
If you have a workgroup, you can still do it by setting up sqlbox\bob, sqlbox\hans etc in a sqlbox local group.
When someone tries to connect (say bob on his PC) windows will ask them for their details. As long as bob knows his SQLbox account detailsm he can connect.
But then, I've not tried this in a workgroup setting...
In addition to using Windows Domain/AD Groups (put the AD groups in appropriate roles you create in SQL Server, so all account maintenance moves to AD), be sure to use the Application Name in your connection string - this allows you to see which applications are performing operations in the profiler etc.
Because when everyone is logging on as themselves from different applications - windows and web, it helps to know that it's actions taking place through an application and not just any user's ad hoc query through ODBC and Excel, say (if you allow users access to certain views for data export or report writing).
Can't you use Active Directory Groups to make the management of the db connections easier?
From MSDN...
The name can be a Windows user name or a Windows group name, of the form DOMAIN\Name.
That way you may have a few groups, read-only, editor, manager, admin etc. It's how I have achieved similar things in that past with Click-Once apps.
It really is the best choice for what you are doing.
I am assuming this is an existing app? If it was new I would otherwise say to go client server with web-services or similar.
PK :-)
With regards to wanting to hide your authentication and use a single application login with WinForms, if the login has very little privilege - select only on views and execute only on stored procedures, anyone who manages to reverse engineer your encryption of the login information in your application will only be able to perform the same functions they could perform in your application. If you absolutely have to raise the level on the security, you could authenticate each stored proc against your users table (poassing user and hash to each SP). Also, rotate the central application login on a regular basis.
All this is a lot more difficult than using integrated authentication and implementing AD platform in your environment. So you're effectively writing your own directory and authentication instead of using one off the shelf.
In addition to gbn's update about workgroup versus domain, you can run the application with RUNAS /NETONLY /USER:SERVER\USER with user credentials on the domain or server which your machine is not a member of. At the point that the application makes the connection to the database, the remote credentials will be authenticated and used. I have an application which actually checks to see how it was run and if it not run with a particular switch, it prompts for username and password and then re-runs itself using the Windows API using a function equivalent to RUNAS /NETONLY /USER:DOMAIN\USER. This is because our workstations are not currently on the domain (or on a domain with a trust relationship) of the SQL Server. In this case, you could still manage the security in local groups on the SQL Server or groups on the domain of the SQL Server. You would basically just lose out on the automated authentication token.

SQLNET.AUTHENTICATION_SERVICES= (NTS) and ASP.NET

I'm trying to access an oracle database using
using System.Data.OracleClient;
from a console application, accessing the database is fine. however from an ASP.NET web site i get the error:
ORA-12640: Authentication adapter initialization failed
I've googled around and found that changing sqlnet.ora file would solve the issue
//before
SQLNET.AUTHENTICATION_SERVICES= (NTS)
//after
SQLNET.AUTHENTICATION_SERVICES= (NONE)
Later I found another application on the same server, that uses other database of Oracle as well, is requiring the value of SQLNET.AUTHENTICATION_SERVICES to be "NTS". This would cause my web site to fail accessing the database with the error ORA-12640. I have tried "ALL" as value but still it didn't work.
How can I configure my website to access the oracle database while sqlnet.ora is configured as "SQLNET.AUTHENTICATION_SERVICES= (NTS)" ?
P.S. the website uses Windows Authentication and impersonate as follow:
<authentication mode="Windows"/>
<identity impersonate="true"/>
This looks like the multi-hop impersonation issue to me.
If it's an option for you, I suggest having your application run under a single identity when accessing the database (this should also allow connection pooling to occur as a beneficial side-effect).
To do this, you would need to configure an app pool to run under an account that has access to Oracle. Once the application is running under that app pool, turn impersonation off in your application so that the database calls occur using the app pool identity.
If you have to impersonate the calling users over the network, the method used will depend on your environment. For more information, see How to Use Impersonation and Delegation in ASP.NET 2.0.
I was also facing the same issue, but finally got it working. Created a service account(named kerb_user in the active directory) and changed the app pool authentication to run as "kerb_user".
First I tried with this, but it was failed.
Please check the request log in oracle database, where you can verify the OS_USERNAME carefully. In my case it shows kerb_user, where as for other kerberos user requested OS_USERNAME was suffixed with domain name, which was missing in my case.
Then I did two changes.
Modified the app pool identity with domain name: kerb_user#xyz.com
Modified the sqlnet.ora file on app server and changed authentication to "ALL"
//before - not working
SQLNET.AUTHENTICATION_SERVICES= (NONE)
//after - worked
SQLNET.AUTHENTICATION_SERVICES= (ALL)
Debugging
Check the oracle log, if requested OS_USERNAME is suffixed with domain name(here kerb_user#xyz.com) or not. If suffixed, will work for sure.
Please verify service user on both side(app- AD User and db- Service User) server, user should have same name.
Verify the service user access at db server and ensure, user must have kerberos access to that database.
Check the SPN settings
Ref: https://www.codeproject.com/Articles/27554/Authentication-in-web-services-using-C-and-Kerbero

Why can't I open a file using impersonation on a remote machine?

I have a WCF service written in C# being hosted on a remote machine, running as the local administrator account. From my machine logged in as an active directory user, I am sending a command that simply tells it to open a file on the network. I have access to the file, but the administrator account on the host machine does not. I'm using the [OperationBehavior(Impersonation=ImpersonationOption.Required)] meta tag on the method that requires impersonation, and I have the credential type and security modes set correctly. I can verify that account is indeed trying to be impersonated by comparing Windows Identities, but I still get an access denied exception. I'm thinking it has something to do with active directory not authenticating the impersonated user. Is there something I'm missing?
You are entering the domain of Kerberos security and two hops-authentication.
You have two options:
Take the red pill: try to get the two hops-authentication to work. Make sure you have at least a Windows Server 2003 domain, time properly synchronized between all machines and setup proper delegation for the spefic users/computer accounts. If you're really "lucky" you'll have to configure SPNs with SetSPN.
Take the blue pill: forget two hops-authentication, impersonate the WCF service under an account that has just enough rights, and check authorization in an earlier step.
Forgive my frustration, but I do think that my brief experience with this topic has cost me at least 10 years of my life. I hate to see that happend to anyone else. Anyways, this post should give you enough Google keywords if you're feeling brave.
Eventlog and network monitor are useful for debugging...
You also probably need to set up delegation from the web server to the file server. This will allow the file server to trust credentials that the web server has validated. See this MSDN article on how to set up delegation for your application, particularly the section on configuring AD.

Categories