2025年12月15日月曜日

fail2ban で ban の際に slack に通知する

fail2ban で ban の際に slack に通知する

概要

前回続きです
ban になったら slack に通知するようにしてみました

環境

  • Ubuntu 24.04
  • docker
  • linuxserver/fail2ban 1.1.0-r2-ls26
  • fail2ban 1.1.0

compose.yaml

ユーザの権限部分など少し変更しています
作成したアクションの設定ファイルもマウントするようにしています

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    network_mode: host
    volumes:
      - ./nginx/log:/var/log/nginx  # このログをfail2banで監視
    restart: unless-stopped

  fail2ban:
    image: linuxserver/fail2ban:latest
    container_name: fail2ban
    network_mode: host
    cap_add:
      - NET_ADMIN # BANに必須
      - NET_RAW   # BANに必須
    volumes:
      - ./config/jail.local:/config/fail2ban/jail.d/jail.local
      - ./config/filter.d/nginx-404.conf:/config/fail2ban/filter.d/nginx-404.conf
      - ./config/action.d/slack.conf:/config/fail2ban/action.d/slack.conf
      - ./nginx/log:/remotelogs/nginx:ro  # nginxログを読み取り専用で監視
    environment:
      - PUID=1000  # 自身のユーザIDとグループIDに合わせて変更 (config/ 配下の権限がコンテナ側のユーザIDで変更されてしまうため編集ができなくなる)
      - PGID=122
      - TZ=Asia/Tokyo
      - VERBOSITY=-vv
    restart: unless-stopped

./config/jail.conf

nginx-404 サービスの action に slack を追加します

[DEFAULT]

# "bantime.increment" allows to use database for searching of previously banned ip's to increase a
# default ban time
bantime.increment = true

# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
bantime.maxtime = 5w

# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier
bantime.factor = 24

# "bantime" is the number of seconds that a host is banned.
bantime = 1h

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 24h

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

# Prevents banning LAN subnets
ignoreip    = 127.0.0.1/8 ::1
              10.0.0.0/8
              172.16.0.0/12
              192.168.0.0/16

# The ban action "iptables-multiport" (default) should work for most
# The ban action "iptables-allports" can be used if multiport causes issues
banaction = iptables-multiport
banaction_allports = iptables-allports

# Read https://github.com/sebres/PoC/blob/master/FW.IDS-DROP-vs-REJECT/README.md before changing block type
# The block type "REJECT --reject-with icmp-port-unreachable" (default behavior) should respond to, but then instantly reject connection attempts
# The block type "DROP" should not respond to connection attempts, resulting in a timeout
#banaction = iptables-multiport[blocktype=DROP]

# Add additional actions
action  = %(action_)s

# [sshd]
# configuration inherits from jail.conf
# enabled = true
# chain   = INPUT
# action  = %(action_)s

[nginx-http-auth]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-badbots]
# configuration inherits from jail.d/nginx-badbots.conf 
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-botsearch]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-deny]
# configuration inherits from jail.d/nginx-deny.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-unauthorized]
# configuration inherits from jail.d/nginx-unauthorized.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-404]
# Custom filter to detect excessive 404 errors
enabled  = true
chain    = INPUT
port     = http,https
filter   = nginx-404
logpath  = /remotelogs/nginx/access.log
maxretry = 3
findtime = 10s
bantime  = 1h
action   = %(action_)s
           slack

config/filter.d/nginx-404.conf

前回と同じです

config/action.d/slack.conf

各種アクション時に通知するようにします
webhook_url は変更してください

# Fail2Ban configuration file
#
# Action to send notifications to Slack
#

[Definition]

# Option: actionstart
# Notes.: command executed once at the start of Fail2Ban.
actionstart = curl -X POST -H 'Content-type: application/json' \
              --data '{"text":"[Fail2Ban] <name> jail has started"}' \
              <slack_webhook_url>

# Option: actionstop
# Notes.: command executed once at the end of Fail2Ban
actionstop = curl -X POST -H 'Content-type: application/json' \
             --data '{"text":"[Fail2Ban] <name> jail has stopped"}' \
             <slack_webhook_url>

# Option: actioncheck
# Notes.: command executed once before each actionban command
actioncheck =

# Option: actionban
# Notes.: command executed when banning an IP. Take care that the
#         command is executed with Fail2Ban user rights.
actionban = curl -X POST -H 'Content-type: application/json' \
            --data '{"text":"[Fail2Ban] :no_entry: <name> jail banned IP: *<ip>*\nFailures: <failures>\nTime: <time>"}' \
            <slack_webhook_url>

# Option: actionunban
# Notes.: command executed when unbanning an IP. Take care that the
#         command is executed with Fail2Ban user rights.
actionunban = curl -X POST -H 'Content-type: application/json' \
              --data '{"text":"[Fail2Ban] :white_check_mark: <name> jail unbanned IP: *<ip>*"}' \
              <slack_webhook_url>

[Init]

# Slack webhook URL
slack_webhook_url = https://hooks.slack.com/services/xxx/xxx/xxx

動作確認

  • docker compose up -d

あとは localhost/test などで 404 を発生させると通知が来ます
unbanip で ban 解除しても通知が来ます

トラブルシューティング

起動時にちゃんと追加したアクションが読み込まれていることを確認しましょう
ここで読み込まれていないとうまく動作しません

  • docker compose logs | grep ‘action’
fail2ban  |  2025-12-12 10:00:26,132 79986F9C2B28 INFO  Loading configs for action.d/iptables-multiport under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 DEBUG Reading configs for action.d/iptables-multiport under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 DEBUG Reading config files: /etc/fail2ban/action.d/iptables-multiport.conf
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/iptables-multiport.conf']
fail2ban  |  2025-12-12 10:00:26,133 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/iptables.conf']
fail2ban  |  2025-12-12 10:00:26,134 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/iptables.conf', '/etc/fail2ban/action.d/iptables-multiport.conf']
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 INFO  Loading configs for action.d/slack under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 DEBUG Reading configs for action.d/slack under /etc/fail2ban
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 DEBUG Reading config files: /etc/fail2ban/action.d/slack.conf
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/slack.conf']
fail2ban  |  2025-12-12 10:00:26,147 79986F9C2B28 INFO    Loading files: ['/etc/fail2ban/action.d/slack.conf']

最後に

ban になったかどうかをいちいち確認する必要がなくなるので便利です
現在の ban 状況を定期的に送信してくれる機能があると更に便利かなと思います

2025年12月14日日曜日

fail2ban を docker で動かす

fail2ban を docker で動かす

概要

fail2ban をコンテナで動作させる方法を紹介します
よく紹介されているのはホストに直接インストールする方法かなと思います
今回は nginx の不正アクセスを ban する方法を紹介します

環境

  • Ubuntu 24.04
  • docker
  • linuxserver/fail2ban 1.1.0-r2-ls26
  • fail2ban 1.1.0

compose.yaml

fail2ban は linuxserver/fail2ban を使います
fail2ban は仕組み上 iptables を使うのでホストネットワークを使う必要があります

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    network_mode: host
    volumes:
      - ./nginx/log:/var/log/nginx  # このログをfail2banで監視
    restart: unless-stopped

  fail2ban:
    image: linuxserver/fail2ban:latest
    container_name: fail2ban
    network_mode: host
    cap_add:
      - NET_ADMIN # BANに必須
      - NET_RAW   # BANに必須
    volumes:
      - ./config/jail.local:/config/fail2ban/jail.d/jail.local
      - ./config/filter.d/nginx-404.conf:/config/fail2ban/filter.d/nginx-404.conf
      - ./nginx/log:/remotelogs/nginx:ro  # nginxログを読み取り専用で監視
    environment:
      - TZ=Asia/Tokyo
    restart: unless-stopped

./config/jail.local

[DEFAULT]

# "bantime.increment" allows to use database for searching of previously banned ip's to increase a
# default ban time
bantime.increment = true

# "bantime.maxtime" is the max number of seconds using the ban time can reach (doesn't grow further)
bantime.maxtime = 5w

# "bantime.factor" is a coefficient to calculate exponent growing of the formula or common multiplier
bantime.factor = 24

# "bantime" is the number of seconds that a host is banned.
bantime = 1h

# A host is banned if it has generated "maxretry" during the last "findtime"
# seconds.
findtime = 24h

# "maxretry" is the number of failures before a host get banned.
maxretry = 5

# Prevents banning LAN subnets
ignoreip    = 127.0.0.1/8 ::1
              10.0.0.0/8
              172.16.0.0/12
              192.168.0.0/16

# The ban action "iptables-multiport" (default) should work for most
# The ban action "iptables-allports" can be used if multiport causes issues
banaction = iptables-multiport
banaction_allports = iptables-allports

# Read https://github.com/sebres/PoC/blob/master/FW.IDS-DROP-vs-REJECT/README.md before changing block type
# The block type "REJECT --reject-with icmp-port-unreachable" (default behavior) should respond to, but then instantly reject connection attempts
# The block type "DROP" should not respond to connection attempts, resulting in a timeout
#banaction = iptables-multiport[blocktype=DROP]

# Add additional actions
action  = %(action_)s
          apprise-api[host="127.0.0.1", tag="fail2ban"]
          cloudflare[cfuser="YOUR-EMAIL", cftoken="YOUR-TOKEN"]

# [sshd]
# configuration inherits from jail.conf
# enabled = true
# chain   = INPUT
# action  = %(action_)s

[nginx-http-auth]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-badbots]
# configuration inherits from jail.d/nginx-badbots.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-botsearch]
# configuration inherits from jail.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-deny]
# configuration inherits from jail.d/nginx-deny.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-unauthorized]
# configuration inherits from jail.d/nginx-unauthorized.conf
enabled = true
chain   = DOCKER-USER
action  = %(action_)s

[nginx-404]
# Custom filter to detect excessive 404 errors
enabled  = true
chain    = INPUT
port     = http,https
filter   = nginx-404
logpath  = /remotelogs/nginx/access.log
maxretry = 3
findtime = 10s
bantime  = 1h

banaction などは適切なものを設定しましょう

ポイントは一番下の nginx-404 です
これで 404 を連発する IP を ban にできます
それ以外も nginx の ban ルールですが認証エラーなどで発生させるのが難しいので今回 404 を追加しています

config/filter.d/nginx-404.conf

# Fail2Ban filter to detect excessive 404 errors from nginx
# Detects clients making too many requests to non-existent pages

[Definition]

failregex = ^<HOST> -.*"(GET|POST|HEAD).*HTTP.*" 404

ignoreregex =

datepattern = {^LN-BEG}%%d/%%b/%%ExY:%%H:%%M:%%S

nginx のログで 404 を正規表現でマッチさせます

起動

  • docker compsoe up -d

動作確認

今回は10秒間に3回404発生でbanなのですぐにbanになります
localhost/test など存在しないページにアクセスしましょう

そして ban になっているか確認します

  • docker compose exec fail2ban fail2ban-client status nginx-404
Status for the jail: nginx-404
|- Filter
|  |- Currently failed: 0
|  |- Total failed:     6
|  `- File list:        /remotelogs/nginx/access.log
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   xxx.xxx.xxx.xxx

ban されると i-Filter のお馴染みの画面が表示されるようになります
また iptables -L でホスト側を確認するとちゃんと REJECT ルールがあることが確認できると思います

sudo iptables -L f2b-nginx-404

Chain f2b-nginx-404 (1 references)
target     prot opt source               destination
REJECT     all  --  xxx.xxx.xxx.xxx      anywhere             reject-with icmp-port-unreachable
RETURN     all  --  anywhere             anywhere

ban解除

  • docker compose exec fail2ban fail2ban-client set nginx-404 unbanip xxx.xxx.xxx.xxx

テストなどですぐに解除したい場合は IP を指定して ban 解除できます
iptables にもないことが確認できます

最後に

今回使った linuxserver/fail2ban は /config/fail2ban/filter.d に大量のプリセットがあるのでログをセットするだけでも簡単に ban 監視を開始することができます
今回は 404 ルールだけなかったので手動で追加しています

fail2ban は iptables を操作するツールなので iptables を使ってアクセス制御しているケースとは親和性は高い気がします
逆にクラウドなどのファイアウォールでアクセス制御している場合は fail2ban とは少し相性が悪いので使い分けが必要かなと思います

結局 iptables なのでホストにインストールしても変わりませんが docker compose でアプリを管理していたりホストにはなるべくアプリをインストールしたくないという場合にはやはり docker で fail2ban を動かしたくなるかなと思います

参考サイト

2025年12月13日土曜日

linuxserver openssh-server にログインしたら通知する方法

linuxserver openssh-server にログインしたら通知する方法

概要

ssh したら slack に通知します
s6-overlay という機能を使います

環境

  • macOS 15.7.1
  • docker 29.1.2
  • linuxserver/openssh-server 10.0_p1-r10-ls211

compose.yaml

services:
  ssh_server:
    image: ghcr.io/linuxserver/openssh-server
    container_name: ssh_server
    ports:
      - "2222:2222"
      - "10022:10022"
    environment:
      - PUID=1000
      - PGID=1000
      - PASSWORD_ACCESS=false
      - USER_NAME=operator
      - PUBLIC_KEY_FILE=/config/ssh/authorized_keys
    volumes:
      - ./custom-cont-init.d:/custom-cont-init.d:ro
      - ./custom-services.d:/custom-services.d:ro
      - ./config/ssh:/config/ssh
    restart: unless-stopped

custom-services.d/run

#!/usr/bin/execlineb -P
with-contenv
exec bash /custom-services.d/notify_ssh/notify.sh
  • chmod +x custom-services.d/run

custom-services.d/notify_ssh/notify.sh

#!/usr/bin/env bash

LOGFILE="/config/logs/openssh/current"
WEBHOOK_URL="https://hooks.slack.com/services/xxx/xxx/xxx"

# ログファイル生成待ち
while [ ! -f "$LOGFILE" ]; do
    sleep 1
done

echo "[notify-ssh] starting log monitor"

# ログ監視
tail -Fn0 "$LOGFILE" | while read -r line; do

    # ① 接続ログ (Connection from)
    if echo "$line" | grep -q "Connection from"; then
        IP=$(echo "$line" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}")
        curl -s -X POST -H 'Content-Type: application/json' \
            --data "{\"text\": \"👀 SSH access from $IP\"}" \
            "$WEBHOOK_URL" >/dev/null
    fi

    # ② 成功ログ (Accepted)
    if echo "$line" | grep -q "Accepted"; then
        IP=$(echo "$line" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}")
        USER=$(echo "$line" | awk '{print $(NF-5)}')
        curl -s -X POST -H 'Content-Type: application/json' \
            --data "{\"text\": \"🔐 SSH login success: user=$USER, ip=$IP\"}" \
            "$WEBHOOK_URL" >/dev/null
    fi

    # ③ 失敗ログ (Failed)
    if echo "$line" | grep -q "Failed"; then
        IP=$(echo "$line" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}")
        curl -s -X POST -H 'Content-Type: application/json' \
            --data "{\"text\": \"⚠️ SSH failed login attempt from $IP\"}" \
            "$WEBHOOK_URL" >/dev/null
    fi

done
  • chmod +x custom-services.d/notify_ssh/notify.sh

最後に

コンテナはこの方法がいいです
systemd があれば socat なども使えます

参考サイト

2025年12月12日金曜日

Windows に metasploit をインストールする

Windows に metasploit をインストールする

概要

Windows で攻撃するということはほぼないですが一応インストールしたのでメモしておきます

環境

  • Windows11Home
  • metasploit 6.4.102
  • msfconsole 4.0.5

インストーラからインストール

https://docs.metasploit.com/docs/using-metasploit/getting-started/nightly-installers.html

ここからインストーラをダウンロードしてインストーします
インストール中に Windows defender が反応しまくりますが無視で OK です

Windows defender で metasploit を除外する

除外しないと msfconsole が起動できません

起動

Powershell でもコマンドプロンプトでも OK です

  • cd C:\metasploit-framework\bin
  • .\msfconsole.bat

起動すれば OK です

最後に

リバースシェルを張りたい場合はここを参考にmsfvenomでペイロード(exe)を作成しWindows上で実行します

基本は metasploit は攻撃側のツールなので Metasploit をインストールした Windows に攻撃するのは Windows を脆弱にするしかないです

  • use でどのマシンにどの脆弱性を使うか決める
  • set payload で脆弱性をつけたときに流し込むペイロード(シェルスクリプトや exe など)を決定
  • exploit で攻撃開始

であとは

  • meterapreter で待ち受けなどを別途する

という流れなので Metasploit をインストールしたマシンはあくまでも攻撃側になります

参考サイト

2025年12月11日木曜日

Mac の UTM でディレクトリ共有する方法

Mac の UTM でディレクトリ共有する方法

概要

紹介します

環境

  • macOS 15.7.1
  • UTM 4.7.4
  • Ubuntu 24.04

共有設定

マウント

sudo mkdir -p /mnt/share
sudo mount -t 9p -o trans=virtio share /mnt/share

最後に

特にツールのインストールは不要でした
qemu-guest-agent も不要っぽいです

参考サイト

2025年12月10日水曜日

linuxserver openssh-server にパスワードなしでログインする方法

linuxserver openssh-server にパスワードなしでログインする方法

概要

鍵を作成し登録してあげます

環境

  • macOS 15.7.1
  • docker 29.1.2
  • linuxserver/openssh-server 10.0_p1-r10-ls211

compose.yaml

services:
  ssh_server:
    image: ghcr.io/linuxserver/openssh-server
    container_name: ssh_server
    ports:
      - "2222:2222"
      - "10022:10022" # SSHリモートポートフォーワードトンネル用
    environment:
      - PUID=1000
      - PGID=1000
      - PASSWORD_ACCESS=false
      - USER_NAME=operator
      - PUBLIC_KEY_FILE=/config/ssh/authorized_keys
    volumes:
      - ./custom-cont-init.d:/custom-cont-init.d:ro
      - ./config/ssh:/config/ssh
    restart: unless-stopped

custom-cont-init.d/01-sshd-config.sh

#!/bin/bash

echo "[custom-init] modifying sshd_config..."

# 対象ファイル
SSHD_CONFIG="/config/sshd/sshd_config"

# Agent Forwarding
sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding yes/' "$SSHD_CONFIG"

# TCP Forwarding
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/' "$SSHD_CONFIG"

# GatewayPorts
sed -i 's/GatewayPorts no/GatewayPorts yes/' "$SSHD_CONFIG"

# X11Forwarding──
sed -i 's/X11Forwarding no/X11Forwarding yes/' "$SSHD_CONFIG"

echo "[custom-init] done."
  • chmod +x config/custom-cont-init.d/01-sshd-config.sh

鍵作成

  • ssh-keygen -t ed25519 -f operator_key
  • mkdir -p config/ssh
  • mv operator_key.pub config/ssh/authorized_keys

動作確認

  • docker compose up -d
  • chmod 600 operator_key
  • ssh -i operator_key operator@localhost -p 2222

ホストの警告が出る場合は以下で無視しましょう

  • ssh -i operator_key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null operator@localhost -p 2222

リモートSSHトンネルは以下

  • ssh -i operator_key -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -N -R 10022:localhost:22 operator@192.168.1.100 -p 2222

最後に

ログインしたいサーバに鍵を渡しましょう

2025年12月9日火曜日

linuxserver openssh-server でリモートフォーワードを有効にする方法

linuxserver openssh-server でリモートフォーワードを有効にする方法

概要

デフォルトだと無効になっています

環境

  • macOS 15.7.1
  • docker 29.1.2
  • linuxserver/openssh-server 10.0_p1-r10-ls211

compose.yaml

services:
  ssh_server:
    image: ghcr.io/linuxserver/openssh-server
    container_name: ssh_server
    ports:
      - "2222:2222"
      - "10022:10022" # SSHリモートポートフォーワードトンネル用
    environment:
      - PUID=1000
      - PGID=1000
      - PASSWORD_ACCESS=true
      - USER_NAME=test
      - USER_PASSWORD=mypass
    volumes:
      - ./custom-cont-init.d:/custom-cont-init.d:ro
    restart: unless-stopped

custom-cont-init.d/01-sshd-config.sh

#!/bin/bash
# chmod +x config/custom-cont-init.d/01-sshd-config.sh を必ず実行

echo "[custom-init] modifying sshd_config..."

# AllowAgentForwarding
sed -i 's/#AllowAgentForwarding yes/AllowAgentForwarding yes/g' /config/sshd/sshd_config

# AllowTcpForwarding
sed -i 's/AllowTcpForwarding no/AllowTcpForwarding yes/g' /config/sshd/sshd_config

# GatewayPorts
sed -i 's/GatewayPorts no/GatewayPorts yes/g' /config/sshd/sshd_config

# X11Forwarding
sed -i 's/X11Forwarding no/X11Forwarding yes/g' /config/sshd/sshd_config

echo "[custom-init] done."
  • chmod +x config/custom-cont-init.d/01-sshd-config.sh

動作確認

  • docker compose up -d
  • docker compose exec ssh_server cat /config/sshd/sshd_config

GatewayPorts などが yes になっていることを確認します

最後に

カスタムスクリプトを配置するパスが /custom-cont-init.d でした
バージョンによって異なるのでご注意ください

参考サイト

2025年12月8日月曜日

ssh できるコンテナを気軽に立てる方法

ssh できるコンテナを気軽に立てる方法

概要

Mac などで ssh サーバを立てるのが面倒な場合に docker で簡単に構築できます

環境

  • macOS 15.7.1
  • docker 29.1.2
  • linuxserver/openssh-server 10.0_p1-r10-ls211

起動

docker run -d \
  -p 2222:2222 \
  -e PASSWORD_ACCESS=true \
  -e USER_PASSWORD=mypass \
  ghcr.io/linuxserver/openssh-server

ログイン

  • ssh linuxserver.io@192.168.1.100 -p 2222

ユーザを指定する

docker run -d \
  -p 2222:2222 \
  -e PASSWORD_ACCESS=true \
  -e USER_PASSWORD=mypass \
  -e USER_NAME=test \
  ghcr.io/linuxserver/openssh-server
  • ssh test@192.168.1.100 -p 2222

最後に

ssh の検証するときには便利です

参考サイト

2025年12月7日日曜日

ただのうpろだにcurlでファイルをアップロードする

ただのうpろだにcurlでファイルをアップロードする

概要

便利

環境

  • macOS 15.7.1
  • curl 8.17.0

アップロード

  • Basic認証のパスワードは公式のものを使うのでそのまま
  • file にはアップロードするファイルをフルパスで
  • delete_pass には削除用パスワードを設定
  • もし閲覧にも制限を設けたい場合には post_password も設定する
  • 時間制限を設けるパラメータはなさそう
curl -X POST https://tadaup.jp/wp-json/custom/v1/upload \
     -u "API:AoLU ets7 2zh3 gvqc cTEe BHfp" \
     -F "file[]=@/Users/user01/Downloads/G7gvWUFbwAAv6fq.jpeg" \
     -F "title=test with curl" \
     -F "comment=" \
     -F "delete_pass=xxx" \
     -F "post_password=" \
     -F "r18=no"

レスポンス

{"success":true,"post_id":1325317,"url":"https:\/\/tadaup.jp\/070953461325317\/","source_url":"https:\/\/tadaup.jp\/CKqGMnka.jpeg","thumbnail":"https:\/\/tadaup.jp\/CKqGMnka.jpeg"}

url がアップロードしたファイルのリンクです

削除

残念ながら API はないので手動で GUI から削除パスワードを使って削除します
今後実装されるかもです

最後に

アーカイブファイルはアップロードできないのでちゅういしてください
直リンクが発行されるのでそれにアクセスすればアーカイブファイルでもアップロードできます

参考サイト

2025年12月4日木曜日

VSCode で Remote tunnel する

VSCode で Remote tunnel する

概要

操作するファイルがリモートサーバにある場合に vscode.dev という中継サーバを介してローカルで起動している VSCode で編集することができます

環境

  • Ubuntu 24.04
    • code 1.106.3
  • Windows 11
    • VSCode 1.106.3

リモートサーバ側でトンネル作成

  • curl -Lk 'https://code.visualstudio.com/sha/download?build=stable&os=cli-alpine-x64' --output vscode_cli.tar.gz
  • tar -xf vscode_cli.tar.gz

これで code というバイナリが入手できます
あとはホームディレクトリに起動して code を起動します

  • cd
  • ./code tunnel

Github Account か Microsoft Account を選択する必要があるので Github Account を選択します

起動すると例のごとくhttps://github.com/login/device にアクセスしてトークンを取得して ./code tunnel に入力してあげます

これでトンネル用の URL が発行されるのでこれをブラウザに入力してもいいのですがそれだと code-server と変わらないので VSCode からアクセスしてみます

VSCode の Remote - Tunnels 拡張をインストールする

  • 左メニューの拡張を選択
  • Remote tunnel を入力して検索
  • 「Remote - Tunnels」拡張をインストール
  • 左メニューに Remote - Tunnels 拡張のアイコンが追加されるので選択
  • さきほど ./code tunnel で起動したサーバが一覧にあるのでそれを選択
  • 初回はブラウザで OAuth の認証が発生するので ./code tunnel と同じ Github Account でログインします
  • 接続に成功すると VSCode が再起動しサーバ上のファイルが操作できるようになります

その他

  • ローカルモードに切り替える場合は切断する必要があるので左下の接続先サーバを選択し「Close Remote Connection」を選択します
  • ./code tunnel 時にはトークンのみですが VSCode 側でトンネルと VSCode を紐づける場合にはブラウザの OAuth が必要になるので VSCode が起動する環境にブラウザがありかつそのブラウザから該当の Github Account でログインできる必要があるので注意しましょう

最後に

普通はこんな使い方しないのでどうしてもローカルで開発できない場合などに使うんだと思います

もしくは Powershell などを使いたくない場合なども使うのかなと思います

参考サイト

2025年11月22日土曜日

Speckit 超入門

Speckit 超入門

概要

code-server + speckit を試してみました
一応アプリは作成してくれましたが生成されるファイルが微妙に違うのでまだサポートしていないのかもしれません

環境

  • Ubuntu 24.04
  • speckit (specify 0.0.22)
  • code-server v4.105.1

インストール

  • uv tool install specify-cli --from git+https://github.com/github/spec-kit.git

初期化

  • specify init speckit_test
    • copilot を選択
    • sh を選択

code-server を開く

  • speckit_test を vscode で開く
  • モデルを Claude Sonnet 4.5 にする

以下 code-server の Github copilot chat のチャット欄で作業します

規則作成

  • /speckit.constitution コード品質、テスト標準、ユーザーエクスペリエンスの一貫性、パフォーマンス要件に焦点を当てた原則を作成します。

/home/devops/work/speckit_test/.github/prompts/speckit.constitution.prompt.md が作成された

仕様作成

  • /speckit.specify 家庭や業務用キッチンで使用できるシンプルで視認性の高いタイマーを、Web ブラウザー経由で提供します。1分、3分、5分を設定するためのボタンを配置します。ボタンを押すと、その時間からカウントダウンが開始されます。カウントダウン中は、残り時間を目立つように表示します。カウントダウン中に再度ボタンを押すと、タイマーがリセットされ、新たなカウントダウンが開始されます。

/home/devops/work/speckit_test/.github/prompts/speckit.specify.prompt.md が作成されました

実装計画作成

  • /speckit.plan HTML5、JavaScript(ES6)、CSS3 で作成してください

/home/devops/work/speckit_test/.github/prompts/speckit.plan.prompt.md が作成されました

作業タスクの作成

  • /speckit.tasks

/home/devops/work/speckit_test/.github/prompts/speckit.tasks.prompt.md が作成されました

実装

  • /speckit.implement

/home/devops/work/speckit_test/.github/prompts/speckit.implement.prompt.md が作成されました

また実際に作成されたコードは以下の通りです

ls -ltrR kitchen-timer/
kitchen-timer/:
total 24
drwxr-xr-x 3 devops docker 4096 Nov 21 13:23 assets
-rw-r--r-- 1 devops docker 1844 Nov 21 13:24 index.html
drwxr-xr-x 2 devops docker 4096 Nov 21 13:24 css
drwxr-xr-x 2 devops docker 4096 Nov 21 13:26 js
-rw-r--r-- 1 devops docker 7522 Nov 21 13:26 README.md

kitchen-timer/assets:
total 4
drwxr-xr-x 2 devops docker 4096 Nov 21 13:23 sounds

kitchen-timer/assets/sounds:
total 0

kitchen-timer/css:
total 8
-rw-r--r-- 1 devops docker 6334 Nov 21 13:24 styles.css

kitchen-timer/js:
total 16
-rw-r--r-- 1 devops docker 2975 Nov 21 13:25 timer.js
-rw-r--r-- 1 devops docker 3650 Nov 21 13:25 audio.js
-rw-r--r-- 1 devops docker 6150 Nov 21 13:26 app.js

動作確認

  • cd speckit_test/kitchen-timer
  • python -m http.server 8000
  • curl localhost:8000

最後に

code-server で speckit を動かしてみました
冒頭にも紹介しましたが挙動が微妙でまだ code-server ではサポートしていないのかもしれません

本当は git コマンドで branch も作成してくれるよういなのですがやってくれませんでした

またモデルが GPT-4.1 だと動きませんでした

続けて修正、エンハンスなどしたい場合は /speckit.specify から再度行います

参考サイト

2025年11月21日金曜日

GCP で CPU 監視する方法

GCP で CPU 監視する方法

概要

コンソールでの手順を紹介します

環境

  • GCP (2025/11/21 時点)

手順

  1. Compute Engine のページを開く
  2. 「VM インスタンス」を選択
  3. 監視したい VM を選択
  4. 「オブザーバビリティ」を選択
  5. 右上にある「推奨アラート」を選択
  6. VM Instance - High CPU Utilization (web) を選択 -> 作成
  7. 「通知チャンネルを使用」で通知したいチャネルを選択 -> 作成

確認

モニタリングにアラートポリシーがあることを確認しましょう

最後に

GCP の VM で CPU 監視する方法を紹介しました
コンソール作業なので UI が変わった場合は手順が変わるので注意してください

しきい値を変えたい場合はポリシーを直接変更すれば OK です

Slack の通知チャネルを作成する方法は以下の参考リンクを参照してください

参考サイト

2025年11月11日火曜日

Ubuntu に uv をインストールする方法

Ubuntu に uv をインストールする方法

概要

グローバルな環境にインストールする方法を紹介します
基本は mcp などに使う場合にこの方法でインストールします

環境

  • Ubuntu 24.04
  • uv 0.9.8

コマンド

curl -LsSf https://astral.sh/uv/install.sh | sh

ログ

downloading uv 0.9.8 x86_64-unknown-linux-gnu
no checksums to verify
installing to /home/devops/.local/bin
  uv
  uvx
everything's installed!

インストールされるパス

ホームディレクトリの .local/bin 配下なので注意してください
PATH が設定されていない場合は PATH に追加しましょう

アップグレードする方法

再度インストールするための curl を叩けば OK です

最後に

pip を使ってインストールする場合はパスや管理方法が変わるので注意しましょう

参考サイト

2025年11月6日木曜日

code-server では Python の拡張だけでは import などを自動でしてくれない

code-server では Python の拡張だけでは import などを自動でしてくれない

概要

VSCode では Pylance の languageServer が使えるのですが code-server では使えません
その影響でメソッドやモジュールの自動インポートはしてくれません

今回はそんな場合の対処方法を紹介します

環境

  • macOS 15.7.1
  • code-server 4.105.1

インストール

  • brew install code-server

起動

  • brew services run code-server

Python 拡張のインストール

  • 左メニュー拡張
  • 検索バーに「Python」
  • ms-python 製の Python 拡張をインストールします

BasedPyright のインストール

  • 左メニュー拡張
  • 検索バーに「basedpyright」
  • detachhead 製の BasedPyright 拡張をインストールします

設定

  • settings.json
{
    "workbench.colorTheme": "Default Dark Modern",
     "basedpyright.analysis.diagnosticMode": "openFilesOnly"
}

動作確認

これでメソッド名の保管時に自動で import してくれるようになります
また型チェックなどもしてくれますが純正の pyright とは違うので注意してください

もし pyright 基準にしたい場合は以下の設定を入れると多少は近くなります

{
    "basedpyright.analysis.typeCheckingMode": "standard"
}

最後に

VSCode では普通にできても code-server ではできないことが多くあるので注意しましょう

参考サイト

2025年11月4日火曜日

OpenSpec 超入門

OpenSpec 超入門

概要

OpenSpec は簡単に言えば Markdown (仕様書) を作成するだけでアプリが開発できてしまうツールです
今までは特にフォーマットもなくプロンプトに命令していましたがそれを定型化したツールになります
今回は簡単なブラウザのタイマーアプリを OpenSpec を使って作ってみました

環境

  • Ubuntu 24.04
  • node 22.21.0
  • openspec 0.13.0
  • code-server v4.104.3

インストール

  • npm install -g @fission-ai/openspec@latest

初期化

まずはプロジェクトを初期化します

  • openspec init

 ????   ?????   ??????  ??  ??   ?????  ?????   ??????   ?????
??  ??  ??  ??  ??      ??? ??  ??      ??  ??  ??      ??
??  ??  ?????   ?????   ?? ???   ????   ?????   ?????   ??
??  ??  ??      ??      ??  ??      ??  ??      ??      ??
 ????   ??      ??????  ??  ??  ?????   ??      ??????   ?????

Welcome to OpenSpec!

Step 1/3

Configure your OpenSpec tooling
Let's get your AI assistants connected so they understand OpenSpec.

Press Enter to continue.

次に使用するLLMを選択します

Step 2/3

Which natively supported AI tools do you use?
Use ↑/↓ to move ・ Space to toggle ・ Enter selects highlighted tool and reviews.

    Natively supported providers (? OpenSpec custom slash commands available)
? ○ Auggie (Augment CLI)
  ○ Claude Code
  ○ Cline
  ○ CodeBuddy Code (CLI)
  ○ Crush
  ○ Cursor
  ○ Factory Droid
  ○ OpenCode
  ○ Kilo Code
  ○ Windsurf
  ○ Codex
  ○ GitHub Copilot
  ○ Amazon Q Developer

    Other tools (use Universal AGENTS.md for Amp, VS Code, GitHub Copilot, …)
  ○ Universal AGENTS.md (always available)

Selected configuration:
  - No natively supported providers selected

今回は Github Copilot を使います

Step 3/3

Review selections
Press Enter to confirm or Backspace to adjust.

? GitHub Copilot

これで初期化が完了です

? GitHub Copilot
? OpenSpec structure created
? AI tools configured

? OpenSpec initialized successfully!

Tool summary:
? Root AGENTS.md stub created for other assistants
? Created: GitHub Copilot
? Skipped: Auggie, Claude Code, Cline, CodeBuddy Code, Crush, Cursor, Factory Droid, OpenCode, Kilo Code, Windsurf, Codex, and Amazon Q Developer

Use `openspec update` to refresh shared OpenSpec instructions in the future.

Next steps - Copy these prompts to GitHub Copilot:
────────────────────────────────────────────────────────────
1. Populate your project context:
   "Please read openspec/project.md and help me fill it out
    with details about my project, tech stack, and conventions"

2. Create your first change proposal:
   "I want to add [YOUR FEATURE HERE]. Please create an
    OpenSpec change proposal for this feature"

3. Learn the OpenSpec workflow:
   "Please explain the OpenSpec workflow from openspec/AGENTS.md
    and how I should work with you on this project"
────────────────────────────────────────────────────────────

project.md
の編集

ここにどんなアプリを作成したいか概要を記載ます とりあえず参考サイトにもあるブラウザで動かくタイマアプリを作成してみます Project Conventions の欄はとりあえず不要なので削除しています

  • vim openspec/project.md
# Project Context

## Purpose
家庭や業務用キッチンで使用できるシンプルで視認性の高いタイマーを、Web ブラウザー経由で提供します。

- 1分、3分、5分を設定するためのボタンを配置します。ボタンを押すと、その時間からカウントダウンが開始されます。
- カウントダウン中は、残り時間を目立つように表示します。
- カウントダウン中に再度ボタンを押すと、タイマーがリセットされ、新たなカウントダウンが開始されます。

## Tech Stack
- HTML5
- JavaScript(ES6)
- CSS3

提案書の作成

code-server の Github Copilot のチャット欄に /openspec-proposal コマンドを入力します

  • /openspec-proposal UIを作成する

すると必要な Markdown ファイルを changes 配下に作成してくれます

ls -lR openspec/changes/ui-timer/
openspec/changes/ui-timer/:
total 12
-rw-r--r-- 1 devops docker  374 Nov  4 13:57 proposal.md
drwxr-xr-x 3 devops docker 4096 Nov  4 13:57 specs
-rw-r--r-- 1 devops docker  341 Nov  4 13:57 tasks.md

openspec/changes/ui-timer/specs:
total 4
drwxr-xr-x 2 devops docker 4096 Nov  4 13:57 timer-ui

openspec/changes/ui-timer/specs/timer-ui:
total 4
-rw-r--r-- 1 devops docker 810 Nov  4 13:57 spec.md

作成されたファイルの中身を確認して仕様に間違いがないか確認しましょう 変更は Copilot Chat にお願いしてもいいですし手動で書き換えても OK です 問題なければ Keep してファイルを保存しましょう

検証する

チャット欄に「検証して」と入力すると勝手に openspec validate コマンドを実行してくれます 内部的には openspec validate ui-timer --strict を実行しています

生成された Markdown に不備があると以下のようにエラーが表示されます ちなみにエラーがよくわからなくても「修正してください」と命令し再度「検証してください」と命令すればそれだけで修正できます

code-server が openspec コマンドを実行できない場合は PATH を確認するか PATH に通っているけど実行できない場合は sudo systemctl restart code-server@USER で再起動しましょう

コードを生成する

あとは /openspec-apply を Copilot Chat に入力すればコードを生成してくれます 各種 html や js が作成されたのが確認できます 内容を確認し問題なければ Keep しましょう

動作確認

これもチャットに「動作確認して」と依頼すればブラウザでアプリを開いて動作確認してくれます

Web アプリがない場合は Python で簡易用のアプリも起動してくれます

アーカイブする

アプリの挙動が問題なければアーカイブします チャット欄に /openspec-archive を入力しましょう すると生成されたコードは archive 配下に移動します

以後アプリに修正を入れたい場合は再度提案を作成し修正するようにしましょう そうすることで changes/archive 配下に日付ごとにディレクトリが作成され提案ごとに履歴管理することができるようになります

最後に

OpenSpec に入門してみました 基本は Markdown を書くだけで開発していく感じです 今まではプロンプトに自分なりの質問やテンプレートを使って質問していたのが完全に形式化したのが OpenSpec かなと思います

新規系は簡単に導入できそうですが既存のアプリにこれを導入するとなるとかなり大変そうです

参考サイト

2025年11月3日月曜日

GCP の Ops エージェントを完全にアンインストールする方法

GCP の Ops エージェントを完全にアンインストールする方法

概要

コンソールから Ops エージェントをインストールするとなぜかアンインストールしても勝手に再インストールしてしまうので完全にアンインストールする方法を紹介します

環境

  • Ops エージェント 2.60.0
  • Ubuntu 22.04

まずは purge

  • sudo apt purge google-cloud-ops-agent

設定ファイルの削除

purge した場合は基本ないはずですが一応確認します

  • sudo ./add-google-cloud-ops-agent-repo.sh --remove-repo
  • sudo rm /etc/apt/sources.list.d/google-cloud-monitoring.list

/opt 配下にも何も無いことを確認します

VM のカスタムメタデータの削除

これがあるとエージェントが勝手に再インストールしている可能性があるので削除します
コンソールでも OK です

  • gcloud compute instances remove-metadata YOUR_INSTANCE_NAME --zone=YOUR_ZONE --keys=enable-osconfig

VM のラベルの削除

  • VM インスタンス -> 選択 -> 編集 -> ラベルを管理 -> goog-ops-agent-policy のラベルを削除

デフォルト

以下のエージェントだけになっていればデフォルトの状態です

dpkg -l | grep google
ii  google-compute-engine            20230808.00-0ubuntu1~22.04.1            all          Google Compute Engine guest environment.
ii  google-compute-engine-oslogin    20231004.00-0ubuntu1~22.04.3            amd64        Google Compute Engine OS Login
ii  google-guest-agent               20231004.02-0ubuntu1~22.04.5            amd64        Google Compute Engine Guest Agent
ii  google-osconfig-agent            20240524.03-0ubuntu2~22.04.1            amd64        Google OS Config Agent

動作確認

最終的にはコンソールでオブザーバビリティを確認したい際に「Opsエージェントをインストール」が表示されていれば完全に削除できています
インストール中の保留中やメトリクスが送信できないような文言の場合はまだタグやラベルが残っている可能性があります

最後に

GCP の小さめのインスタンスに Ops エージェントをインストールすると CPU とメモリを食い尽くして他のプロセスが全く動かなくなるので安易にインストールするのはやめておきましょう

2025年11月1日土曜日

Gitlab CI で nikto を動かし DAST 的なことをする

Gitlab CI で nikto を動かし DAST 的なことをする

概要

前回 Gitlab CI のサービスを使って Web アプリを起動しそのアプリにテストする方法を紹介しました
今回は nikto を実行してみます

環境

  • Gitlab 18.2.6
  • Gitlab Runner 18.2.2
  • Python 3.12.11

.gitlab-ci.yml

stages:
  - build   # ビルドステージ
  - test    # テストステージ
  - pentest # ペネトレーションテストステージ

build:
  stage: build
  image:
    name: moby/buildkit:rootless  # BuildKit を使用するための Docker イメージ
    entrypoint: [""]  # デフォルトのエントリーポイントを無効化
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox  # BuildKit の設定
  before_script:
    # Docker 認証情報を設定
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    # BuildKit を使用して Docker イメージをビルドし、GitLab Container Registry にプッシュ
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

app:
  stage: test
  services:
    # ビルドした Docker イメージをサービスとして起動
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: testapp  # サービスに "testapp" というホスト名を割り当て
  script:
    # サービスが起動するまで待機
    - echo "Waiting for the service to be ready..."
    - >
      for i in {1..30}; do
        curl -s http://testapp:5000 && break || sleep 1;  # サービスが応答するまで最大 30 秒間リトライ
      done
    - echo "Service is ready."  # サービスが起動したことを確認

validate:
  stage: test
  image: curlimages/curl:latest  # curl を使用するための軽量イメージ
  services:
    # ビルドした Docker イメージを再度サービスとして起動
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: testapp  # サービスに "testapp" というホスト名を割り当て
  script:
    # サービスに対してレスポンスを検証
    - echo "Running validation tests..."
    - |
      RESPONSE=$(curl -s http://testapp:5000)  # サービスにリクエストを送信しレスポンスを取得
      if [ "$RESPONSE" = "Hello, World!" ]; then
        echo "Validation passed!"  # レスポンスが期待通りの場合
      else
        echo "Validation failed: Expected 'Hello, World!' but got '$RESPONSE'"  # レスポンスが期待と異なる場合
        exit 1  # ジョブを失敗させる
      fi

pentest:
  stage: pentest
  image:
    name: ghcr.io/sullo/nikto:latest
    entrypoint: [""]
  services:
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: testapp
  script:
    - echo "Running Nikto penetration test..."
    - mkdir -p reports  # レポートを保存するディレクトリを作成
    - nikto.pl -h http://testapp:5000 -F htm -o reports/report.html
    - echo "Nikto scan completed. Report saved to reports/report.html."
  artifacts:
    paths:
      - reports/report.html  # レポートをアーティファクトとして保存
    expire_in: 1 week  # レポートの保存期間を 1 週間に設定

最後に

Gitlab Ultimate に登録していない場合に DAST が使えないので自分で .gitlab-ci.yml にペネトレーションテスト的なことを記載する必要があります
またレポートも自分で保存する必要があるので artifact を使って保存するようにしましょう

2025年10月31日金曜日

Gitlab CI で service を使って Web アプリを起動し簡単なテストを行う

Gitlab CI で service を使って Web アプリを起動し簡単なテストを行う

概要

イメージをビルドしビルドしたイメージからアプリを起動しそのアプリに対してテストするみたいな流れを GItlab CI でやってみます

今回 Web アプリは Python を使っていますが好きな言語のアプリで OK です

環境

  • Gitlab 18.2.6
  • Gitlab Runner 18.2.2
  • Python 3.12.11

Pipfile

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
flask = "*"

[dev-packages]

[requires]
python_version = "3.12"

app.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello, World!"

if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")

Dockerfile

FROM python:3.12.11-slim-bookworm

# 作業ディレクトリを設定
WORKDIR /app

# 必要なファイルをコンテナにコピー
COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock
COPY app.py app.py

# 必要なPythonパッケージをインストール
RUN pip install pipenv
RUN pipenv install

# アプリケーションを起動
CMD ["pipenv", "run", "python", "app.py"]

.gitlab-ci.yml

stages:
  - build  # ビルドステージ
  - test   # テストステージ

build:
  stage: build
  image:
    name: moby/buildkit:rootless  # BuildKit を使用するための Docker イメージ
    entrypoint: [""]  # デフォルトのエントリーポイントを無効化
  variables:
    BUILDKITD_FLAGS: --oci-worker-no-process-sandbox  # BuildKit の設定
  before_script:
    # Docker 認証情報を設定
    - mkdir -p ~/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > ~/.docker/config.json
  script:
    # BuildKit を使用して Docker イメージをビルドし、GitLab Container Registry にプッシュ
    - |
      buildctl-daemonless.sh build \
        --frontend dockerfile.v0 \
        --local context=. \
        --local dockerfile=. \
        --output type=image,name=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA,push=true

app:
  stage: test
  services:
    # ビルドした Docker イメージをサービスとして起動
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: testapp  # サービスに "testapp" というホスト名を割り当て
  script:
    # サービスが起動するまで待機
    - echo "Waiting for the service to be ready..."
    - >
      for i in {1..30}; do
        curl -s http://testapp:5000 && break || sleep 1;  # サービスが応答するまで最大 30 秒間リトライ
      done
    - echo "Service is ready."  # サービスが起動したことを確認

validate:
  stage: test
  image: curlimages/curl:latest  # curl を使用するための軽量イメージ
  services:
    # ビルドした Docker イメージを再度サービスとして起動
    - name: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
      alias: testapp  # サービスに "testapp" というホスト名を割り当て
  script:
    # サービスに対してレスポンスを検証
    - echo "Running validation tests..."
    - |
      RESPONSE=$(curl -s http://testapp:5000)  # サービスにリクエストを送信しレスポンスを取得
      if [ "$RESPONSE" = "Hello, World!" ]; then
        echo "Validation passed!"  # レスポンスが期待通りの場合
      else
        echo "Validation failed: Expected 'Hello, World!' but got '$RESPONSE'"  # レスポンスが期待と異なる場合
        exit 1  # ジョブを失敗させる
      fi

最後に

Gitlab には Ultimate ライセンスを使っていると DAST を使えるのですがそうじゃないライセンスだと使えません
そんな場合に自分で DAST 的なことをする必要があるので今回のような流れで実装できます

次回は nikto を組み合わせてセキュリティテストしてみます

2025年10月26日日曜日

LetsEncrypt で 0001 や 0002 などの証明書が取得できてしまった場合の解決方法

LetsEncrypt で 0001 や 0002 などの証明書が取得できてしまった場合の解決方法

概要

前の設定が残っている状態で新規に証明書を発行すると連番が付いてしまいます
こうなると /etc/letsencrypt/live/ 配下で証明書を管理するディレクトリにも 0001 などが付き色々と面倒なので削除する方法を紹介します

なお過去に紹介した docker compose バージョンの certbot を使います

環境

  • Ubuntu 22.04
  • certbot 5.1.0

削除する

サフィックスが付いている証明書および付いていないやつも削除しましょう

  • docker compose run --rm certbot delete --cert-name your-domain.com-0001
  • docker compose run --rm certbot delete --cert-name your-domain.com-0002
  • docker compose run --rm certbot delete --cert-name your-domain.com

これで再度取得すればサフィックスなし版が取得できます
なおサフィックス版があるかないかは certificates サブコマンドを使って確認しましょう

  • docker compose run --rm certbot certificates

最後に

ホスト側の証明書をコンテナにマウントして使う場合にサフィックスがあるといろいろ面倒なのでなし版で取得するようにしましょう

参考サイト

2025年10月25日土曜日

Microsoft Graph API を使って個人の OneDrive を操作する方法

Microsoft Graph API を使って個人の OneDrive を操作する方法

概要

前回 OneDrive にアクセスできるトークンの取得まで行いました
今回はそのトークンを使って実際に OneDrive にアクセスします

環境

  • macOS 15.7.1
  • Python 3.12.11

サンプルコード

import json
import os

import requests

TENANT_ID = "xxx"
CLIENT_ID = "xxx"
CLIENT_SECRET = "xxx"
REDIRECT_URI = "http://localhost:8080"
TOKEN_FILE = "onedrive_token.json"
GRAPH_API_BASE = "https://graph.microsoft.com/v1.0"


# =========================
# トークン管理
# =========================
def load_tokens():
    """保存済みトークンを読み込む"""
    if not os.path.exists(TOKEN_FILE):
        raise FileNotFoundError(f"{TOKEN_FILE} が存在しません。先にトークン取得を実行してください。")
    with open(TOKEN_FILE, "r", encoding="utf-8") as f:
        return json.load(f)


def save_tokens(tokens):
    """トークンを保存"""
    with open(TOKEN_FILE, "w", encoding="utf-8") as f:
        json.dump(tokens, f, indent=2)


def refresh_access_token(refresh_token, client_id, client_secret, redirect_uri):
    """リフレッシュトークンからアクセストークンを更新"""
    token_url = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
    data = {
        "client_id": client_id,
        "client_secret": client_secret,
        "redirect_uri": redirect_uri,
        "grant_type": "refresh_token",
        "refresh_token": refresh_token
    }
    res = requests.post(token_url, data=data)
    res.raise_for_status()
    return res.json()


# =========================
# OneDrive 操作
# =========================
def get_headers(access_token):
    return {"Authorization": f"Bearer {access_token}"}


def upload_file(access_token, local_path, remote_name):
    """小さなファイルをアップロード"""
    url = f"{GRAPH_API_BASE}/me/drive/root:/{remote_name}:/content"
    with open(local_path, "rb") as f:
        res = requests.put(url, headers=get_headers(access_token), data=f)
    if res.status_code in (200, 201):
        print(f"✅ アップロード成功: {remote_name}")
    else:
        print(f"❌ アップロード失敗 ({res.status_code}): {res.text}")


def list_files(access_token):
    """ルートディレクトリのファイル一覧取得"""
    url = f"{GRAPH_API_BASE}/me/drive/root/children"
    res = requests.get(url, headers=get_headers(access_token))
    if res.status_code == 200:
        items = res.json().get("value", [])
        print("📄 OneDrive ファイル一覧:")
        for item in items:
            print(f" - {item['name']}")
    else:
        print(f"❌ 取得失敗 ({res.status_code}): {res.text}")


def delete_file(access_token, remote_name):
    """ファイル削除"""
    url = f"{GRAPH_API_BASE}/me/drive/root:/{remote_name}"
    res = requests.delete(url, headers=get_headers(access_token))
    if res.status_code in (204, 200):
        print(f"🗑️ 削除成功: {remote_name}")
    else:
        print(f"❌ 削除失敗 ({res.status_code}): {res.text}")


# =========================
# メイン
# =========================
if __name__ == "__main__":
    tokens = load_tokens()
    access_token = tokens["access_token"]

    # 必要に応じてリフレッシュ
    tokens = refresh_access_token(tokens["refresh_token"], CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
    access_token = tokens["access_token"]
    save_tokens(tokens)

    # サンプルファイル作成
    sample_file = "test.txt"
    with open(sample_file, "w", encoding="utf-8") as f:
        f.write("Hello OneDrive via Graph API!")

    # アップロード
    upload_file(access_token, sample_file, "test.txt")

    # ファイル一覧
    list_files(access_token)

    # 削除(必要であればコメント解除)
    delete_file(access_token, "test.txt")

ポイント

  • トークンが切れた場合にリフレッシュトークを使って再度トークンを取得します
  • 毎回行っても問題ないので実際は毎回リフレッシュトークを使って問い合わせたほうがいいです
  • トークンをファイルで保存していますが漏洩すると大変なので管理には注意しましょう

最後に

Python で OneDrive の操作をしてみました
認証が面倒ですが一度認証してしまえばあとはスクリプトを実行するだけです

アプリのシークレットに期限があるのでその期限がすぎたら再度ブラウザを開いてトークンファイルを更新する必要があります