paramikoを用いて、SSHClient.connectするときのエラーについて
背景
Discordから、ARKサーバーの起動・停止を行いたいです。
下記Qiitaページを参考に、作業していましたがエラーが発生しました。
DiscordからMinecraftサーバーを立ち上げた話
また、下記ページは実施済みで、正常にssh接続できました。
EC2インスタンス間でSSHパスワードなしでログインする方法
自分なりに調べ尽くしましたが、肝心のpythonやssh接続の知識が不足しているため、ご協力をお願いしたいです。
環境
- ARKサーバー:AWS ec2 t2.large
- Discord bot:AWS ec2 t2.micro
- python 3.7.8
- paramiko 2.7.1
ソースコード
Discord botのソースコードは下記になります。
import discord
import subprocess
import paramiko
import time
# 自分のBotのアクセストークンに置き換えてください
TOKEN = 'GbQ8(以下略)'
# game-serverのインスタンスidを指定してください
INSTANCEID = 'i-5c(以下略)'
# 接続に必要なオブジェクトを生成
client = discord.Client()
# ***************************
# *** 処理関数
# ***************************
class DiscordBOT:
def __init__(self, client):
self.SSHClient = None
async def main(self, discord_event):
get_text = discord_event.content
print('init完了')
if "$start ark" in get_text:
print('start ark 受け付けました')
# インスタンスの起動
subprocess.call("aws ec2 start-instances --instance-ids {}".format(INSTANCEID), shell=True)
time.sleep(3)
print('インスタンス起動処理終了')
# インスタンスが起動するまで待機
subprocess.call("aws ec2 wait instance-status-ok --instance-ids {}".format(INSTANCEID), shell=True)
time.sleep(3)
print('インスタンス起動待機終了')
# インスタンスのIPアドレスを取得
proc = subprocess.run(["aws ec2 describe-instances --instance-ids {} --query 'Reservations[*].Instances[*].PublicIpAddress' --output text".format(INSTANCEID)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
ip_add = proc.stdout.decode("utf-8")
ip_add = ip_add.replace(".", "-").replace("\n", "")
self.server_ip = ip_add
time.sleep(3)
print('インスタンスのIPアドレス取得終了')
print(ip_add)
print(self)
# SSH接続クライアント作成
self.SSHClient = paramiko.SSHClient()
self.SSHClient.set_missing_host_key_policy(paramiko.WarningPolicy())
self.SSHClient.connect('ec2-{}.compute-1.amazonaws.com'.format(ip_add), username='root', password='')
time.sleep(2)
print('SSH接続クライアント作成終了')
# SSHでarkサーバー起動
print('********実行su-steam')
stdin, stdout, stderr = self.SSHClient.exec_command("su - steam")
for x in stdout:
print(x)
for x in stderr:
print(x)
time.sleep(1)
print('********実行パスワード入力')
stdin, stdout, stderr = self.SSHClient.exec_command("password")
for x in stdout:
print(x)
for x in stderr:
print(x)
time.sleep(1)
print('********実行arkmanagerstart')
stdin, stdout, stderr = self.SSHClient.exec_command("arkmanager start")
for x in stdout:
print(x)
for x in stderr:
print(x)
time.sleep(2)
# 接続用のIPアドレスをdiscordに送信
send_text = "インスタンスの起動とARKサーバーへの接続に成功しました。\n サーバー起動までお待ちください。".format(ip_add.replace("-", "."))
elif "$stop ark" in get_text:
# サーバーの停止
self.SSHClient.connect('ec2-{}.compute-1.amazonaws.com'.format(ip_add), username='root', password='')
time.sleep(2)
stdin, stdout, stderr = self.SSHClient.exec_command("arkmanager stop")
self.SSHClient.close()
# インスタンスの停止
subprocess.call("aws ec2 stop-instances --instance-ids {}".format(INSTANCEID), shell=True)
send_text = "サーバーの停止が完了しました。"
if send_text:
await discord_event.channel.send(send_text)
discordbot = DiscordBOT(client)
@client.event
async def on_ready():
print('ログインしました')
# on get message
@client.event
async def on_message(message):
if message.author.bot:
return
await discordbot.main(message)
# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)
エラー内容
python3 discordbot.py
を実行し、
ログインしました
と返ってきたら、discordにて$start ark
と入力します。
結果は下記のようになります。
[ec2-user@ip-172-XX-XX-XXX ~]$ python3 discordbot.py
ログインしました
init完了
start ark 受け付けました
{
"StartingInstances": [
{
"InstanceId": "i-5c(以下略)",
"CurrentState": {
"Code": 16,
"Name": "running"
},
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
インスタンス起動処理終了
インスタンス起動待機終了
インスタンスのIPアドレス取得終了
18-183-75-150
<__main__.DiscordBOT object at 0x7f1a35b83810>
Ignoring exception in on_message
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/discord/client.py", line 312, in _run_event
await coro(*args, **kwargs)
File "discordbot.py", line 107, in on_message
await discordbot.main(message)
File "discordbot.py", line 50, in main
self.SSHClient.connect('ec2-{}.compute-1.amazonaws.com'.format(ip_add), username='root', password='')
File "/usr/local/lib/python3.7/site-packages/paramiko/client.py", line 340, in connect
to_try = list(self._families_and_addresses(hostname, port))
File "/usr/local/lib/python3.7/site-packages/paramiko/client.py", line 204, in _families_and_addresses
hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
File "/usr/lib64/python3.7/socket.py", line 752, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known
インスタンスは正常に起動しますが、その後のssh接続の部分にて、
socket.gaierror: [Errno -2] Name or service not known
が発生しています。
追記 2020/08/31
uasiさんご回答ありがとうございます。
当初の問題であるsocket.gaierror: [Errno -2] Name or service not known
は解決しました。
本当にありがとうございます!
新たなエラーが発生しましたので、追記いたします。
上記ソースコードより修正部分を抜粋
# インスタンスのホストネームを取得
proc = subprocess.run(["aws ec2 describe-instances --instance-ids {} --query 'Reservations[*].Instances[*].PublicDnsName' --output text".format(INSTANCEID)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
time.sleep(3)
print('インスタンスのホスト名取得終了')
proc = proc.stdout.decode("utf-8")
proc = proc.replace("\n","")
print(proc)
print('上記がホスト名になります')
# SSH接続クライアント作成
self.SSHClient = paramiko.SSHClient()
self.SSHClient.set_missing_host_key_policy(paramiko.WarningPolicy())
print('connect開始')
self.SSHClient.connect(proc, username='ec2-user', password='')
time.sleep(2)
print('SSH接続クライアント作成終了')
発生したエラー
[ec2-user@ip-172-XX-XX-XXX ~]$ python3 discordbot.py
ログインしました
init完了
start ark 受け付けました
{
"StartingInstances": [
{
"InstanceId": "i-5c(以下略)",
"CurrentState": {
"Code": 16,
"Name": "running"
},
"PreviousState": {
"Code": 16,
"Name": "running"
}
}
]
}
インスタンス起動処理終了
インスタンス起動待機終了
インスタンスのホスト名取得終了
ec2-18-181-177-242.ap-northeast-1.compute.amazonaws.com
上記がホスト名になります
connect開始
/usr/local/lib/python3.7/site-packages/paramiko/client.py:837: UserWarning: Unknown ssh-ed25519 host key for ec2-18-181-177-242.ap-northeast-1.compute.amazonaws.com: b'06dc52b1da6f592a8477c7d623994ca5'
key.get_name(), hostname, hexlify(key.get_fingerprint())
Ignoring exception in on_message
Traceback (most recent call last):
File "/usr/local/lib/python3.7/site-packages/discord/client.py", line 312, in _run_event
await coro(*args, **kwargs)
File "discordbot.py", line 113, in on_message
await discordbot.main(message)
File "discordbot.py", line 56, in main
self.SSHClient.connect(proc, username='ec2-user', password='')
File "/usr/local/lib/python3.7/site-packages/paramiko/client.py", line 446, in connect
passphrase,
File "/usr/local/lib/python3.7/site-packages/paramiko/client.py", line 764, in _auth
raise saved_exception
File "/usr/local/lib/python3.7/site-packages/paramiko/client.py", line 751, in _auth
self._transport.auth_password(username, password)
File "/usr/local/lib/python3.7/site-packages/paramiko/transport.py", line 1509, in auth_password
return self.auth_handler.wait_for_response(my_event)
File "/usr/local/lib/python3.7/site-packages/paramiko/auth_handler.py", line 250, in wait_for_response
raise e
paramiko.ssh_exception.BadAuthenticationType: Bad authentication type; allowed types: ['publickey']
proc
に代入されたホストネームは、ec2のARKサーバーのパブリックDNSと一致していたので正しくホストネームを取得できていると思います。
ARKサーバーにて、下記のようにパーミッションの変更は実施しました。
chmod 700 .ssh
chmod 600 ~/.ssh/authorized_keys
Bad authentication type; allowed types: ['publickey']
から、公開鍵を使った接続をしていないと推測しましたが、修正方法が分かりません。
追記 2020/09/01
uasiさん再びご回答いただきまして、本当にありがとうございます!
「追記 2020/08/31」に記載しましたエラーは解決できました。
その後も新たなエラーはでましたが、なんやかんや調べて直し、botを完成させることができました!
下記に、完成したdiscordBotの機能やソースコードを記載します。
どなたかの参考になれば幸いです。
・Discordにて、 ※
完成したdiscordBotについて(クリックで展開)
botの機能
$start ark
を送信
→ec2のARKサーバー インスタンスを起動
→ARKサーバー インスタンスにSSH接続し、ARKサーバーを起動
・Discordにて、$stop ark
を送信
→ARKサーバー インスタンスにSSH接続し、ARKサーバーを停止
→ec2のARKサーバー インスタンスを停止
Discord上での動作の様子
pc-gamer-bot
の動作は、ARKサーバーで使っているARK: Survival Evolved Linux Server Tools
によるものです。
※インスタンスが正常に起動・停止できていることは、ec2の管理画面で確認済みです。
ソースコード
import discord
import subprocess
import paramiko
import time
# 自分のDiscord Botのアクセストークンに置き換えてください
TOKEN = 'GbQ8(以下略)'
# ARKサーバーのインスタンスidを指定してください
INSTANCEID = 'i-5c(以下略)'
# 接続に必要なオブジェクトを生成
client = discord.Client()
# ***************************
# *** 処理関数
# ***************************
class DiscordBOT:
def __init__(self, client):
self.SSHClient = None
async def main(self, discord_event):
get_text = discord_event.content
print('init完了')
if "$start ark" in get_text:
print('start ark 受け付けました')
# ARKサーバー インスタンスの起動
subprocess.call("aws ec2 start-instances --instance-ids {}".format(INSTANCEID), shell=True)
time.sleep(3)
print('インスタンス起動処理完了')
# ARKサーバー インスタンスが起動するまで待機
subprocess.call("aws ec2 wait instance-status-ok --instance-ids {}".format(INSTANCEID), shell=True)
time.sleep(3)
print('インスタンス起動待機終了')
# ARKサーバー インスタンスのホスト名を取得
proc = subprocess.run(["aws ec2 describe-instances --instance-ids {} --query 'Reservations[*].Instances[*].PublicDnsName' --output text".format(INSTANCEID)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
time.sleep(3)
print('ARKサーバー インスタンスのホスト名取得完了')
proc = proc.stdout.decode("utf-8")
proc = proc.replace("\n","")
print('ARKサーバー インスタンスのホスト名:', proc)
# SSH接続クライアント作成
self.SSHClient = paramiko.SSHClient()
self.SSHClient.set_missing_host_key_policy(paramiko.WarningPolicy())
self.SSHClient.connect(proc, username='ec2-user', password='', key_filename='.ssh/discordbot_key')
time.sleep(2)
print('SSH接続クライアント作成終了')
# SSHでarkサーバー起動
print('********su-steam実行')
stdin, stdout, stderr = self.SSHClient.exec_command('su - steam')
time.sleep(2)
print('********パスワード入力実行')
stdin.write('password\n')
stdin.flush()
time.sleep(2)
print('********arkmanagerstart実行')
stdin.write('arkmanager start\n')
stdin.flush()
time.sleep(2)
print('********ARKサーバー起動処理完了')
# サーバー起動処理完了のメッセージをdiscordに送信
send_text = "<@&746619641706709003> インスタンスの起動とARKサーバーへの接続に成功しました。\n サーバー起動までお待ちください。"
elif "$stop ark" in get_text:
# ARKサーバー インスタンスのホスト名を取得
proc = subprocess.run(["aws ec2 describe-instances --instance-ids {} --query 'Reservations[*].Instances[*].PublicDnsName' --output text".format(INSTANCEID)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
time.sleep(3)
print('ARKサーバー インスタンスのホスト名取得完了')
proc = proc.stdout.decode("utf-8")
proc = proc.replace("\n","")
print('ARKサーバー インスタンスのホスト名:', proc)
# SSH接続クライアント作成
#self.SSHClient = paramiko.SSHClient()
#self.SSHClient.set_missing_host_key_policy(paramiko.WarningPolicy())
self.SSHClient.connect(proc, username='ec2-user', password='', key_filename='.ssh/discordbot_key')
time.sleep(2)
print('SSH接続クライアント作成終了')
# SSHでarkサーバー停止
print('********su-steam実行')
stdin, stdout, stderr = self.SSHClient.exec_command('su - steam')
time.sleep(2)
print('********パスワード入力実行')
stdin.write('password\n')
stdin.flush()
time.sleep(2)
print('********arkmanagerstop実行')
stdin.write('arkmanager stop\n')
stdin.flush()
time.sleep(2)
print('********サーバー停止処理完了、15秒後にインスタンス停止実行')
time.sleep(11)
stdin.write('exit\n')
stdin.flush()
time.sleep(2)
#SSH接続終了
self.SSHClient.close()
time.sleep(2)
print('SSH接続終了')
# インスタンスの停止
subprocess.call("aws ec2 stop-instances --instance-ids {}".format(INSTANCEID), shell=True)
send_text = "<@&746619641706709003> サーバー及びインスタンスの停止が完了しました。\nまたのご来訪をお待ちしております。"
if send_text:
await discord_event.channel.send(send_text)
discordbot = DiscordBOT(client)
@client.event
async def on_ready():
print('ログインしました')
# on get message
@client.event
async def on_message(message):
if message.author.bot:
return
await discordbot.main(message)
# Botの起動とDiscordサーバーへの接続
client.run(TOKEN)