ゆるキャン△スタンプラリー

久々の旅行ネタに本件のツアーに行ってきました。といっても適当に旅行計画をたてて宿を取ってとポイントを回ってくるだけですが、、、

富士山カレー赤

あまり時間的余裕も無いので、とりあえず一番遠いコース富士山麓周遊コースを強行。

富士山麓周遊コースは左の地図の5カ所で、⑤がゴールになるため①の朝霧高原から訪れることにした。名古屋からだと東名高速道路が圧倒的に早いのだけど、せっかく来たから攻めるので中央自動車道上り線を利用することとした。

甲府南IC手前のサービスエリア双葉にて昼食。

お子様ランチ的な見た目に騙されてはいけないほど辛いカレーでした(というか元々青いルーの物が普通のカレーで赤いルーは超辛口というオチ)。

続きを読む…

テスト印刷など

3Dプリンタ購入後、各種設定や印刷環境の整理を行い、テスト印刷などをボチボチ始めている。

ガチガチにサポート材が追加されてます

まずは定番として、FF11からの吸い出しモデルのタルタルを印刷。Metasequoiaに読み込んでSTL出力。それをプリント出力用のFlashPrintにてプリンタ出力。FlashPrintでは土台やサポート材も自動で追加してくれるので、複雑な形状もあまり気にせずに印刷出来そう。但し、あまりゴチャゴチャしたサポート材だとプリントアウト終了後にサポート材を取りはずす時にモデルを破壊してしまう可能性もあり、どこかで工夫が必要なような気がする。

とりあえず完成品

完成後の物が右の写真。サポート材を除去したときに色々折れてしまった所もあり、接着剤で補修してます。フィラメントは同時購入の物ではなく、フィギュアとか仕上がりのキレイなシルク仕上げのPLAを使用しました。

製作した防湿ケース

表面がキラキラしていてとても良い感じ。通常のPLA素材とは違った質感が良いです。

また、フィラメントもボチボチ増えてきてしまい、保管方法も考えなければと、色々調査し、

https://www.youtube.com/channel/UCn23JisBcbb-Nw-3IhofaNA

↑のサイト様を参考に、保管兼引き出し用の防湿ケースの設計と製作を行った。

ケースは参考のサイト様どおりにダイソーで買ってきた300円タッパにフィラメントリール用軸とケースを立てて使うための脚、湿度計取り付けホルダをFreeCADで設計して製作しました。

その他、フィラメント引き出し用のナイロンチューブの蓋とかも製作。またナイロンチューブのケース通し穴に関しては参考サイト様ではTPU素材で製作されていたけど、こちらではホームセンターで適当なサイズのゴムグロメットを買ってきて密閉用として代用してます。

いろいろ試しながら印刷条件を見つけている段階です。タルタルフィギュアもフィギュアサイズやサポート材の数、元のモデルの微調整やら印刷時温度設定など変えながら何度かやり直してます。
だいたいノリが分かってきた所で、再び実用品も作って行こうかと思います。

3Dプリンタ購入

以前から検討していた3Dプリンタを購入しました。動機の大半が興味本位でオモチャが欲しかった程度なんですが、生活用便利治具や自在なケース類、慣れてきたらプラモデルの補助パーツの作成を考えての事なので、方式としてはFDM、またあまり大きいと自部屋での作業に向かないのである程度小さい物(金属製ラックで運用出来る程度)。

ということで、FLASHFOGEのAdventure3に決定し。日本法人の公式ページでポチりました。最低一週間で納品とのことなのでとりあえず発注だけしてボチボチ準備をしようとしていたら、何と次の日に発送通知メールが届き、その次の日の午前に佐川さんが届けに来てくれました。「ちょ、Amazonより早くね?」ということで慌てて開封。

お早い到着で
ラックにもスッキリ収まるサイズ

ルミナスのシステムラック(25mm)でスッキリ収まったけど、天板の水平がイマイチ取れていなそうなのが心配。

マニュアルを見ながら初期設定(キャリブレーション)など行い、付属のフィラメントを導入して、内蔵メモリにある単純な直方体と、使い方の良く分からないNozzle_Remover(ノズルを取り外すための治具らしい)を印刷。

20mm四方の直方体は10分程度、治具は1時間半くらいかかりました。

次に当初の目的である生活治具で、天板固定治具の印刷を行った。これは部屋で使用しているコンパクトな作業台としてキャンプ用のキッチンテーブルを使用しているが、天板がアルミのロールテーブルになっているためコレをホームセンターで買ってきたパイン材に変更している。とりあえずはロールアルミテーブルの上に乗せて使っていたが、3Dプリンタで治具を作製してアルミパイプにしっかり取り付けるという目的。

↑3mmの木ネジ4個で天板に取り付け、アルミパイプを軽くホールド出来る治具を4個作製

アルミパイプに取り付けたところ、お試し試作でPLAなので割れないように少し緩めに製作。コレでも十分に実用的だが、あまりよく外れるようなら内側にテープを貼るか、きつめのABSで製作する必要があるかも知れない。

←は使用しているキッチンテーブル、古いヤツなので、これの収納ラックとか風除け板の付いていないヤツ。メインの天板を16mmのパイン材に交換しました(水性ニスをベタベタに塗ってツヤを出してます)。

いきなり目的のブツを作ってしまったという。。。それくらいお手軽に使えるのは想定外でした。次は何を作ろうかと、フリー素材でハロウィンのカボチャお化けのデータを取ってきて作ってみたら、目と口の穴のところで材料が飛んでしまい失敗(お試しでモデルを小さくしすぎてサポート材が入れられなかったのか)。
複雑な物を作るときはまだまだ勉強が必要ッぽい(データ作成用ソフトFlashPrintの使い方を含めて)。

EC6とDOMの件

EcmaScript6(javascript)の勉強の件で、DOMの記載に関して考えてみた。以前のjavascriptではjQueryなどのライブラリを使ってDOMを簡潔に記載していたが、EC6以降も豊富なライブラリを使う機会が増えているような感じ。

しかし、折角なので、ここはライブラリを使用せずに素のEC6でDOMを分かりやすく記述出来ないものかと考えてみた。通常、素のjavascriptでDOMを記載するときは、例えばBODYタグから下を全てDOMで記述する場合

const body = document.querySelector("body"); // まずBODYタグを見つける
var title = document.createElement("h1");    // タイトル用のH1タグを生成
title.innerHtml = "page title";              // タイトル文字を設定
body.appendChild(title);                     // タイトルをBODYの子要素とする
var content = document.createElement("p");   // 本文段落用タグを生成
content.innerHTML = "page content";          // 本文を設定
body.appendChild(content);                   // 本文をBODYの子要素とする

の様に、既存のタグを見つけるか、新規タグを生成して内容を設定し、親要素に接続する作業を繰り返して文書構造を組み立てていく形になるが、階層が深くなると何が何だか分からなくなってしまう。そのあたりをjQueryでは文書構造に沿った形で記述出来て便利なのだが、”$”関数の数珠つなぎとなりなんだか違う言語のような気がして、個人的には好きにはなれなかった。そこでEC6ではライブラリを一切用いずに次のように記載してみた。

((body) => {                            // bodyを引数とした矢印関数①
  body.className='hogehoge';
  body.appendChild(((title) => {        // bodyタグへのappendChildを同様に矢印関数で記述②
    title.innerHtml = "page title";
    return title;
  })(document.createElement('h1')));
  body.appendChild(((content) => {      // 同様に本文ダグも矢印関数で生成と同時にappendChild
    content.innerHtml = "page content";
    return content;
  })(document.createElement('p')));
  return body;                          // ①関数の戻り値(これが無いと結果が空になる)
})(document.querySelector('body'))      // ①関数の引数として body = document.querySelector('body')

同様の内容を文書構造を意識した記載方法にしてみた。矢印関数を用いてタグを生成(またはquery)し、タグの内容を関数内で記載。子要素も矢印関数内で新たに矢印関数で生成したタグをその場でappendChildする。

かなり冗長な記述になるが、文書構造がHTMLに近い形で多少は見やすくなるのではと考える。

というか、そもそも文書全体をDOM化しようとするからイケないんですけどねw
あと、Lispかっ?!てくらい括弧がメチャメチャ多くなるので、エディタの力を借りないと括弧が一個違うとかでバグ探しも大変なことになりそう。。。vscodeさんありがとう!

不正アクセスログ集計ツールその後

以前の記事で不正アクセスの疑いのあるIPアドレスを集計してアクセスブロックするツールを作成し運用していることと、docker導入に当たってブロックの仕組みを再検討して運用している記事を上げていましたが、もう一つポータルサイトのWEBアプリへの不正アクセス(django管理画面やwordpressへの不正ログイン)を集計する仕組みも紹介しました。実はこの仕組みに関してもdocker化で動作しなくなっていました。

データベースに保管された不正ログイン情報(Login_History)は別コンテナのデータベースサーバーにあるので、localhostからコンテナ名に変更するだけで良いのだが、ブロック済みのIPアドレスリストを取得して新規IPアドレスのみを表示する仕組みについては、ブロックリストがホストで管理されているためコンテナ内からはアクセス出来ない。ブロックリストを保管している/etc/ufw/before.rulesをコンテナに強引にマウントしてしまう方法もあるが、あまりスマートではない。

ということでこれまた興味本位の勉強を兼ねてホスト側からdockerブリッジネットワークへのサービスとしてソケット通信でブロックリストを公開する仕組みを作成した。

  • blocklist-server
#!/usr/bin/env python3
#--*-- coding: utf-8 --*--
# 対象ネットワーク: docker bridge network([shared] 192.168.29.0/24)
# ブロックリストホスト: 192.168.29.1
# 起動(su): /usr/local/sbin/blocklistsrv.py | /usr/bin/logger -i -t blocklistsrv 2>&1 &
# 停止: ps aux | grep blocklistsrv  -> kill -9
# ポート確認: lsof -i :8765  -> kill -9

import os, sys, socket
import psutil
import argparse
import re
import numpy as np

import daemon
from daemon import pidfile

pfile = '/var/run/blocklist-server.pid'

ipset_ufw = '/etc/ufw/before.rules'
ipset_start_line = 'blacklist-ipset-S'
ipset_end_line = 'blacklist-ipset-E'

svport = 8765
svhost = '192.168.29.1'

def getArgs():
    parser = argparse.ArgumentParser('blocklist service cmd')
    parser.add_argument('cmd', nargs='?', help='(start|stop|restart)')
    parser.add_argument('-l', '--syslog', action='store_true', help='enable syslog output')
    return parser.parse_args()

args = getArgs()

ーーー省略ーーー
def blserver():
    socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socket1.bind((svhost, svport))
    socket1.listen(5)

    logger.info('Start Blocklist server. port : %d', (svport))
    while True:
        try:
            clientsocket, address = socket1.accept()
            blist = []
            logger.info(f"Connection from {address} has been established!")
            with open(ipset_ufw, 'r', encoding='utf-8') as fi:
                for line in fi:
                    if re.search(ipset_start_line, line):
                        break
                for line in fi:
                    if res:= re.match('^-A ufw-before-input -s ([0-9./]+) .*', line):
                        #logger.debug(res[1])
                        blist.append(res[1])
                    if re.search(ipset_end_line, line):
                        break
        #except KeyboardInterrupt:
        #    #clientsocket.close()
        #    exit('exit with keyboard interrupt!')
        except FileNotFoundError:
            logger.error('file not found! : %s' % (ipset_ufw))
        except PermissionError:
            logger.error('can\'t read %s, please run as superuser!' % (ipset_ufw))
        finally:
            clientsocket.send('\n'.join(blist).encode('utf-8'))
            clientsocket.close()

        ## blist 送信 

def main():
    #args = getArgs()
    #logger = getLoggerConf(args.syslog)
    if args.cmd == 'start':
        pass
    elif args.cmd == 'stop' or args.cmd == 'restart':
        # デーモンストップ
        if os.path.exists(pfile):
            try:
                logger.info(pfile + " is exits")
                pid =  open(pfile, 'r').read().rstrip()
                proc = psutil.Process(int(pid))
                proc.terminate()
                os.remove(pfile)
                logger.info('pid : %s removed.' % pid)
            except PermissionError:
                logger.error('PID File, permission error, please run as super user.')
                exit(0)
            #except:
            #    logger.error('some error occured.')
        if args.cmd == 'stop':
            exit(0)
    else:
        logger.error('operation command is one of (start|stop|restart)')
        exit(1)

    # デーモン起動
    with daemon.DaemonContext(pidfile=pidfile.TimeoutPIDLockFile(pfile)) as context:
        logger.info(context.is_open)
        blserver()


    #exit(0)



if __name__ == '__main__':
    main()


start, stop, restart機能を持つデーモンスクリプトを作成し、ubuntuホストへサービス登録

  • /usr/lib/systemd/system/blocklist.service
[Unit]
Description=blocklistsrv: this is service of ufw blocking IP address list.
After=docker.service

[Service]
ExecStart=/usr/local/sbin/blocklist-service -l start
ExecStop=/usr/local/sbin/blocklist-service -l stop
ExecReload=/usr/local/sbin/blocklist-service -l restart
Restart=no
Type=simple

[Install]
WantedBy=multi-user.target

ここでsudo systemctl start blocklistでサービスが起動される。ちなみに恒久的に起動するようにするにはsudo systemctl emable blocklistとする。

クライアントであるdocker内のdjangoのコードも下記のようにソケット対応に変更。

blocklist_socket_server = ('192.168.29.1', 8765) 

def get_blocklist():                                                                                                    
    """                                                                                                                 
    root権限でブロックリスト取得コマンドを走らせて標準入力からブロックアドレスのリストを返す                            
    """                                                                                                                 
    socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)                                                         
    socket1.connect(blocklist_socket_server)                                                                            
    bipset = [ ipv4adrset(ips) for ips in socket1.recv(4096).decode('utf-8').split('\n') ]                              
    return bipset                                                                                                       

とりあえず動作するようになったので一安心。

名前付きなUNIXソケットでも良かったけど、またソケットファイルをコンテナにマウント〜となってしまうので、dockerネットワークのゲートウェイから見せるソケットサーバーとしました。こんな感じで良いのだろうか。まぁ特に重要なサービスでもないので結果オーライで運用中です。

カボンバ追加

ということで、水槽掃除のついでに再びモッサモサのカボンバを投入して、3匹ともワチャワチャしてますw

前回とは違い、さっそくつつきはじめてますが・・・さて、いつまで持つかな〜

水槽掃除は結構汚くなるまで放置気味なだったので反省。とりあえず半分程度もとの水を残して再投入する感じで砂利とガラス面その他を掃除しました。

金魚飼育その後

なかなか水槽を洗ってあげられないけど、夏場も越えて元気に育ってくれてます。以前の記事では転覆病に悩まされていて、色々試した結果水草を入れてあげるということで様子を見ることにしましたが、夏の水温のせいもあるかもですが、転覆病の症状もほとんど出なくなり(食後に少し浮きやすい感じは残ってますが)、その代わり水槽が大変なことに・・・。前記事ではもっさりあった水草のカボンバですが、散々食い荒らされてこんなんなってしまいましたw

汚い水槽ですが、背景の緑は写真なので、哀れなカボンバがわかると思いますw

最初の頃は隠れ家くらいにしか使っていなかったのに、いつの間に味を覚えたのか、すっかり葉がなくなってしまいました。明日くらいに新しいカボンバを献上がてらに水槽掃除をしてあげようと思いますー。

これで涼しくなって水温が下がっても転覆の症状が出なければ治療成功といったところかもですが、、
それにしても新芽から狙って食べるようになってきてるから次の水草買ってきても長くは持たないような気がする・・・w

MacBook Pro購入その他

プログラム開発環境の一環として、というか殆ど興味本位レベルで以前から購入検討していたMacBookProを購入しました。Windowsマシンは既にあるので、あまり互換性とか考えず性能重視でM1品をGET。

そんなこんなで初めて使うMacの設定にバタバタしながらの数日でした。以前の職場で使用していた頃のMac(Classicとかwww)とは違いほとんどLinuxマシンと同じ感覚でとりあえずコマンドライン強化に重きを置き、ネットの情報をもとに着々と進めているところです。

あんまり関係ないけど、今朝の日の出写真。雲から下半分だけ顔を出した大陽の図。

たまにはこういうのも良いかな。コロナであまり出かけられないので、ネタが完全に偏ってるし。。。

wordpressのdocker化と既存データの移植

従来環境ではApache2+MySQLでしたが、dockerへの移植時はwebの情報を参考にnginx+php+fpm+mariadbとしました。DockerHubからwordpress+fpmとmariaDBの公式イメージを使用して構築しました。

version: '3.1'
services:
  db:
    image: mariadb:latest
    container_name: mariadb_wp
    volumes:
      - mariaDb:/var/lib/mysql
      - ./db_backup:/backup
    restart: always
    command: --innodb-read-only-compressed=OFF
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: wpdata
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      TZ: Asia/Tokyo
  php-fpm:
    image: wordpress:5.8-fpm
    container_name: php_wp
    volumes:
      - ./blog:/var/www/html
      - ./conf/upload.ini:/usr/local/etc/php/conf.d/upload.ini
    restart: always
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: webdata
      WORDPRESS_DB_PASSWORD: password
      WORDPRESS_DB_NAME: user
      TZ: Asia/Tokyo
  nginx:
    image: nginx
    container_name: nginx_wp
    depends_on:
      - php-fpm
    environment:
      - VIRTUAL_HOST=blog.marochanet.org
      - TZ=Asia/Tokyo
    volumes:
      - ./blog:/var/www/html
      - ./conf/default.conf:/etc/nginx/conf.d/default.conf
      - ./conf/nginx.conf:/etc/nginx/nginx.conf
    expose:
      - '443'
    restart: always

volumes:
  mariaDb:

networks:
  default:
    external:
      name: shared

最後のsharedネットワークへの参加とnginxホストの環境変数VIRTUAL_HOSTでリバースプロキシで構成する仮想ホスト名でアクセス出来るようになっています。この場合ここではport公開せずにexposeを使用します。

mariaDBはwordpressだけではなくその他django、rainloop、nextcloudでも使用するが、とりあえずこのcomposeプロジェクトで立ち上げておく(他のcomposeからもDBホストmariadb_wpでアクセス出来る)。

nginxホストの設定:nginx.conf と default.confはサンプルのまま

$ cat /conf/nginx.conf
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$uri - $is_args - args :::'
                      '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
}

$ cat /conf/default.conf
server {
    index index.php;
    charset utf-8;
    server_name localhost;
    root /var/www/html;
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
    location ~* /wp-config.php {
        deny all;
    }
    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location ~* .(html|css|js|jpe?g|png|gif|svg|mpg|flv|swf)$ {
        expires max;
        access_log off;
    }
}

このままアクセスすると、言語およびadminのパスワード設定など初期設定が始まる。今回は既存サイトの移植が目的だが、まずはこのまま初期化して素のwordpressがアクセス出来るようにします。そののちwp-contentsを既存サイトのものと置き換えます。この段階では一度ポートを閉じるなどしてアクセス出来ないようにしておく必要があるかも知れません。

$ sudo rm -rf wp-contents
$ sudo cp -rf [old position]/wp-contents .
$ sudo chown -R www-data.www-data wp-contents 

次にデータベースのコピーですが。まずは既存サイトのデータベースをバックアップします。たとえば同じホストのmysqlからバックアップするとして

$ mysql-dump --lock-all-tables -u root -p --databases wpdata > wpdata.sql
Passwrd:

今回はmysqlからmariadbへの移植も兼ねているので、そのままではエラーになるため、キーワードを入替えておく必要がある。

今回の場合ではunicodeの表現方式で

utf8mb4_0900_ai_ci => utf8mb4_unicode_ci

に変換しておく必要があった。

変換の後、コンテナのデータベースにリストアします。メンテナンス用にdbサービスにマウントしているdb_backupにコピーしてから

$ docker-compose exec db /bin/bash
db$ mariadb -u root -p
mariadb> drop database wpdata
mariadb> quit
db$ mariadb -u root -p < /backup/wpdata.sql

すでに作成済のデータベースwpdataを一旦削除してから改めてバックアップデータをリストア

これにて無事移植が完了しました。

ここまで簡単な流れにまとまったけど、これでも結構失敗を繰り返したり、、あーでもないこーでもないと試した結果だったりします。その紆余曲折をまとめられれば良いのだが、やってるときは悩みながら突っ走ってしまうので、過程があまり残らないことが間々あるw

IEさようなら

こちらのブログはwordpressを使っているので、まだ大丈夫っぽいですが、ポータルサイトは先月辺りにBootstrap5にアップグレードしたのでいよいよIEでは見られないサイトとなってきました。具体的にBotostrap5のどこがIEでダメなのかはよく調べてないですが、とりあえずjavascriptの記述はmodule式にしたのでIEでは動作はしません。

bootstrap5からjavascriptの形式としてesm(EcmaScriptModule?)が追加されているので、これはモジュールとして使用する必要があります。この場合従来では

<head>
    ・・・・・
    <meta charset="UTF-8">
    <link rel="stylesheet" href="css/bootstrap.css">
    <script type="text/javascript" src="js/bootstrap.bundle.js"></script>
    ・・・・・
</head>

上記のようにヘッダで設定していたものを

<head>
  ・・・・
  <style type="text/css">
    @import '/css/bootstrap.css';
  </style>
  <script type="module">
      import { Alert, Button, Carousel, Collapse, Dropdown, Modal, Offcanvas, Popover, ScrollSpy, Tab, Toast, Tooltip } from "./js/bootstrap.esm.js";
  </script>
  ・・・・
</head>

と、新しい形で書いてあげる必要があります。

まぁ、esmじゃ無い方を使えば従来式で書けるんだけど、まぁ新しいモノ好きということで。。。