※この記事にはアフィリエイト広告(プロモーション)が含まれます。
MySQL を入れた直後は、同じサーバーの中からしか接続できないことが多いです。別のサーバーから mysql -h で接続するには、待ち受けるアドレス(bind-address)と、接続元のホストを含むユーザーの2つが要ります。
この記事のログは MySQL 8.4.11(Ubuntu 26.04 の標準パッケージ)で採りました。サーバーの IP は 192.168.0.11、ホスト名は dbhost1、ユーザーは app です。
まず見る(結論)
| エラー | 原因 | 直す場所 |
|---|---|---|
ERROR 2003 (HY000): Can't connect to MySQL server on '...:3306' (111) |
そのアドレスで待ち受けていない | bind-address(再起動が要る) |
ERROR 1130 (HY000): Host '...' is not allowed to connect |
接続元のホストに一致するユーザーが無い | CREATE USER 'app'@'接続元' |
ERROR 1045 (28000): Access denied for user ... |
ユーザーは一致した。パスワードが違う | そのホストのユーザーのパスワード |
ERROR 1044 (42000): Access denied ... to database |
接続はできた。データベースの権限が無い | GRANT ... ON db.* TO 'app'@'接続元' |
エラー番号ごとの意味(-p の書き方で変わる 1045、ソケットの 2002 など)は「mysql -u root -pの意味」でも扱っています。この記事は外部からの接続に絞ります。
ERROR 2003:bind-address
既定のままサーバーの IP に接続した結果です。
$ mysql -h 192.168.0.11 -u app -p
ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.0.11:3306' (111)
末尾の (111) は OS の「接続を拒否された」というエラーです。そのアドレスとポートでは誰も待ち受けていません。Ubuntu のパッケージでは bind-address = 127.0.0.1 が設定済みで、ss -ltn にも 127.0.0.1:3306 しか出ません。
設定の場所を探すには、まず MySQL が読むファイルの順番を確かめます。
$ mysqld --verbose --help 2>/dev/null | grep -A1 "Default options are read"
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
$ grep -rn bind-address /etc/mysql/
/etc/mysql/mysql.conf.d/mysqld.cnf:31:bind-address = 127.0.0.1
/etc/mysql/mysql.conf.d/mysqld.cnf:32:mysqlx-bind-address = 127.0.0.1
bind-address を 0.0.0.0(すべての IPv4 アドレス)に変えて再起動します。
# /etc/mysql/mysql.conf.d/mysqld.cnf
bind-address = 0.0.0.0
$ sudo systemctl restart mysql
$ mysql -e "SELECT @@bind_address"
0.0.0.0
mysqlx-bind-address は X プロトコル(33060番)用で、普通の mysql クライアントの接続には関係しません。
ERROR 1130:接続元に一致するユーザーが無い
待ち受けを変えた後に接続すると、エラーが変わります。
$ mysql -h 192.168.0.11 -u app -p
ERROR 1130 (HY000): Host 'dbhost1' is not allowed to connect to this MySQL server
ユーザー app は 'app'@'localhost' としてしか作っていません。MySQL のユーザーは「ユーザー名@ホスト」の組で1つのアカウントです。パスワードが正しくても、ホストが合わなければ一致しません。
メッセージの Host '...' には、接続元の IP を逆引きしたホスト名が出ることがあります(skip_name_resolve が 0 のとき)。この検証ではサーバー自身から自分の IP 宛てに接続したので、サーバーのホスト名 dbhost1 が出ました。ユーザーの host 部分には IP でもホスト名でも書けます。
接続元のネットワークを含むユーザーを作り、権限を付けます。
CREATE USER 'app'@'192.168.0.%' IDENTIFIED BY '********';
GRANT ALL ON appdb.* TO 'app'@'192.168.0.%';
% はワイルドカードで、192.168.0.% は 192.168.0.0/24 からの接続に一致します。'app'@'%' にすればどこからでも接続できますが、絞れるなら絞ります。
接続してから CURRENT_USER() を見ると、どのアカウントに一致したかが分かります。
SELECT CURRENT_USER(), USER();
current_user() user()
app@192.168.0.% app@dbhost1
USER() は接続してきた側です。ホスト名で表示された接続が、IP の範囲(192.168.0.%)で作ったユーザーに一致しています。
ホストが違えば、別のユーザー
CREATE USER の直後、GRANT を流す前に appdb を使おうとすると、接続はできても、データベースを使う段階で止まりました。
ERROR 1044 (42000) at line 1: Access denied for user 'app'@'192.168.0.%' to database 'appdb'
'app'@'localhost' に付けた権限は、'app'@'192.168.0.%' には付きません。パスワードも別です。'app'@'192.168.0.%' のパスワードだけを変えると、次のようになりました。
$ mysql -h 127.0.0.1 -u app -p # 古いパスワード → 'app'@'localhost' として入れる
$ mysql -h 192.168.0.11 -u app -p # 古いパスワード
ERROR 1045 (28000): Access denied for user 'app'@'dbhost1' (using password: YES)
「ローカルでは入れるのに外からだけ 1045 になる」ときは、ホストごとのユーザーを一覧で見ます。
SELECT user, host, plugin FROM mysql.user WHERE user = 'app';
root は外から入れない
Ubuntu のパッケージで入れた MySQL の root は 'root'@'localhost' だけで、認証方式は auth_socket です。
SELECT user, host, plugin FROM mysql.user WHERE user = 'root';
| root | localhost | auth_socket |
auth_socket は OS のユーザー名で認証するので、パスワードは使いません。sudo mysql で入ります。-h 127.0.0.1 を付けて TCP で接続すると、どんなパスワードでも ERROR 1698 (28000): Access denied for user 'root'@'localhost' になりました。外部の IP から接続すると、'root'@'dbhost1' に一致するアカウントが無いので 1045 になります。管理用には root を外に開けず、必要な権限だけを持つユーザーを別に作ります。
このログは、サーバー自身から自分の IP(192.168.0.11)宛てに接続して採りました。127.0.0.1 ではなく外向きの IP に向けた接続なので、bind-address とユーザーのホストの判定は別のホストから接続したときと同じです。ファイアウォールの設定は含んでいません。PostgreSQL の場合は「PostgreSQLに外部から接続できない」で扱っています。