I am starting mongodb from C# code.
I am connecting to it without mentioning any port:
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = dir + #"\mongod.exe";
start.WindowStyle = ProcessWindowStyle.Hidden;
start.UseShellExecute = false;
start.Arguments = "--dbpath d:\test\mongodb\data";
Process mongod = Process.Start(start);
MongoClient client = new MongoClient();
MongoServer server = client.GetServer();
MongoDatabase database = server.GetDatabase("db_name");
at the mongodb console (output window) I see that mongodb is listening to a port.
Is it possible to start mongo without a port?
You can't. MongoDB is a standalone server. The only way to communicate with MongoDB is using TCP or unix sockets, so it's never a truly embedded database. Auto-Deploying the database doesn't make it an embedded database, it will have its own process and it will be available to other applications.
When you don't configure a port, MongoDB (and it's drivers) will use port 27017.
If you need an embedded database, use one. Candidates for C# include SQLite, db4o, perst and it's BSD-licensed fork volante, and a ton of smaller projects like siaqodb (some of these not free).
MongoDB will be trouble because it's rather aggressive about memory allocation and might need repair when things go wrong.
You can't start mongod without it listening to a port, however to restrict access you can:
use the bind_ip configuration option to limit connections to localhost only (127.0.0.1)
use the port configuration option to change to a non-standard port specific to your application
configure authentication and appropriate user roles
Given you are planning to spawn the mongod process on the user's machine, you unfortunately can't prevent determined users with Administrator access from bypassing any of the above restrictions.
As noted in the answer by #mnemosyn, there are certainly databases such as SQLite that are designed to be embeddable and compiled into your application code without spawning external processes.
Related
Context: I wrote a non-elevated WinForms app, from which I want to allow the user to query the file system using NTFS' MFT (Master File Table) because it's so damn fast! However, starting with Windows 8 or 10 or 1809 or something or other, querying the MFT requires process elevation, and I don't want to run my app elevated. So I plan to build another executable, running elevated, to query the MFT. If I do that, then I need to use inter-process communication (IPC). Seems like named pipes is simple enough (and safe?).
The challenge: I wrote a test program that uses .NET's NamedPipeServerStream and NamedPipeClientStream classes to do some IPC. When client and server processes run either both elevated or both unelevated, they can communicate using the pipe. However, if one is elevated and the other isn't, then the client throws a System.UnauthorizedAccessException exception when trying to connect to the server. I kind of expected that.
My question: Is it a simple matter of constructing the server and client pipe objects with a carefully-crafted System.IO.Pipes.PipeSecurity object? If so, please help me to craft that object, both for the client and the server.
Below is what I got working, with the first two lines being the relevant ones and the rest just to demonstrate its use.
var ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(Environment.UserName, PipeAccessRights.ReadWrite, AccessControlType.Allow));
using(var pipe = new NamedPipeServerStream(
pipeName: PipeName,
direction: PipeDirection.InOut,
maxNumberOfServerInstances: 1,
options: PipeOptions.None,
transmissionMode: PipeTransmissionMode.Byte,
inBufferSize: 1024,
outBufferSize: 1024,
pipeSecurity: ps))
{
...
}
I have a very specific issue I fail to fix with my application.
I am building up a remote software validation tool (in C#) for a secure software located inside a VDI-type infrastructure built up on Windows Server 2016.
The idea is to USB plug in a smart card with valid a certificate inside a distant computer (using classic Windows 10), access the VDI by connecting via RDP (using Windows Remote Desktop) and use the validation tool to gain access to the secured application.
Everything works smoothly as long as I am using the application from an executable launched via Visual Studio debug or built up as .exe and launched inside the remote session.
However, I have an obligation to embed the executables as Windows Services, and in such it renders the validation tools unusable. I have found out that it is because in such case the validation software uses the VDI smart card service and not, as I expected, the smart card service from the computer I access the VDI with.
As such, the validation tools seeks for cards on the VDI and not on my access computer.
So, my question is, is there such a way to "hijack" the VDI smart card reader service with the one from my own computer, or is there any way the App can detect the service from my RDP session ?
Thanks
Edit: a simplified C# method code we use to gather the data from the service:
protected void ExtractRawSC()
{
//----------------------- TEST SYSCAL
var proc1 = new ProcessStartInfo();
string Command = "certutil -scinfo > C:\\Users\\Administrateur\\Documents\\output.txt";
proc1.UseShellExecute = true;
proc1.WorkingDirectory = #"C:\Windows\System32";
proc1.FileName = #"C:\Windows\System32\cmd.exe";
proc1.Verb = "runas";
proc1.Arguments = "/c " + Command;
//proc1.WindowStyle = ProcessWindowStyle.Hidden;
Process.Start(proc1);
//------------------------
}
I currently run MongoDB pointing to the appropriate data directory using the command line below:
mongod --dbpath "somePath/data"
But currently this is a manual step that I run before running a particular suite of tests. Is there a way I can set the path within the code (without calling a script or batch file) using the Mongo C# driver to use a specific data directory?
Update:
To clarify, the reason I'm looking to do this isn't for use in production code, but to isolate test databases for different test suites and to point at a disposable and isolated data directory so that each server instance is clean at the time of running tests and is only populated with the data it requires for the same server settings as production.
You probably won't find any way to do that. The Mongo C# Driver is for programming a MongoClient, not a server. The documentation for C# Driver for MongoDB says - MongoClient class serves as the root object for working with a MongoDB server. When you are programming a client, you automatically would assume that the server is up and running. Whether you do it manually or you write another code for it, that is a different story.
Very rarely would you allow people to connect to a machine and let them start a server AND A CLIENT on it. And why is it rare? You may try to start a server on another machine and screw up with that machine (which may be providing some other completely different service too!). There are some ways (and there are times when it is needed) to start a server remotely, but that is not what you can do using the MongoDB C# Driver.
Now, in order to get your task done, you can try this:
Start one mongod per database on your server, and make each mongod listen to a different port. Then in your code, you can connect your MongoClient to mongod running on the concerned database's port. You can achieve this by using a simple if condition (or a switch case) and checking what database the MongoClient wants to connect to and thus finding the right port value to put in the connection string. Each mongod can serve only one database or more or whatever you want.
So if you are running three mongod's on port1, port2 and port3 and all those three are connected to their respective db paths, the code can be somewhat like this:
var DBNAME = name_of_the_db;
string connectionString;
switch (DBNAME)
{
case name_of_first_DB:
connectionString = "mongodb://[user:pass#]hostname[:port1][/[DBNAME][?options]]";
break;
case name_of_second_DB:
connectionString = "mongodb://[user:pass#]hostname[:port2][/[DBNAME][?options]]";
break;
case name_of_third_DB:
connectionString = "mongodb://[user:pass#]hostname[:port3][/[DBNAME][?options]]";
break;
default:
Console.WriteLine("Invalid DB Name");
}
Answering the updated part of the question:
You can start mongod's on different partitions of the server. Even start the daemons from different drives altogether and make them listen to different ports. Goes without saying that the dbpaths should not be pointing to the same drive for any two databases to at least pretty closely mimic what you wanted.
Just to complete this answer I am adding what #Schaliasos has mentioned in comments.. Consider installing mongo as a window service.
I am developing an app that should be portable and I am using mongodb.
By portable I means that my app has a folder with all: dlls, exes, mongo files, mongo databases. Then with this folder I can run my app in any machine.
Then I need to know:
Is there some library that allow me to run the mongod process when the app start and end
the process when the app ends ?
Exists a good practice to do that stuff ?
Advices are welcome and thanks in advance.
According to the MongoDb installation instructions it should be quite simple.
Mongodb starts as a console application waiting for connections, so when your app starts, you should run mongodb hidden. We are always assuming that ALL the mongodb files are in place with your application files and database files are in the correct directory).
When your app terminates, you should kill the process.
Yo should set the correct paths on this example:
//starting the mongod server (when app starts)
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = dir + #"\mongod.exe";
start.WindowStyle = ProcessWindowStyle.Hidden;
start.Arguments = "--dbpath d:\test\mongodb\data";
Process mongod = Process.Start(start);
//stopping the mongod server (when app is closing)
mongod.Kill();
You can see more information about the mongod configuration and running here
I needed to do the same thing and my starting point was Salvador Sarpi's answer. But, I found a couple things that needed to be added to his example.
First, you need to set UseShellExecute to false for the ProcessStartInfo object. Otherwise, you may get a security warning when the process is started asking the user if they want to run it or not. I don't think this is desired.
Second, you need to call Shutdown on the MongoServer object before killing the process. I had an issue where it locked the database and required it to be repaired if I didn't call the Shutdown method before killing the process. See Here for details on repairing
My final code is different, but for this example I used Salvador's code as the base for reference.
//starting the mongod server (when app starts)
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = dir + #"\mongod.exe";
start.WindowStyle = ProcessWindowStyle.Hidden;
// set UseShellExecute to false
start.UseShellExecute = false;
//#"" prevents need for backslashes
start.Arguments = #"--dbpath d:\test\mongodb\data";
Process mongod = Process.Start(start);
// Mongo CSharp Driver Code (see Mongo docs)
MongoClient client = new MongoClient();
MongoServer server = client.GetServer();
MongoDatabase database = server.GetDatabase("Database_Name_Here");
// Doing awesome stuff here ...
// Shutdown Server when done.
server.Shutdown();
//stopping the mongod server (when app is closing)
mongod.Kill();
I'm using plink from c# to connect to Linux servers and run some programs. Both the c# console program and the plink.exe are on the same windows machine.
The problem is when I connect to a Linux server for the first time, plink asks me if I want to accept and store the SSH key from the Linux server. I always want to respond yes to this because all the servers are in my LAN and there is no security issue.
I'm using c# Process type, pass the correct argument to plink, redirect the output and start. Now the problem is when plink prompts, the process.StandardOutput.ReadToEnd(); hangs and I have no way of figuring out whether I'm prompted by plink to accept the key or actually logged into the Linux server.
string output = string.Empty;
string error = string.Empty;
string arguments = #" -ssh -pw password root#12.12.12.12 ./Install_xxx.bin";
using (Process process = new Process())
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "plink";
psi.Arguments = arguments;
psi.ErrorDialog = false;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;
process.StartInfo = psi;
process.Start();
output = process.StandardOutput.ReadToEnd();
error = process.StandardError.ReadToEnd();
}
Thanks
Your Deadlock problem is explained in the Process docu. When the process waits for input it cannot close stdin so it will block. StandardOutput will not be closed because the process is still waiting for your input from stdin: Deadlock.
You can either use the asynchronous apis to read line by line or you use for the other stream another thread.
Instead of using an external executable to make your SSH connection, why not use a SSH library and do it (more reliably) through code?
sharpSsh (most recent version here) is a C# port of Jsch, a BSD-licensed Java library. I've used it in my C# projects very successfully; you'll be able to programmatically handle all aspects of the SSH connection, including the key negotiation.
In order to seamlessly connect to a remote server via PLINK, the first time and on subsequent times, you have to manually coordinate your SSH keys. That is, use PUTTYGEN to create your .ppk private key file that PLINK will use, and store the corresponding public key in the ~/.ssh/authorized_keys file on the server beforehand.
You also need to coordinate the sshd's host keys, so use PUTTYGEN to generate a set of private key files ssh_host_rsa_key and ssh_host_dsa_key under /etc/ssh (look at the existing files to figure out which key formats to export to), and then run ssh-keygen -y -f /etc/ssh/ssh_host_rsa_key > /etc/ssh/ssh_host_rsa_key.pub to create the corresponding public key files (do the same to create the ssh_host_dsa_key.pub file).
If you have more than one server you want to PLINK to, and don't mind using the same key to connect to all of them (this will depend on your local IT security policy), then just copy the same key files onto each server.
THEN, on your Windows machine that you're using PLINK, manually connect to any server using PUTTY or PLINK, and interactively accept the host key. Once you do that, PUTTY/PLINK stores that key in the Windows registry, under HKCU\Software\SimonTatham\PuTTY\SshHostKeys. You'll notice a entry like rsa2#22:host, where host is your server's host name.
Now, for the crux of the procedure, you need to copy the REG_SZ hex value 0x23,... into your C# app, where you'll have to manually use the Microsoft.Win32.RegistryKey classes to write this value for all other servers using the same rsa2#22:host naming scheme before you shell out to PLINK (whether you consider this registry value to be security-sensitve is up to you, so protect it somehow in your C# app if you have to).
Like others have mentioned, you may want to consider using an SSH library to get more programmatic feedback on your connection process, but to use PLINK, this is what you need to do.