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:
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.
Configure a Fail2Ban service that watches SSH authentication failures and blocks brute-force attempts in cooperation with the network firewall configured.
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
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.
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...
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`
- Instruct employees that any MySQL client will need to connect to
localhost:13306
.