I am trying to create a tunnel using ssh2_tunnel() to a remote AWS database, after being connected in ssh to the bastion host, via php, that has access to this remote MySql db.
Here is my actual code :
<?php
$ssh = ssh2_connect('domainofbastionhost', 22);
if (ssh2_auth_password($ssh,'myuserofthebastion','mypassofthebastion'){
$tunnel = ssh2_tunnel($ssh,'xxxx.xxx.xxx.rds.amazonaws.com',3306);
$stream_set_blocking($tunnel,true);
//Everything works fine up to here
$db = new PDO('mysql:host=xxxx.xxx.xxx.rds.amazonaws.com;port=3306;dbname=myawsdbname','myusertoaccessmyawsdb','mypasstoaccessmyawsdb');
}
?>
Due to this $db line, I get on my localhost page "Uncaught PDOException: SQLSTATE[HY000] [2002] Connection timed out ....".
I have tried to connect to the db from the bastion host (by ssh) through my shell, everything works fine, I am greeted by the "Trying 'IPOFTHEAWSDB'... Connected to 'canonical domain name of the db'. Escape character is '^]'. ] 5.5.5-xx.x.xx-MariaDB-xxxxxxmysql_native_password", which means everything works fine. I even tried to connect directly to the aws db, and I get disconnected, which means that my bastion host is clearly whitelisted to access the db.
I think the problem is that when I want to create my new PDO object, I am not whitelisted to access the resource, which means I have to use that $tunnel variable somewhere. I have seen a few examples online of some users using, instead of my $db line :
$db = new PDO('mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=myawsdbname,'myusertoaccessmyawsdb','mypasstoaccessmyawsdb');
But this is not working, as I don't have access to this mysql.sock resource as it should be on the bastion host. It's using my own mysqld.sock from my local machine. But where is the $tunnel stream really stored ? I am not sure.
I am quite confused as if it is possible to do so, and if so, how.
If you know anything or find anything, please tell me, it would mean a lot.
ssh2_tunnel() returns a raw socket resource, which is not going to be usable by any MySQL client libraries. It does not create a tunnel like the ssh CLI binary does. Also Unix sockets and TCP are very different beasts, so I would suggest not trying to follow that thread any further.
I would suggest not attempting to establish the tunnel in the context of a PHP script at all, as every request will open another connection and tunnel, potentially creating a lot of overhead on the bastion's SSH server.
To create an SSH tunnel on the command line:
ssh -N -L 3306:your.rds.instance:3306 your_user#bastion.host
Now you should be able to connect via 127.0.0.1:3306 in your PHP script.
To close the connection/turn off the tunnel issue ssh -O exit your_user#bastion.host.
However, I would not suggest using SSH tunnels for anything intended to be unattended. This is because I have found SSH tunnels to be finnicky, and they do not re-establish themselves after an interruption unless you're using a wrapper script, which is another layer of kludge. So I would suggest this approach for local dev/remote access only.
For a persistent connection that you might want to use to connect services/websites over, I would suggest some form of VPN depending on your particular requirements. I believe you can also apply security groups to your RDS instances to simply whitelist connections from certain public IP addresses/networks, but use with caution.
I have a .php file which should receive and show data from a remote database. I run my program from PHPStorm (which is connected to the remote database through the "Database" right-hand pane) and a browser. Both ways I get an error which depends on the number of arguments I pass to pg_connect() function.
If I use
$dbconn = pg_connect("host=pg hostaddr=server.address.ru port=5432 dbname=studs user=... password=...")
than the error is
Unable to connect to PostgreSQL server: could not parse network address "server.address.ru": Unknown host in...
But I am sure that I wrote the address correctly (there are no typos in it). This way I am not sure about the correctness of the format of the passed arguments.
If I use
$dbconn = pg_connect("host=server.address.ru dbname=studs user=... password=...")
command, the error is
pg_connect(): Unable to connect to PostgreSQL server: could not connect to server: Connection timed out
I found a lot of information about this errors, but it mostly refers to localhosts and doesn't solve my problem. I guess, the problem can be in the way this connection is set in the function, but I do not know why it doesn't work properly. How can I solve it?
Thanks to #TangentiallyPerpendicular, I got on a right way of setting the connection. But since I have PostgreSQL remote connection, it wasn't just up to this answer.
What I did and how I set the connection (I work from PHPStorm so all the actions are based on this platform):
Open PuTTY and set an SSH connection (an SSH tunnel) between the server's DB port (for PostgreSQL it's usually 5432) and your local computer's PostgreSQL port (5432 most often too). You can do the same from a command line.
Open PHPStorm and in "Database" section (an icon on the right-hand side of the environment or "Data Sources and Drivers" in Settings) set general information ("General" section) and set another SSH tunnel ("SSH/SSL"). In SSH Configurations (the same "SSH/SSL" section) set a local port - it will be important in our connection! Let's say, this port is 20000. The port of the server you're connecting to is a usual one (usually 22 or 2222).
In the program the right use of function is $dbconn = pg_connect("host=localhost port=20000 dbname=studs user=... password=...") or die('Error: ' . pg_last_error());
The connection is set.
For those who has troubles setting an SSH tunnel with a remote PostgreSQL from PHP this can be useful too.
I have an application in which I have to access all pc connected to same network and their MySql Databases and I also want want to connect to remotely a server.
Actually I have list of drop down services and each service holding a database name. when I select a service then I want to build connection to database either it lies on same network or any remote server.
Remember, I know the hostname, username, password and dbname. and I am using mysqli_connect function.
I have try multiple options given on web, but all in vain. e.g grant host and user access. But not found any solution can help to solve my problem.
I have try bind-address option in my.conf file but no solution.
Here is my code
For remote Server
$con = new mysqli_connect('xxx xxx xxx:3306', 'username', 'pass', 'dbname');
For local Network
$con = new mysqli_connect('xxxx xxx xxx:3306', 'localhost', '', 'talent');
Error
mysqli::mysqli();(HY000/2002): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. and also get this one earlier Warning: mysqli_connect(): (HY000/1045): Access denied for user 'username'#'localhost' (using password: YES).
I am using windows platform. WIndow 8.1
I guess you want to access mysql database installed on different computers on same network as your computer and also on some remote server.
What you need is IP Addresses of all the computers on which the mysql server is installed including the remote server. And on each such computer mysql server remote access should be enabled for your computer's IP Address and mysql user you are using in 'new mysqli_connect()'. See this tutorial.
Also as fvu said check php documentation for 'mysqli_connect' function.
UPDATED
What is SSH?
1. SSH is SecureSHell.
2. Its nothing but a remote login tool or program(like telnet but in secured way).
3. Remote login is logging in to some other computer(known as remote computer) as a user of that computer from your computer.
4. After you successfully logged in to the remote computer via ssh, you can type commands on that remote computer on behalf of the user you are loggen in with.
5. Consider this as if you are sitting in front of your computer and watching the command prompt(terminal in linux and mac) screen of the remote computer.
6. Whataver you will type here will reflect there.
For your knowledge to make you understand the problem:
1. When you install XAMPP or WAMP on your computer it also installs MySQL Server with it.
2. MySQL Server is a process running in the background to which we can request to do
database operations like SELECT, UPDATE, etc.
3. This thing we generally do using 'mysql_connect' or 'mysqli_connect' in php.
4. MySQL Server can have many users and different users have different previledges/permissions. So that MySQL admin user(i.e. root) can control what things are allowed and not allowed for a user.
5. Now while connecting to the mysql server process we need to specify the user credentials. That you specify in mysqli_connect function.
Now let me explain you why you are getting that error:
1. The user credentials you are using either does not exist or not correct or the user has no access to connect to MySQL Server process remotely. i.e. from other computer that the one has MySQL Server installed on. In your case from your computer to computer A or to computer B or remote server.
2. The other reason may be the firewall settings of remote computers.
Solution:
1. For computer A and computer B you don't need ssh you can directly go to the computer and open command prompt on it and type commands.
2. But for remote computer you need to use ssh.
3. Now another problem is you are using Windows so ssh program will not be available to you.
4. You will need to download putty program. Its ssh implementation for windows. Same as ssh only name is different.
5. Take a putty tutorial to connect to remote server.
6. Then you can use this tutorial for granting remote login access to mysql user you are using in mysqli_connect function.
If the server is a local hosted database and you can connect to it from local host and i you are not able to connect to it from a remote machine consider the following options.
There is a firewall in ubuntu that you need to open, you do this by granting access in IPtables.
/sbin/iptables -A INPUT -i eth0 -p tcp --destination-port 3306 -j ACCEPT And now we should be able to login to our server from our local machine:
mysql -h255.112.324.12 -uroot -pMyPASSWORD
You need
to grant access to mysql: https://askubuntu.com/questions/159053/mysql-server-not-accessible-from-remote-machine
as root, open your /etc/mysql/my.cnf with your favorite editor look for the [mysqld] section, and in there for the bind-address keyword. This usually is set to 127.0.0.1 -- change that to match your "normal" IP-address save the file, and reload the service (e.g. using service mysql restart)
Last but not least you need to give remote access in mysql:
GRANT ALL ON mydb.* TO root#'%' IDENTIFIED BY 'MyPASSWORD';
Find more information here: http://web.archive.org/web/20120930214828/http://chosencollective.com/technology/how-to-enable-remote-access-to-mysql
Right now you seem to be mixing object-oriented and procedural approaches. If you want to use object-orented approach, mysqli object should be instantiated as follows:
$con = new mysqli($host, $username, $password, $dbname, $socket); // all but first parameter are optional
If you want to use procedural approach, mysqli connection resource should be created as follows:
$con = mysqli_connect($host, $username, $password, $dbname, $socket); // all but first parameter are optional
I have allowed remote connections to mysql. I can successully connect from the console of another machine.
I have some php files on another server but I cant connect to the same mysql db from php.
This is the way I'm doing it:
mysql_pconnect("theipaddress","username","password")
or die("Unable to connect to db server");
Now bear in mind I am using the same creds which I successfully used in the console. I even tried putting the port after the ip but no joy.
Any ideas?
You must have a user in MySQL who is allowed to connect from % (any host) (see manual for details).
Have you selinux installed? Maybe selinux is not allowing apache to make remote connections? If yes, type:
setsebool -P httpd_can_network_connect=1
I have my database on remote Linux machine, and I want to connect using SSH and PHP functions (I am currently using ssh2 library for that). I tried using mysql_connect, but it gives me can't access (although I have granted permission)
when I tried using this function:
$connection = ssh2_connect('SERVER IP', 22);
ssh2_auth_password($connection, 'username', 'password');
$tunnel = ssh2_tunnel($connection, 'DESTINATION IP', 3307);
$db = mysqli_connect('127.0.0.1', 'DB_USERNAME', 'DB_PASSWORD',
'dbname', 3307, $tunnel)
or die ('Fail: '.mysql_error());
I got error "mysqli_connect() expects parameter 6 to be string, resource given". How can I resolve this?
SSH Tunnel Solution
Set up an SSH tunnel to your MySQL database server (through a Jumpbox proxy for security).
(A) GUI Tools
Depending on your requirements, you can use a GUI MySQL client with SSH Tunnelling support built-in such as Visual Studio Code Forwarding a port / creating SSH tunnel, TablePlus or use PuTTY to setup local port forwarding.
On macOS, I like Secure Pipes or TablePlus.
(B) Command Line
Step 1.
ssh -fNg -L 3307:10.3.1.55:3306 username#ssh-jumpbox.com
The key here is the '-L' switch which tells ssh we're requesting local port forwarding.
I've chosen to use port 3307 above. All traffic on my local machine directed to this port will now be 'port-forwarded' via my ssh client to the ssh server running on the host at address ssh-jumpbox.com.
The Jumpbox ssh proxy server will decrypt the traffic and establish a network connection to your MySQL database server on your behalf, 10.3.1.55:3306, in this case. The MySQL database server sees the connection coming in from your Jumpbox' internal network address.
Local Port Forwarding Syntax
The syntax is a little tricky but can be seen as:
<local_workstation_port>:<database_server_addr_remote_end_of_tunnel>:<database_server_port_remote_end> username#ssh_proxy_host.com
If you're interested in the other switches, they are:
-f (go to background)
-N (do not execute a remote command)
-g (allow remote hosts to connect to local forwarded ports)
Private Key Authentication, add (-i) switch to above:
-i /path/to/private-key
Step 2.
Tell your local MySQL client to connect through your SSH tunnel via the local port 3307 on your machine (-h 127.0.0.1) which now forwards all traffic sent to it through the SSH tunnel you established in step 1.
mysql -h 127.0.0.1 -P 3307 -u dbuser -p passphrase
Data exchange between client and server is now sent over the encrypted SSH connection and is secure.
Security note
Don’t tunnel directly to your database server. Having a database server directly accessible from the internet is a huge security liability. Make the tunnel target address the internet address of your Jumpbox/Bastion Host (see example in step 1) and your database target the internal IP address of your database server on the remote network. SSH will do the rest.
Step 3.
Now connect up your PHP application with:
<?php
$smysql = mysql_connect( "127.0.0.1:3307", "dbuser", "passphrase" );
mysql_select_db( "db", $smysql );
?>
Credit to Chris Snyder's great article detailing ssh command line tunnelling for MySQL connectivity.
Unfortunately, the ssh2 tunnel offered by php doesn't seem able to handle a remote mysql connection as you cannot specify the local port to tunnel (it only works with port 22 or whatever ssh port your remote server is running on). My solution to this is to just open the tunnel via exec() operator and connect as usual from there:
exec('ssh -f -L 3307:127.0.0.1:3306 user#example.com sleep 10 > /dev/null');
$mysqli = new mysqli('127.0.0.1', 'user', 'password', 'database', '3307');
I was looking for the same thing, but I prefer not to need external commands and manage external processes. So at some point I thought, how hard can it be to write a pure PHP MySQL client which can operate on any PHP stream? It took me about half a day, based on the MySQL protocol documentation.
https://gist.github.com/UCIS/4e509915ed221660e58f5169267da004
You can use this with the SSH2 library or any other stream:
$ssh = ssh2_connect('ssh.host.com');
ssh2_auth_password($ssh, 'username', 'password');
$stream = ssh2_tunnel($ssh, 'localhost', 3306);
$link = new MysqlStreamDriver($stream, 'SQLusername', 'SQLpassword', 'database');
$link->query('SELECT * FROM ...')->fetch_assoc();
It does not implement the complete mysqli API, but it should work with all plain-text queries. Please be careful if you decide to use this, I haven't thoroughly tested the code yet and the string escaping code has not been reviewed.
According to the docs, that last parameter is supposed to be a socket or pipe name, something like '/var/run/mysql/mysql.sock'. Since you're not connecting using a UNIX socket, that doesn't apply to you... so try just leaving it out.
I believe that the reason I (and I suppose most people) have a problem getting this to work is because the user in the mysql server is set to only allow from "localhost" and not 127.0.0.1, the IP address of localhost.
I got this to work by doing the following steps:
Step 1: Allow 127.0.0.1 host for target user
SSH normally into your server, and log in with the mysql root user, then issue the command:
GRANT ALL ON yourdbname.* TO yourdbuser#127.0.0.1 IDENTIFIED BY 'yourdbpassword';
The key of course, is specifying 127.0.0.1 above.
Step 2: Start local SSH tunnel to MySQL
You can now start your local SSH tunnel to the remote MySQL server, like so:
ssh -vNg -L 33306:127.0.0.1:3306 sshuser#remotehost.com
-v makes ssh operate in verbose mode, which kind of helps to see what's happening. For example, you'll see debugging output like this in your terminal console when you attempt a connection:
debug1: client_input_global_request: rtype hostkeys-00#openssh.com want_reply 0
debug1: Connection to port 33306 forwarding to 127.0.0.1 port 3306 requested.
and output like this when you close the connection:
debug2: channel 2: is dead
debug2: channel 2: garbage collecting
debug1: channel 2: free: direct-tcpip: listening port 33306 for 127.0.0.1 port 3306, connect from 127.0.0.1 port 52112 to 127.0.0.1 port 33306, nchannels 3
-N makes ssh issue no commands, and just wait instead after establishing connection.
-g allows remote hosts to connect to local forwarded ports. Not completely sure if this is necessary but it might be useful for multiplexing multiple connections through the same SSH tunnel.
-L This is the main parameter that specifies the local port 33306 to connect to the remote host's local IP address 127.0.0.1 and the remote host's mysql port, usually 3306.
You can use whatever mechanisms / other parameters needed after this to connect through SSH to your remote host. In my case, I use key files configured in my ~/.ssh/config so I just need to specify user#host to get in.
Issuing the command like this runs SSH in the foreground, so I can easily close it with Ctrl + C. If you want to run this tunnel in a background process you can add -f to do this.
Step 3: Connect from PHP / other mysql compatible methods
The SSH tunnel running from above on your localhost will behave exactly like as if your mysql was running on 127.0.0.1. I use port 33306 (note the triple 3) which lets me run my local sql server at its normal port. You can now connect as you would normally do. The mysql command on the terminal looks like this:
mysql -h 127.0.0.1 -P 33306 -u yourmysqluser -p
where -P (capital P) specifies the port where your SSH tunnel's local end is accepting connections. It's important to use the 127.0.0.1 IP address instead of localhost because the mysql cli will try to possibly use the linux socket to connect.
For PHP connection strings, my data source name string (for my Yii2 config) looks like this:
'dsn' => 'mysql:host=127.0.0.1;dbname=yourdbname;port=33306',
Passwords, and usernames are specified as normal.
even i tried it by doing ssh both by root credentials and and public private key pair, but it allows me to conect through command line but not through php code. I tried by creating tunnel also(by using ssh2 functions),ans running shell commands from php code(system,exec etc), nothing worked. Finally i tried ssh2 function to execute shell command and it finally worked :) Here is code, if it helps you:----
$connection = ssh2_connect($remotehost, '22');
if (ssh2_auth_password($connection, $user,$pass)) {
echo "Authentication Successful!\n";
} else {
die('Authentication Failed...');
}
$stream=ssh2_exec($connection,'echo "select * from zingaya.users where id=\"1606\";" | mysql');
stream_set_blocking($stream, true);
while($line = fgets($stream)) {
flush();
echo $line."\n";
}
it worked for me try this if want to use php functions specifically.
Make sure that your username and password that you are connecting with has the right hostname permissions. I believe you can use '%' for a wildcard. Also if you are connecting to remote machine (which I would assume you are if you are trying to ssh into it) that is not on your local network you will have to forward the ports on your router where the server is for outside traffic to be able to connect to it.
http://www.lanexa.net/2011/08/create-a-mysql-database-username-password-and-permissions-from-the-command-line/
When using ssh2 to connect, since there is no special function for php to use ssh2 to connect to mysql, we can only use the traditional socket and mysql protocol for data interaction.
When we use the ssh2_tunnel method, if the creation is successful, a socket object will be returned, then we can use this socket object for mysql data interaction. Of course, we need to understand the mysql protocol, which is the so-called "handshake packet"
......
$tunnel = ssh2_tunnel(....)
......
//we need to construct the mysql data packet and then use fwrite to transfer this packet to mysql host
fwrite($tunnel, MYSQLDATAPACKET)
when we use ssh2_connect method to connect the mysql host, it will return a MySQL handshake packet, it looks like this:
a 5.5.5-10.3.34-MariaDB-cll-lve�WfyP`uKW����RtscuF:/}J7umysql_native_password!��
here is the mysql handshake packet structure:
size(byte) description
1 protocol version
n server version
4 connection id
8 auth-plugin-data-part-1
1 filler
2 capability flags
1 character set
2 status flags
2 capability flags
1 length of auth-plugin-data
10 reserved
13 auth-plugin-data-part-2
n auth-plugin name
so we should parse the byte array by using such structure.
After receving the Mysql handshake, which means we have connected the mysql host, and now we need to login mysql, so we need to construct the mysql send data packet
here is the data structure: (HandshakeResponse41)
4 capability flags, CLIENT_PROTOCOL_41 always set
4 max-packet size
1 character set
string[23] reserved (all [0])
string[NUL] username
if capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA {
lenenc-int length of auth-response
string[n] auth-response
} else if capabilities & CLIENT_SECURE_CONNECTION {
1 length of auth-response
string[n] auth-response
} else {
string[NUL] auth-response
}
if capabilities & CLIENT_CONNECT_WITH_DB {
string[NUL] database
}
if capabilities & CLIENT_PLUGIN_AUTH {
string[NUL] auth plugin name
}
if capabilities & CLIENT_CONNECT_ATTRS {
lenenc-int length of all key-values
lenenc-str key
lenenc-str value
if-more data in 'length of all key-values', more keys and value pairs
}
See Also: https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse41
Okay, so we have connected the mysql host successfully, and then you can query the database.
Here is the sample code for the Mysql Handshake and Mysql Socket Login:
function parseMysqlHandshakePack($hex_string)
{
$dataField = {
"protocol_version" => "",
"server_version" => "",
"thread_id" => "",
"salt1" => "",
"salt2" => "",
"salt" => "",
}
$dataField["protocol_version"] = UtiliHelper::HexToInt(UtiliHelper::HexSub($hex_string,0,1));
$dataField["server_version"] = UtiliHelper::HexToStr(UtiliHelper::HexSub($hex_string,1,7));
$dataField["thread_id"] = UtiliHelper::HexToInt(UtiliHelper::HexSub($hex_string,8,4));
$dataField["salt1"] = UtiliHelper::HexSub($hex_string,12,8);
$dataField["salt2"] = UtiliHelper::HexSub($hex_string,39,12);
$dataField["salt"] = $dataField["salt1"] . $dataField["salt2"];
return $dataField;
}
function constructMysqlLoginPacket($username, $password, $database, $salt){
$tags = [
"power_tag" => "",
"power_ext" => "",
"max_length" => "",
"charset" => "",
"fill_pad" => "",
"username" => "",
"password" => "",
"database" => "",
"client_auth_plugin" => "",
"payload" => ""
];
$tags['power_tag'] = "8da2";
$tags['power_ext'] = "0b00";
$tags['max_length'] = "000000c0";
$tags['charset'] = "08";
$tags['fill_pad'] = "0000000000000000000000000000000000000000000000";
$tags['client_auth_plugin'] = "6d7973716c5f6e61746976655f70617373776f726400";
$tags["payload"] = "150c5f636c69656e745f6e616d65076d7973716c6e64";
$tags['username'] = UtiliHelper::StrToHex($username)."0014";
$tags['password'] = UtiliHelper::encryptionPass($password,$salt);
$tags['database'] = UtiliHelper::StrToHex($database)."00";
$message = "";
foreach ($tags as $tagv){
$message .= $tagv;
}
return UtiliHelper::IntToHex(strlen($message)/2)."01".$message;
}
the $salt parameter came from the Handshake phase, so you need to parse the mysql handshake message and get the salt.
The UtiliHelper is a custom class which is from a 3rd party party project in Github: https://github.com/gphper/PHPMysql/blob/master/src/UtiliHelper.php