7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

RasPiやNanoPiをdebug terminalなしで立ち上げる

Last updated at Posted at 2023-03-25

はじめに

RasPiやNanoPiのSBCでLinuxをInstallするとき、最終的に必要がないのに設定のためにRS-232CやKeyboard & Mouse, HDMIを繋ぐことがあります。面倒ですね。
ここでは、コンソールやDebug TerminalなしにWindowsでSD cardを焼いて、いきなりネットからLogin出来る方法を紹介します。Windows 10/11で操作します。Linuxでもできます。MacOSもたぶん行けます。

IP Addressを探す

結局は。SBCがDHCPDでどこに割り当てられたかが不明なだけです。それさえわかればtelnetで接続できます。IPを調査するツールを作りました。

プログラムは以下の通り。

ipquest.py
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
#
# ipquest.py
# Copyright (c) 2023 Shigeru Makino
# MIT License
from scapy.all import ARP, Ether, srp
import sys
import subprocess
import ipaddress
from icmplib import multiping
import netifaces
import os
import platform
import telnetlib
import paramiko
import getpass


def mac2ip(mac, net):
    arp = ARP(pdst=net, hwdst="ff:ff:ff:ff:ff:ff")
    ether = Ether(dst="ff:ff:ff:ff:ff:ff")
    packet = ether / arp
    for i in range(0,5):
        result = srp(packet, timeout=5, verbose=0)[0]
        if result:
            break
    for sent, received in result:
        if received.psrc and received.hwsrc == mac:
            return received.psrc


def connect_host(ipaddress):
    port = 22
    command = "uname -a"
    u = "pi"
    username = input(f"Username: (Default:{u}):")
    if not username:
        username = u
    password = getpass.getpass()
    tp = paramiko.Transport((ipaddress, int(port)))
    try:
        tp.connect(username=username, password=password, hostkey=None)
    except Exception:
        tp.close()
        raise SystemExit("Bad username or password.")
    ch = tp.open_channel("session")
    ch.exec_command(command)

    while not ch.closed:
        if ch.recv_stderr_ready:
            print(ch.recv_stderr(1024).decode("utf-8)"))
        if ch.recv_ready:
            print(ch.recv(1024).decode("utf-8"))


def t_telnet(host):
    port = 22
    timeout = 5
    try:
        tn = telnetlib.Telnet(host, port, timeout)
    except Exception as e:
        print(f"\nCould not connect to {host}:{port}. Error: {e}")
    else:
        print(f"\nTry connect to {host}:{port}")
        tn.write(b"\r\n")
        ans = tn.read_all()
        print(f'Get Answer: {ans.decode("utf-8")}')
        tn.close()


def is_admin():
    if platform.system() == "Windows":
        try:
            return subprocess.check_output("net session", shell=True)
        except subprocess.CalledProcessError:
            return False
    else:
        return os.getuid() == 0


def get_mac(ip):
    arp = ARP(pdst=ip)
    ether = Ether(dst="ff:ff:ff:ff:ff:ff")
    packet = ether / arp
    result = srp(packet, timeout=15, verbose=0)[0]
    if result:
        return result[0][1].hwsrc
    else:
        return None


def get_iface():
    interfaces = netifaces.interfaces()
    ifaces = []
    for iface in interfaces:
        iface_addresses = netifaces.ifaddresses(iface)
        if netifaces.AF_INET in iface_addresses:
            ip_address = iface_addresses[netifaces.AF_INET][0]["addr"]
            if ip_address != "127.0.0.1":
                ifaces.append((iface, ip_address))
    return ifaces


if not is_admin():
    print("Administrative privileges are required to run this program.")
    input("Enter and Exit")
    sys.exit()

ansi_cls = '\033[2J\033[H'
print(f"""
{ansi_cls}
This program checks which IPs of the hosts connected in the first
      scan are disconnected in the second scan."""
)

ifaces = get_iface()
print("\nI have inferface(s):")
for iface in ifaces:
    print(iface)

iface_no = len(ifaces)
if iface_no < 1:
    ans = 'n'
elif iface_no == 1:
    ans = 'y'
else:
    for iface in ifaces:
        ans = input(f"Can I scaning {iface} (y/N) ?")
        if ans == "y":
            break

if ans != 'y':
    print('There is no interface which you choiced, bye !')
    sys.exit()

gateway = iface[1]
network = ipaddress.IPv4Network(f"{gateway}/24", strict=False)
address = [str(ip) for ip in network.hosts()]
print(f"Now, I am scanning {network}, wait a moment")
broadcast = f'{network.broadcast_address}/24'
alive_hosts = []
ip_mac_dict = {}
scan1 = multiping(address)
for host in scan1:
    if host.is_alive:
        ip = host.address
        macaddr = get_mac(ip)
        ip_mac_dict[ip] = macaddr
        alive_hosts.append(ip)
        ent = {ip: macaddr}
        ip_mac_dict.update(ent)
        print(f"Alive:{ip} [{macaddr}]")
input(
    """
First scan is finished.
    Unplug the LAN of the host in question, Enter to continue: """
)
scan2 = multiping(alive_hosts)
for lost in scan2:
    if not lost.is_alive:
        ip = lost.address
        macaddr = ip_mac_dict[ip]
        print(f"Down:{ip} [{macaddr}]")
        input("Re-Plug the LAN & Enter:")
        ipnew = mac2ip(macaddr, broadcast)
        print(f"IP changed or the same: {ip} -> {ipnew}")
        if ipnew:
            ip = ipnew
        t_telnet(ip)
        connect_host(ip)
        print("Finished")
        sys.exit(0)
print("No downed host, Bye !")

このツールを管理者権限で走らせます。

This program checks which IPs of the hosts connected in the first
      scan are disconnected in the second scan.

I have inferface(s):
('{8B42FD22-5C81-4150-8037-471FD5C4FC57}', '192.168.200.1')
('{05D60E80-B223-4085-8095-C06B96913E78}', '169.254.67.210')
('{D7DBAD4F-05FB-4362-B5AF-3D351C139181}', '192.168.1.9')
Can I scaning ('{8B42FD22-5C81-4150-8037-471FD5C4FC57}', '192.168.200.1') (y/N) ?
Can I scaning ('{05D60E80-B223-4085-8095-C06B96913E78}', '169.254.67.210') (y/N) ?
Can I scaning ('{D7DBAD4F-05FB-4362-B5AF-3D351C139181}', '192.168.1.9') (y/N) ?y
Now, I am scanning 192.168.1.0/24, wait a moment
Alive:192.168.1.1 [d4:c1:c8:63:8c:04]
Alive:192.168.1.3 [e8:51:77:99:f3:ec]
Alive:192.168.1.4 [8c:f7:10:4b:38:8c]
Alive:192.168.1.7 [3e:3a:1b:9e:7d:b8]
Alive:192.168.1.9 [c8:34:8e:55:ab:43]

First scan is finished.
    Unplug the LAN of the host in question, Enter to continue:
Down:192.168.1.7 [3e:3a:1b:9e:7d:b8]
Re-Plug the LAN & Enter:
IP changed or the same: 192.168.1.7 -> None

Could not connect to 192.168.1.7:22. Error: timed out
Username: (Default:pi):pi
Password:

Linux NanoPi-NEO4 4.19.193 #3 SMP PREEMPT Thu Aug 25 21:05:15 CST 2022 aarch64 GNU/Linux

Finished
PS C:\users\admin>

はじめにpingに応答したHostとそのmac addressをリストし、後半に応答が無くなったHostを示します。これがあればtelnet/ssdでつなぐことができますし、hdcpdを設定してIPを固定することも容易です。

でも、いくつかの環境整備が要ります。

Windowsの場合

無料のVC++開発環境をDownloadから取ってPythonとVC++が使えるようにします。

次にNpcapをInstallします。IP Packetを操作し、ARPを利用できるようにします。

Debian/Ubuntuの場合

gcc, python-dev環境をinstallします。

apt install gcc
apt install python-dev

その他のOS

その他のOSでもC++の開発、python libraryを構築するヘッダー(Python開発環境)があれば実行可能です。ない場合pipがエラーを吐きますので、下記「OS共通」を実行し、そのエラーを丸ごとChatGPTに送ると教えてくれると思います。

OS共通

pipでパッケージを集めます。

pip install icmplib
pip install netifaces
pip install scapy
pip install paramiko

します。数分間の時間がかかります。pipが自動的にC++でソースをコンパイルし、インストールします。

起動します

該当のNanoPiをLANにつなぎ電源を入れてからこのプログラムを走らせます。
Windowsでは管理者モードのPowerShell、Linux系OSではrootで実行してください。
準備ができたらEnterを押すと1回目のScanをし、応答したホストを表示します。
NanoPiのLANを抜いてEnterを押すと前回応答したものだけをScanします。これで応答しなくなったものがNanoPiのIPです。

Telnet / SSH で繋ぐ

こうなればPuTTYTera termで接続するだけです。
pi/piで入れるのでまず、パスワードを変更します。

すぐやるべきこと

  1. piのパスワードを変える
  2. sudo passwd root でrootにパスワードを変える
  3. sudo adduser YOU であなたのアカウントを作る
  4. sudo deluser pi でアカウントを消す
  5. root loginを禁止にする
  6. sudo禁止にする

piのパスワード

ネットにつないでいるのですからNanoPiでもRaspiでも世界の果てからアッタックさされる危険性があります。まずパスワードを変えます。

新たにアカウントを作る

あなたのアカウントを独自に作ってください。

adduser <YOU>

piを消す

user名とpasswdが組で防衛してます。knownなpiを使い続けるのはセキュアではありません。

deluser pi

root login

sshdを設定します。

echo 'PermitRootLogin no' > /etc/ssh/sshd_config.d/PermetRootLogin.cfg

sshd サービスを再起動します。

systemctl restart sshd

まとめ

  1. SBC LinuxはIP addressさえわかればtelnetできる
  2. ipquest.pyはDHCPが割り当てたIPを探るプログラムである
  3. Telnet に成功したら直ちにセキュリティ対策をとる
  4. 可能ならば、設定中は外部から遮断されたプライベートはネットワークが好ましい。
7
7
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?