Access AWS EC2 MySQL instance remotely - Best practice

We have a small business with less than 10 employees. We have a MySQL database with sensitive information that is hosted on an AWS EC2 instance. The employees need to have access to the DB. Currently, I have exposed the DB to the internet and each employee has his own username/password.

What is the safest way and best practice to allow access to the DB from the internet?

Here are the options I thought of so far:

1- Keep the username/password method
2- Disable remote access to the DB and force users to create an SSH tunnel (with PuTTY for example)
3- Disable remote access to the DB and setup a VPN to which users will connect to

Thank you


Solution 1:

What is the safest way and best practice to allow access to the DB from the internet?

The best practice for a database containing sensitive information is denying access from the internet. :-)

Ok, let me stop kidding now. I sometimes fail to supply quick and proper answers to questions asking the safest way to achieve some result, because the more hardened a system is, the more difficult any interaction with it is. Information security is derived by risk analysis somehow. Will compensation amount and market loss caused by a hypothetical data leakage be more expensive than management cost associated to deployed security controls? If so, security controls may be improved or added. If not, that means management cost is too expensive and may be reduced by relaxing security controls. There is no unique or canonical answer for the question, because it is influenced by business reality.

I see the expression sensitive information. Because of that, I am strongly against direct exposure of the DB port ([::]:3306) to the internet. In such scenario, a single 0-day vulnerability in MySQL would be sufficient to have all data leaked. I would not discard the username/password method at all (option #1), but I suggest an indirect exposure instead, by using either a SSH tunnel (option #2) or a VPN tunnel (option #3) as an additional security layer. I would use SSH because it adds less complexity than a VPN and, also, because recent OpenSSH versions allows ethernet (L2) and point-to-point (L3) tunnels in addition to TCP (L4) ones if necessary. I suggest the steps below:

  1. Set up a network firewall, such as IPTABLES or NFTABLES, which allows incoming connections to the SSH port ([::]:22) only. If the application that consumes the database runs on a different host, allow connections to the DB port ([::]:3306) that comes from such host only.

  2. Configure a Fail2Ban service that watches SSH authentication failures and blocks brute-force attempts in cooperation with the network firewall configured.

  3. In OpenSSH configuration file, allow keyfile-based authentication only:

# /etc/ssh/sshd_config
GSSAPIAuthentication no
HostbasedAuthentication no
KbdInteractiveAuthentication no
PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
  1. Request each employee to generate a SSH key pair and send you their public key. Advise them to set key passphrases. They can use PuTTYgen for that.

  2. Create a single local user to be used as a bridge to MySQL database and store into associated ${HOME}/.ssh/authorized_keys file all employees' public keys. Restrict SSH capabilities of those keys to the minimum required for a tunneled connection to the MySQL server:

# ${HOME}/.ssh/authorized_keys
restrict,command="/bin/false",port-forwarding,permitopen="localhost:3306",permitopen="127.0.0.1:3306",permitopen="[::1]:3306" ssh-rsa AAAA...
restrict,command="/bin/false",port-forwarding,permitopen="localhost:3306",permitopen="127.0.0.1:3306",permitopen="[::1]:3306" ssh-rsa AAAB...
restrict,command="/bin/false",port-forwarding,permitopen="localhost:3306",permitopen="127.0.0.1:3306",permitopen="[::1]:3306" ssh-rsa AAAC...
  1. Instruct employees how to establish the SSH connection properly.

    For PuTTY, for example, the following settings have to be adjusted:

    • In Connection => Data => Login details, the bridge user's name should be written in the Auto-login username field.
    • In Connection => SSH => Protocol options, the box Don't start a shell or command at all must be checked.
    • In Connection => SSH => Auth => Authentication parameters, a proper private key file must be supplied.
    • In Connection => SSH => Tunnels => Port forwarding, a local forwarded port entry L13306 localhost:3306 must exist. The source port 13306 can be changed freely by the employee, actually.

    Those changes are distributable through Windows Registry files. Check this Stack Overflow question for information.

    For OpenSSH client, all those changes are specified as command-line flags:

$ ssh -l bridgeuser -f -N -i /path/to/private/key -L 13306:localhost:3306 server.example.com
$ mysql --protocol=TCP -h localhost -P 13306`
  1. Instruct employees that any MySQL client will need to connect to localhost:13306.

Solution 2:

Some thoughts (maybe not the whole answer)

  • You should have a security group that limits the IP addresses you can log in from

  • The server should be hardened, disabling services it doesn't need. CIS (center for internet security) has some resources to help with this, such as pre-hardened images, but it's not a trivial task

  • The server including MySQL should be patched regularly

Best practice is probably to use the AWS RDS service, but that is more expensive than hosting your own on EC2. They do all the updates for you, which is an advantage.

Solution 3:

If by "Exposing MySQL to the internet" you mean exposing TCP/3306 to 0.0.0.0/0 I wouldn't even consider that an option.

1) Would work ok if you limited access to the users to TCP/3306 to their IPs (or a shared office IP). [in AWS you'd do this w/ security groups]

2) Would be better, but again, I would not expose TCP/22 to the world and instead limit access to specific IPs.

3) Probably ideal.