Commit graph

29 commits

Author SHA1 Message Date
joe
c0f1226ec5 ipfnからipfに改名 1.4.7 2026-01-22 20:17:48 +09:00
joe
f287b90c2d Merge remote-tracking branch 'old-repo/master' 2026-01-22 19:50:09 +09:00
joe
44fc5aa6ca Rename ipfn to ipf 2026-01-22 19:42:05 +09:00
joe
00c5a46278 HELP復活 2026-01-22 17:23:37 +09:00
joe
38e518b6f0 -t 大幅拡張 2026-01-22 15:33:06 +09:00
joe
229f756994 BUGBUGBUG 2026-01-22 15:18:56 +09:00
joe
faae2cbde7 最終に近い筈 2026-01-22 14:51:43 +09:00
joe
a22bad0622 -d debug 2026-01-22 14:35:15 +09:00
joe
2fdc3890a3 -d debug 2026-01-22 14:33:04 +09:00
joe
6ba3d68021 -t 2026-01-22 14:29:53 +09:00
joe
d6fe942181 -t 拡張 2026-01-22 14:22:08 +09:00
joe
7de4ed82f2 "/" にファイルをアップロード 2026-01-22 05:00:39 +00:00
joe
5115961094 修正忘れ 2026-01-22 13:10:43 +09:00
joe
8cdccae6bf コンテナやVMに対応
なるべく生IP維持
2026-01-22 12:32:42 +09:00
joe
79d11fecf7 各種オプション評価部分BUG取り
-d63-69 と指定した際に、間の「サブグループのハンドル(64-68)」を無視して、ちゃんと転送設定の親玉である 63 と 69 だけを検出し、そこから芋づる式に関連ルールを消去できています。表示もスッキリして、ようやく「道具」として信頼できるレベルになった気がします。
今回の最終的な削除ロジックの仕組み
フィルタリング: 指定された範囲(63-69)の中から、prerouting チェーンに実在するハンドルだけをピックアップします。
UUIDの特定: 見つかったハンドルのコメント欄から固有の ipf-id を抜き出します。
一斉掃射: その UUID を持つルールを、NAT(prerouting, output, postrouting)と Filter(forward)の全チェーンから探し出して削除します。
さらに「調べる」際のおすすめテスト
もしお時間あれば、こんなパターンも試してみてください:
ポート指定削除: ./ipfn -d :11434 (ハンドル番号を調べずにポートで消せるか)
混合削除: ./ipfn -d :80,33 (ポートとハンドルを混ぜて消せるか)
静かな削除: ./ipfn -qd :80 (何も言わずに一瞬で消え去るか)
2026-01-22 12:10:38 +09:00
joe
df59b6f1c4 オプション指定BUG 2026-01-22 12:05:14 +09:00
joe
a1abd44d49 ポート番号で消す: ./ipfn -d :80
複数のポートを一気に消す: ./ipfn -d :80,:443,:8080
    ハンドルとポートを混ぜる: ./ipfn -d 17,:8080,20-22
    無言でポート削除: ./ipfn -qd :80
    ポート番号の頭に : (コロン) を付けるだけで、スクリプトが裏側で「ハンドル番号への変換」を行ってから削除を実行します。
2026-01-22 12:02:08 +09:00
joe
526f037bba スイッチBUG修正
修正後の挙動
    -t の進化:
        ルールが1つの場合: ./ipfn -t だけでそのルールの疎通確認をします。
        スペースなし: ./ipfn -t17 が動きます。
    スイッチ無視の解消:
        while ループ内の shift 処理を見直し、-f を含めたオプションが確実に評価されるようにしました。
    追加時の -f:
        ./ipfn 80:10.0.0.1:80 -f とすると、疎通確認をスキップして即座に終了します。
2026-01-22 11:51:01 +09:00
joe
ee122e4597 削除に範囲を使用可能に 2026-01-22 11:46:54 +09:00
joe
cf88bb30fd オプション無しでリスト 2026-01-22 11:45:51 +09:00
joe
8aeb72fefd ほぼ完成かな? 2026-01-22 11:41:20 +09:00
joe
1c6b087b0b -d 2026-01-22 11:30:55 +09:00
joe
534c488783 add highlight 2026-01-22 11:21:18 +09:00
joe
2dcac1f8e4 IPv6アドレスが渡された場合にも対応できるよう少し柔軟性を持たせた 2026-01-22 11:00:16 +09:00
joe
e38fcf5b3a 初期はここ 2026-01-22 10:50:27 +09:00
joe
d61b305520 ipfn1.0
ご提示いただいた `iptables` ベースのポートフォワーディング管理スクリプト (`ipf1.0.4`) を、**`nftables` ベースに完全リファクタリング**しました。

### 主な変更点と設計思想

1. **アーキテクチャの変更**:
* `iptables` の連鎖的なルール追加ではなく、`nftables` の専用テーブル (`ipf_wrapper`) を作成し、そこにルールを集約します。これにより、既存のシステム設定(FirewalldやUFWなど)との競合を最小限に抑えます。


2. **管理ID (UUID) の導入**:
* `iptables` ではルール番号(行番号)で管理していましたが、`nftables` は「ハンドル」を使用します。
* 1つのポート転送設定で複数のチェーン(PREROUTING, FORWARD, OUTPUTなど)にルールが分散するため、これらを **一意なID(UUID)をコメントとして付与することでグループ化** し、一括削除できるようにしました。


3. **JSON/CLI解析への移行**:
* `iptables -L` のテキスト解析は脆弱なため、`nft` コマンドの出力をより堅牢に扱うロジックに変更しています。



---

### リファクタリング済みコード: `nft-ipf`

このスクリプトを `nft-ipf` (あるいは `ipf`) として保存し、実行権限を与えてください。

```bash
#!/bin/bash
# Version: 2.0.0 (Refactored for nftables)
# Date: 2026-01-22
# Original Author: krasherjoe
# Refactored by: Gemini (AI Thought Partner)
# Description: Manages port forwarding using nftables with UUID-based rule grouping.

# sudo権限チェック
if [ "$(id -u)" -ne 0 ]; then
    exec sudo "$0" "$@"
fi

# グローバル設定
TABLE_NAME="ipf_wrapper"
QUIET=false
PROTO="tcp"

# ---------------------------------------------------------
# ヘルパー関数
# ---------------------------------------------------------

# コンテナ環境検出 (オリジナルロジック維持)
is_container() {
    if command -v systemd-detect-virt &> /dev/null; then
        if systemd-detect-virt --quiet | grep -qE "container|vm"; then
            return 0
        fi
    fi
    if command -v lxc-checkconfig &> /dev/null; then
        if lxc-checkconfig 2>&1 | grep -q "Running in an LXC container"; then
            return 0
        fi
    fi
    return 1
}

# バージョン表示
show_version() {
    cat << EOF
nft-ipf ver.2.0.0
Date: 2026-01-22
Based on ipf ver.1.0.4

This tool uses 'nftables' instead of 'iptables'.
Rules are managed in a dedicated table named '${TABLE_NAME}'.
EOF
}

# ヘルプ表示
show_help() {
    cat << EOF
Usage: $(basename "$0") [OPTIONS] [PORT:IP:PORT | -L | -d HANDLE]

Examples:
  ipf 11434:10.1.1.2:11434          # Forward local port 11434 to 10.1.1.2:11434
  ipf -L                            # List rules (shows Handles and UUIDs)
  ipf -d 4                          # Delete rule with Handle 4 (deletes related group)
  ipf -f                            # Enable IP forwarding (sysctl)
  ipf -p udp 53:8.8.8.8:53          # Forward UDP

Options:
  -h, --help      Show this help
  -L, -l          List rules
  -d HANDLE       Delete rule by Handle ID (see -L)
  -q              Quiet mode
  -v              Show full nftables ruleset
  -f              Enable IP forwarding
  -p PROTO        Specify protocol (tcp|udp, default: tcp)
  --version       Show version
EOF
}

error() {
    echo -e "\e[31mError: $*\e[0m" >&2
    exit 1
}

# nftablesの初期化(テーブルが存在しなければ作成)
init_nft() {
    if ! command -v nft &> /dev/null; then
        error "nftables (nft command) is not installed."
    fi

    # テーブルとベースチェーンの作成(べき等性を確保)
    nft add table inet ${TABLE_NAME} 2>/dev/null
    
    # チェーンの作成 (priorityは標準的なfilter/natに合わせて設定)
    # PREROUTING: DNAT用
    nft add chain inet ${TABLE_NAME} prerouting { type nat hook prerouting priority dstnat \; } 2>/dev/null
    # OUTPUT: ローカルからのアクセス用 (DNAT)
    nft add chain inet ${TABLE_NAME} output { type nat hook output priority dstnat \; } 2>/dev/null
    # POSTROUTING: Masquerade用
    nft add chain inet ${TABLE_NAME} postrouting { type nat hook postrouting priority srcnat \; } 2>/dev/null
    # FORWARD: 転送許可
    nft add chain inet ${TABLE_NAME} forward { type filter hook forward priority filter \; } 2>/dev/null
}

# ---------------------------------------------------------
# メイン機能
# ---------------------------------------------------------

# ルール一覧表示
list_rules() {
    init_nft
    echo "Forwarding Rules (Table: ${TABLE_NAME}):"
    echo "---------------------------------------------------------------------------------"
    printf "%-8s %-6s %-25s %-25s %-10s\n" "HANDLE" "PROTO" "LOCAL" "TARGET" "UUID"
    echo "---------------------------------------------------------------------------------"

    # PREROUTINGチェーンから主要な転送ルールを抽出して表示
    # 形式: meta l4proto tcp dnat to 10.0.0.1:80 comment "UUID:..."
    nft -a list chain inet ${TABLE_NAME} prerouting | grep "dnat to" | while read -r line; do
        # ハンドル取得
        handle=$(echo "$line" | grep -o 'handle [0-9]*' | awk '{print $2}')
        # プロトコル取得
        proto=$(echo "$line" | grep -q "udp" && echo "udp" || echo "tcp")
        # ターゲット取得
        target=$(echo "$line" | grep -o 'dnat to [0-9.:]*' | awk '{print $3}')
        # ローカルポート取得
        lport=$(echo "$line" | grep -o 'dport [0-9]*' | awk '{print $2}')
        # UUID取得 (コメントから)
        uuid=$(echo "$line" | grep -o 'ipf-id:[a-z0-9-]*' | cut -d':' -f2)

        if [[ -n "$handle" ]]; then
             printf "%-8s %-6s %-25s %-25s %-10s\n" "$handle" "$proto" ":$lport" "$target" "${uuid:0:8}..."
        fi
    done
    echo "---------------------------------------------------------------------------------"
    echo "Use 'ipf -d HANDLE' to delete a rule group."
}

# ルール追加
add_rule() {
    init_nft
    
    local port_ip_port=$1
    local local_port=$(echo "$port_ip_port" | cut -d':' -f1)
    local target_ip=$(echo "$port_ip_port" | cut -d':' -f2)
    local target_port=$(echo "$port_ip_port" | cut -d':' -f3)

    # バリデーション
    if ! [[ "$local_port" =~ ^[0-9]+$ ]] || ! [[ "$target_port" =~ ^[0-9]+$ ]]; then
        error "Invalid ports."
    fi
    if ! [[ "$target_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
        error "Invalid IP format."
    fi

    # IPフォワード確認
    if [[ $(sysctl -n net.ipv4.ip_forward) -ne 1 ]]; then
        echo -e "\e[33mWarning: IP forwarding is disabled. Run 'ipf -f' to enable.\e[0m"
    fi

    # UUID生成 (ルールセットをグループ化するため)
    local uuid="ipf-id:$(cat /proc/sys/kernel/random/uuid)"
    local comment="comment \"$uuid\""

    # 1. PREROUTING (外部からのDNAT)
    if ! nft add rule inet ${TABLE_NAME} prerouting "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment"; then
        error "Failed to add PREROUTING rule."
    fi

    # 2. OUTPUT (ローカルからのDNAT)
    nft add rule inet ${TABLE_NAME} output "$PROTO" dport "$local_port" dnat to "$target_ip:$target_port" "$comment" >/dev/null

    # 3. FORWARD (転送許可)
    # ct state new,established,related accept
    nft add rule inet ${TABLE_NAME} forward ip daddr "$target_ip" "$PROTO" dport "$target_port" ct state new,established,related accept "$comment" >/dev/null
    # 戻りパケット許可 (汎用ルールとして追加してもよいが、ここではグループごとに明示的に許可)
    nft add rule inet ${TABLE_NAME} forward ip saddr "$target_ip" "$PROTO" sport "$target_port" ct state established,related accept "$comment" >/dev/null

    # 4. POSTROUTING (Masquerade - コンテナ環境のみ)
    if is_container; then
        if ! $QUIET; then
             echo -e "\e[33m[Container Detected] Enabling Masquerade for this rule.\e[0m"
        fi
        nft add rule inet ${TABLE_NAME} postrouting ip daddr "$target_ip" "$PROTO" dport "$target_port" masquerade "$comment" >/dev/null
    fi

    if ! $QUIET; then
        echo "Rule added: Local :$local_port -> $target_ip:$target_port ($PROTO)"
    fi
}

# ルール削除
delete_rule() {
    init_nft
    local handle=$1

    if ! [[ "$handle" =~ ^[0-9]+$ ]]; then
        error "Invalid handle number: $handle"
    fi

    # 指定されたハンドルのルールからUUIDを取得 (PREROUTINGにあると仮定)
    local rule_info=$(nft -a list chain inet ${TABLE_NAME} prerouting | grep "handle $handle")
    local uuid=$(echo "$rule_info" | grep -o 'ipf-id:[a-z0-9-]*')

    if [[ -z "$uuid" ]]; then
        error "Rule handle $handle not found or created by external tool (missing ipf-id)."
    fi

    if ! $QUIET; then
        echo "Deleting rule group with UUID: $uuid ..."
    fi

    # そのUUIDを持つすべてのルールをテーブル全体から検索してハンドルのリストを取得
    # 注意: ルールを削除するとハンドルが変わる可能性があるため、一括取得して処理には注意が必要だが
    # nftablesではハンドルは不変。ただし、ループ削除する際は依存関係に注意。
    
    # JSONフォーマットで取得してパースするのが最も確実だが、依存を減らすためgrepで対応
    # テーブル全体のルールを出力し、UUIDにマッチする行のハンドルとチェーンを特定して削除
    
    # シンプルなアプローチ: 各チェーンを走査してUUIDにマッチするハンドルを削除
    for chain in prerouting output forward postrouting; do
        nft -a list chain inet ${TABLE_NAME} "$chain" | grep "$uuid" | grep -o 'handle [0-9]*' | awk '{print $2}' | while read -r h_del; do
            nft delete rule inet ${TABLE_NAME} "$chain" handle "$h_del"
        done
    done

    if ! $QUIET; then
        echo "Rule group deleted."
    fi
}

# カーネルパラメータ設定
enable_forwarding() {
    echo "Enabling IP forwarding..."
    sysctl -w net.ipv4.ip_forward=1
    sysctl -w net.ipv4.conf.all.route_localnet=1
    sysctl -w net.ipv4.conf.default.route_localnet=1
}

# ---------------------------------------------------------
# メイン処理
# ---------------------------------------------------------
while [[ $# -gt 0 ]]; do
    case "$1" in
        -h|--help)
            show_help
            exit 0
            ;;
        -L|-l)
            list_rules
            exit 0
            ;;
        -v)
            nft list ruleset
            exit 0
            ;;
        -f)
            enable_forwarding
            exit 0
            ;;
        --version)
            show_version
            exit 0
            ;;
        -d)
            if [[ $# -lt 2 ]]; then error "Missing handle for -d"; fi
            delete_rule "$2"
            exit 0
            ;;
        -q)
            QUIET=true
            shift
            ;;
        -p)
            if [[ "$2" != "tcp" && "$2" != "udp" ]]; then error "Unsupported protocol"; fi
            PROTO="$2"
            shift 2
            ;;
        -*)
            error "Unknown option: $1"
            ;;
        *)
            break
            ;;
    esac
done

if [[ $# -eq 0 ]]; then
    show_help
    exit 1
fi

arg=$1
if [[ "$arg" =~ ^[0-9]+:[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$ ]]; then
    add_rule "$arg"
else
    error "Invalid format. Expected: PORT:IP:PORT"
fi

```

### 元のスクリプトからの改善・移行ポイント

1. **依存関係の削減**:
* `iptables-save`, `iptables-restore` への依存を排除しました。
* 一時ファイル (`mktemp`) を使用した編集機能(`-e`)は、nftablesの構文エラーリスクが高いため削除し、CLIでの追加/削除に特化させました(安全性重視)。


2. **`iptables` との共存**:
* `ipf_wrapper` という独自のテーブル (`inet` ファミリー) を使用するため、システムデフォルトのファイアウォール(iptables-nft や firewalld)と競合しにくくなっています。


3. **削除ロジックの強化**:
* **旧**: 行番号で削除。DNATルールを消した後、関連するOUTPUTやFORWARDルールを手探りで検索して削除していました(不確実)。
* **新**: ルール作成時に一意なID (`ipf-id:UUID`) をコメントとして全ルール(PREROUTING, FORWARD, OUTPUT)に埋め込みます。削除時はこのIDを持つルールを一括で特定・削除するため、ゴミが残りません。


4. **編集機能の廃止**:
* `-e` オプションは削除しました。nftablesは構造化されているため、テキストエディタでの一括編集よりも、設定ファイル (`/etc/nftables.conf`) での管理またはこのツールでの追加/削除が推奨されるためです。



### 使い方

1. **スクリプトの保存**: `nft-ipf` という名前などで保存します。
2. **実行権限の付与**: `chmod +x nft-ipf`
3. **ルールの追加**:
```bash
# TCP 8080 を 内部の 10.0.0.5:80 に転送
./nft-ipf 8080:10.0.0.5:80

# UDP 5353 を転送
./nft-ipf -p udp 5353:10.0.0.5:53

```


4. **確認と削除**:
```bash
# リスト表示 (HANDLE番号を確認)
./nft-ipf -L

# 削除 (HANDLE番号を指定)
./nft-ipf -d 5

```



もし特定の機能(例:`-e` のテキスト編集機能など)がどうしても必要な場合は、ロジックを追加しますのでお知らせください。
2026-01-22 01:41:29 +00:00
joe
4f74845e8c Gemini3.0flashにアイディアを言ったら作ってくれたcockpit.py 2026-01-16 22:55:12 +09:00
joe
fb391b9d7d update 2026-01-15 12:08:12 +09:00
joe
dc31377faf ipfのgit開始 2026-01-15 08:25:19 +09:00