[MDEV-21778] Disable system commands in mysql/mariadb client Created: 2020-02-19  Updated: 2022-12-01

Status: Open
Project: MariaDB Server
Component/s: Scripts & Clients
Fix Version/s: None

Type: Task Priority: Major
Reporter: Ian Gilfillan Assignee: Unassigned
Resolution: Unresolved Votes: 2
Labels: beginner-friendly


 Description   

There may be circumstances where it makes sense to disable the system command in the mysql client. See https://mariadb.com/kb/en/how-to-disable-system-command-on-mariadb-shell, https://stackoverflow.com/questions/40958372/how-to-make-the-system-command-unavailable-in-mysql and https://bugs.mysql.com/bug.php?id=26941 for a MySQL patch that was never implemented.



 Comments   
Comment by Aman Alam Bora [ 2020-02-23 ]

Hi I'm trying for GSOC 2020 . I would like to work on this issue to get familiar with the code base. I would require some guidance as I'm new in this org as well as Jira.

Comment by Ralf Gebhardt [ 2021-04-08 ]

To make the mariadb command line client "more secure" as requested, we do not only need to disable the system command, we also need to think about disabling for:

  • tee
  • load data local infile
  • source

Another option would be to add chroot in the client and to be able to define a root directory for the client.

Comment by Ralf Gebhardt [ 2022-01-21 ]

serg, to follow up on this. Do we really need the system command in the client, I am not sure if it is used that much. Do you have an opinion on that? chroot would of course also be a good option with the directory to be defined in the config file (and a good default).

Comment by Sergei Golubchik [ 2022-01-21 ]

I've used it myself for backups. Like

mysql <<EEE
flush tables with read lock;
\! copy -r /var/lib/mysql /path/to/backup
EEE

that was, admittedly, long time ago and doesn't work all too well with InnoDB. Still, I suppose people use it.

Comment by Valerii Kravchuk [ 2022-01-24 ]

System command is also useful while importing InnoDB tables, see http://mysqlentomologist.blogspot.com/2016/11/how-to-recover-corrupted-innodb.html for a typical way to use cp etc from inside mysql session.

Comment by Ralf Gebhardt [ 2022-01-24 ]

serg, so it looks like using chroot is a good approach here. If you agree, does it need more details before somebody could work on this?

Comment by Sergei Golubchik [ 2022-03-25 ]

anyone, it's a trivial patch. But it'll mean that mariadb client will need to be run as root. Is it acceptable?

Comment by Laurent Blume [ 2022-03-25 ]

Hello, I'd just would like to know, what does "need to be run as root" mean here? Enabling the setuid bit?

My original concern with system stems from this: when using unix_socket to authenticate MariaDB's root (something which is now the default setting in at least Ubuntu), then giving somebody root access to the database also means giving them root access to the system, which is not always desirable.

Laurent

Comment by Sergei Golubchik [ 2022-03-30 ]

I mean that only system root user can do chroot(). So for mariadb command client to do it, it must either be run by root or be setuid root.

But let's get to your original concern. system command can only run commands as the user who started mariadb command line client. If root has started it, then those commands will be run as root. If non-root starts it, they'll be run as that user.

When using unix_socket to authenticate MariaDB's root, then only system root will be able to connect as MariaDB root. But you can give anyone access to MariaDB root via a password, without giving system root privileges. Just login as root and issue

SET PASSWORD = "...whatever...";

and you'll be able to connect with a password if you aren't system root. Connecting as system root will still work without a password (good for scripts and for resetting the password when needed).

Comment by Laurent Blume [ 2022-03-30 ]

That's where I'm coming from
The idea for me is to get rid of passwords wherever possible for local access. Passwords are a pain, because due to rigorous compliance constraints, they imply a lot of administrative tasks, such as handling their secure storage and periodic rotation. As for scripts, storing an application user password on disk needs a business need and evidence of safety, storing a root password on disk is definitely forbidden.
It's much easier in my context to allow MariaDB root access via sudo. It's usually not a problem, but there is the possibility that somebody need root access to the DB, but not the system's root.
Compliance auditors are specifically focusing on applications that allow escaping to a shell, so I'm making sure they're all under control.

Comment by Sergei Golubchik [ 2022-03-31 ]

So, you want to have only system root access to mariadb root, and you configure /etc/sudoers so that other users will be able to run mysql client as root, but not any other commands?

Yes, in this case chroot approach will work and will do what you need.

A more database centric approach would be to create a role, say, "admin" and grant it to those users that you want to be root-like. Say,

CREATE ROLE admin;
GRANT ALL PRIVILEGES ON *.* TO admin;
GRANT admin TO tom, dick, harry;

After that, say, Tom will be able to login without a password as tom, and then he'll be able to set the admin role to do root stuff:

tom ~ $ mysql
MariaDB> SHOW BINARY LOGS;
ERROR ... permission denied to user `tom`@`localhost`
MariaDB> SET ROLE admin;
MariaDB> SHOW BINARY LOGS
+--------------------------+
...

That is, a user can login without a password as himself — just as you like. And then the user can assume the superuser role if needed, but can also work as non-superuser normally, so it doesn't mean that Tom becomes a superuser. Pretty much like you'd have with sudo and /etc/sudoers approach. But without a dangerous system root access.

Comment by Daniel Black [ 2022-03-31 ]

Less invasive than chroot that doesn't require elevated privs, set the process rlimit to 1

rlimit PROC

diff --git a/client/mysql.cc b/client/mysql.cc
index 15325aec69d..c161d8b0f8b 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -45,6 +45,7 @@
 #if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H)
 #include <locale.h>
 #endif
+#include <sys/resource.h>
 
 const char *VER= "15.1";
 
@@ -1121,6 +1122,11 @@ int main(int argc,char *argv[])
   DBUG_ENTER("main");
   DBUG_PROCESS(argv[0]);
   
+  struct rlimit rlimit;
+
+  rlimit.rlim_cur= rlimit.rlim_max= 1;
+  setrlimit(RLIMIT_NPROC, &rlimit);

effect of client

MariaDB [(none)]> \! ls
MariaDB [(none)]> \! ls
MariaDB [(none)]> \! echo ya

Sure some error handling is needed here.

strace

clone3({flags=CLONE_VM|CLONE_VFORK, exit_signal=SIGCHLD, stack=0x7fd064f46000, stack_size=0x9000}, 88) = -1 EAGAIN (Resource temporarily unavailable)
...

Comment by Daniel Black [ 2022-04-01 ]

Other non-code solutions:

  • Custom selinux/apparmor profile for the mariadb client
  • run under restricted shell (if the remaining privs are acceptable)
  • firejail
  • run mariadb client in container podman run -v /var/run/mysqld:/run/mysqld --rm mariadb:10.5 mariadb
Comment by Sergei Golubchik [ 2022-04-01 ]
  • nice trick with rlimit. Doesn't protect against \T overwriting system files. And against load data local reading them.
  • I don't see how restricted shell can help
  • custom selinux profile might work
  • haven't used firejail or podman, but likely will have the same issue as using chroot for a non-code solution (that is, running chroot /restricted mysql) — it needs a complete execution environment in /restricted with all needed libraries and config files. So, awkward to use.
Generated at Thu Feb 08 09:09:43 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.