[LINUX] Create a Python script for Wake on LAN (NAT traversal Wake on LAN [5])

Introduction

This is a continuation of Last time. This is what I want to do for the time being.

network_detail.png

This time, finally, "Create a Python script for Wake on LAN".

-[1] Obtain a domain using GCP and MyDNS -[2] Set up Softether Server on GCP (Connect from iPhone / Raspberry Pi) -[3] Reverse proxy from Apache on GCP to local Raspberry Apache -[4] Make Raspberry Pi a router -[5] Create a Python script for Wake on LAN.

Desktop PC settings

Assumption: Ubuntu 18.04. First, set up so that Wake on LAN can be executed.

Fixed IP assignment

sudo vi /etc/netplan/~~.yaml

Fill in the following,

/etc/netplan/~~.yaml


# Let NetworkManager manage all devices on this system
network:
  version: 2
  renderer: NetworkManager

  ethernets:
    eno1:
       dhcp4: no
       wakeonlan: true # enable wake on lan
       addresses: [192.168.1.**/24] # assign arbitrary address
       gateway4: 192.168.1.1  # your router(raspi)'s address
       nameservers:
         addresses: [8.8.8.8,8.8.4.4] # google's public dns

Restart.

sudo netplan apply
sudo reboot

Install ʻethtool` to make sure wake on lan is enabled.

sudo apt-get install -y ethtool

Make a note of the Ethernet name. Next, check if the IP is fixed.

$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eno1<<<<copy<<<<: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.**/24 brd 192.168.1.255 scope global noprefixroute eno1
       valid_lft forever preferred_lft forever
    inet6 fe80::2d8:61ff:fe56:242d/64 scope link
       valid_lft forever preferred_lft forever

Make sure Wake on LAN is enabled. It is OK if it is Wake-on: g.

$ sudo ethtool eno1
~~
Supports Wake-on: pumbg
Wake-on: g # if it's d, your wake on lan setting may be wrong
~~

BIOS settings

Omit. WOL setting (1/3) of the booted PC: BIOS / UEFI setting wo please refer.

Verification

Check ʻip a on Raspberry Pi and it is OK if ip is assigned to the Ethernet connected to Desktop. (It must be ʻUP, LOWER_UP. Thank you telcomM # 588883)))

$ ip a
4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether **:**:**:**:**:** brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.1/24 brd 192.168.1.255 scope global noprefixroute eth1
       valid_lft forever preferred_lft forever
    inet6 fe80::83fa:6dee:9799:9a6e/64 scope link 
       valid_lft forever preferred_lft forever

Confirmation (confirmation that wake onlan can be installed and started)

Install wake onlan on Raspberry Pi.

sudo apt install wakeonlan
wakeonlan -i 192.168.1.255 -p 7 **:**:**:**:**:**

If you can start it with this, it's OK.

mod_wsgi First, install mod_wsgi and various things that can run Python scripts with Apache.

sudo apt install python3-dev python3-pip apache2 libapache2-mod-wsgi-py3
sudo apt-get -y install python3 ipython3 python3-flask curl

Python scripting

For the time being, create a working Python script. This time, Flask is used.

cd ~
mkdir flask && cd flask
vi app.py

~/flask/app.py


from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
    return 'This is vpn server for wake on lan!\n\n'
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

For the time being, check if mod_wsgi works.

$ python3 app.py
$ curl http://localhost:5000 #In another tab
This is vpn server for wake on lan!

If you see "This is vpn server for wake on lan!", It's OK.

Apache and mod_wsgi settings

Create a new configuration file for flask.

sudo vi /etc/apache2/sites-available/flask.conf

This time, I am redirecting from GCP to port 80, so enter as follows.

/etc/apache2/sites-available/flask.conf


<VirtualHost *:80>
    ServerName kado.example.com
    WSGIDaemonProcess flask user={username} group={username} threads=5
    WSGIScriptAlias / /home/{username}/flask/adapter.wsgi
    <Directory /home/{username}/flask/>
        WSGIProcessGroup flask
        WSGIApplicationGroup %{GLOBAL}
        WSGIScriptReloading On
        Require all granted
    </Directory>
</VirtualHost>

Enable flask.conf.

sudo a2dissite 000-default.conf
sudo a2ensite flask.conf
sudo service apache2 restart

Create an adapter.wsgi file.

vi ~/flask/adapter.wsgi

~/flask/adapter.wsgi


import sys
if sys.version_info[0]<3:       # require python3
    raise Exception("Python3 required! Current (wrong) version: '%s'" % sys.version_info)
sys.path.insert(0, '/home/kadorpi/flask/')
from app import app as application # <- app means app of app.py

Restart Apache.

sudo service apache2 restart

Check if it works.

$ curl localhost
This is vpn server for wake on lan!

The Python script is now running on Apache.

Wake on LAN scripting for Line bot

It will be a little later when we reach here. Send an HTTP request from Line to GCP and redirect the received request to port 80 of Raspberry Pi. Then, the request received by Raspberry Pi is processed by Python.

First, register an account in Line Developer. Next, create a Channel.

1.png 2.png

3.png

Issue a Channel Secret and make a note of it.

4.png

Issue a Channel Access Token and make a note of it.

5.png

Set Auto-reply etc. from Response settings as appropriate.

6.png

Enter the Webhook URL from the Message API. This time, it is https: // {domain name} / wake-on-lan.

7.png

This completes the LINE Bot settings.

Reconfigure mod_wsgi

Set the Channel Secret and Channel Access Token that you noted earlier as environment variables.

vi ~/flask/adapter.wsgi

Add the following.

~/flask/adapter.wsgi


import os
os.environ['LINE_CHANNEL_SECRET'] = 'fuga'
os.environ['LINE_CHANNEL_ACCESS_TOKEN'] = 'hogehoge'

Python script

Install the required modules.

pip3 install line-bot-sdk flask

The python code looks like this:

python


import os, sys
from flask import Flask, request, abort
app = Flask(__name__)

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    TextMessage, TextSendMessage, MessageEvent
)

# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)

@app.route('/')
def show_web():
    return 'This is vpn server for wake on lan!\n\n'

@app.route('/wake-on-lan', methods=['POST'])
def wake_on_lan():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'ok'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    message = event.message.text

    replies = []
    if re.compile("\s*(check)\s*", re.IGNORECASE).search(message):
        result = confirm()
        replies += [TextSendMessage(result)]

    elif re.compile("\s*(kick)\s*", re.IGNORECASE).search(message):
        if confirm() == 'Awake':
            replies += [TextSendMessage('Already awake')]
        else:
            result = kick()
            replies += [TextSendMessage(result)]

    buttons_template = ButtonsTemplate(
        title='usage', text='Tap below buttons', actions=[
            MessageAction(label=m, text=m) for m in message_list
        ])

    template_message = TemplateSendMessage(alt_text='Usage', template=buttons_template)
    replies += [template_message]

    line_bot_api.reply_message(event.reply_token, replies)


def confirm():
    hostname = "192.168.1.**"
    response = os.system("ping -c 1 " + hostname)
    # and then check the response...
    if response == 0:
        pingstatus = "Awake"
    else:
        pingstatus = "Sleeping"

    return pingstatus

def kick():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.sendto(b'\xFF' * 6 + b'\x**\x**\x**\x**\x**\x**' * 16, ('192.168.1.255', 7))
    s.close()

    return 'kicked'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

To give a little explanation, first load the Channel Secret and Channel Access Token for LINE below.

# get channel_secret and channel_access_token from your environment variable
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if channel_secret is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)

The following has been added for Web display.

@app.route('/')
def show_web():
    return 'This is vpn server for wake on lan!\n\n'

Write the Webhook request processing set above here.

@app.route('/wake-on-lan', methods=['POST'])
def wake_on_lan():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)

    return 'ok'

And finally, when "kick" comes in the message, send MagicPacket, and when "check" comes, I write the process to check if it can be started by Ping.

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    message = event.message.text

    replies = []
    if re.compile("\s*(check)\s*", re.IGNORECASE).search(message):
        result = confirm()
        replies += [TextSendMessage(result)]

    elif re.compile("\s*(kick)\s*", re.IGNORECASE).search(message):
        if confirm() == 'Awake':
            replies += [TextSendMessage('Already awake')]
        else:
            result = kick()
            replies += [TextSendMessage(result)]

    buttons_template = ButtonsTemplate(
        title='usage', text='Tap below buttons', actions=[
            MessageAction(label=m, text=m) for m in message_list
        ])

    template_message = TemplateSendMessage(alt_text='Usage', template=buttons_template)
    replies += [template_message]

    line_bot_api.reply_message(event.reply_token, replies)


def confirm():
    hostname = "192.168.1.**"
    response = os.system("ping -c 1 " + hostname)
    # and then check the response...
    if response == 0:
        pingstatus = "Awake"
    else:
        pingstatus = "Sleeping"

    return pingstatus

def kick():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    s.sendto(b'\xFF' * 6 + b'\x**\x**\x**\x**\x**\x**' * 16, ('192.168.1.255', 7))
    s.close()

    return 'kicked'

Restart Apache.

sudo service apache2 restart

end

In the second half, I was tired and drowned. .. .. Furthermore, it may be difficult to understand because it is written as a memorandum. .. .. I'm glad I could write everything for the time being. I'm glad if you can use it as a reference.

動画

reference

How To Use Apache HTTP Server As Reverse-Proxy Using mod_proxy Extension

Recommended Posts

Create a Python script for Wake on LAN (NAT traversal Wake on LAN [5])
Get a domain using GCP and MyDNS (NAT traversal Wake on LAN [1])
Create a Python environment on Mac (2017/4)
Create a python environment on centos
Create a virtual environment for python on mac [Very easy]
Create a Python environment for professionals in VS Code on Windows
Turn Raspberry Pi into a router (Wake on LAN [4] over NAT)
Create a python environment on your Mac
Let's create a virtual environment for Python
A python script for Mac that zips without garbled characters on Windows
Create a New Todoist Task from Python Script
[Venv] Create a python virtual environment on Ubuntu
A memorandum for touching python Flask on heroku
Create a Python execution environment on IBM i
Create a Python virtual development environment on Windows
Create a Python module
Create a Python environment
[Grasshopper] When creating a data tree on Python script
[Python] 2 Create a risk-return map for your asset portfolio
Create a QR code for the URL on Linux
Build a Python extension for E-Cell 4 on Windows 7 (64bit)
Create a comfortable Python 3 (Anaconda) development environment on windows
Procedure for building a CDK environment on Windows (Python)
Create a Layer for AWS Lambda Python with Docker
Python script to create a JSON file from a CSV file
Create a decent shell and python environment on Windows
Create a Python development environment on OS X Lion
Reverse proxy from Apache on GCP to local Raspberry Pi Apache (NAT traversal Wake on LAN [3])
Script to easily create a client device environment for AWS IoT (Python v2 version)
Create a Wox plugin (Python)
Create a function in Python
Create a dictionary in Python
Create a Python (pyenv / virtualenv) development environment on Mac (Homebrew)
Create a list in Python with all followers on twitter
[Python, shell script, team development] Create a nifty Git repository
Create a child account for connect with Stripe in Python
[Python] Create a date and time list for a specified period
Set up Softether Server on GCP (Connect from iPhone / Raspberry Pi) (NAT traversal Wake on LAN [2])
Create a Twitter BOT with the GoogleAppEngine SDK for Python
Create a python numpy array
Build a Python extension for E-Cell 4 on Mac OSX (Yosemite)
Create a classroom on Jupyterhub
Create a directory with python
Build a python environment on CentOS 7.7 for your home server
[Python] Create a screen for HTTP status code 403/404/500 with Django
Create a Python execution environment for Windows with VScode + Remote WSL
Create a striped illusion with gamma correction for Python3 and openCV3
Create a shell script to run the python file multiple times
Create a color picker for the color wheel with Python + Qt (PySide)
Python vba to create a date string for creating a file name
Create a CGH for branching a laser in Python (laser and SLM required)
A python script that deletes ._DS_Store and ._ * files created on Mac
Create a USB boot Ubuntu with a Python environment for data analysis
Building a Python environment on Mac
Python script for ldapsearch base64 decode
Create a python GUI using tkinter
Create a DI Container in Python
Building a Python environment on Ubuntu
Create a virtual environment with Python!
Create a binary file in Python
A little script for malware self-defense