
この設定を使用して、期待どおりに機能するwg-quick
LinuxブックワームコンピュータのデフォルトTable=off
以外のインターフェイスを正常に設定しました。wg1
curl ifconfig.co
私の実際のIPを返します。curl --interface wg1 ifconfig.co
私のWireguardサーバーのIPを返します。
wg1
さて、ルートレスPodmanコンテナを実行して外部の世界と通信できるかどうかを知りたいです。デフォルトでは、Podmanコンテナは私のeth0
インターフェースを使用して、podman run -it docker.io/curlimages/curl:latest ifconfig.co
私が望む実際のIPを返します。ただし、コンテナ内でホストと同じ操作を実行する方法が見つかりません。
逃げられると思いましたがpodman run --network=slirp4netns:outbound_addr=wg1 -it docker.io/curlimages/curl:latest ifconfig.co
また来ましたcurl: (6) Could not resolve host: ifconfig.co
。 ifconfig.coのIPを使用するとうまく機能しないため、これはDNSの問題ではないようですcurl: (7) Failed to connect to 172.64.110.32 port 80 after 75840 ms: Couldn't connect to server
。
私はPodmanネットワークやPodなどを自分で設定する必要があると思いましたが、私が見つけた多くのチュートリアルに落ちて欲しい結果が得られませんでした。curl
ルート内でワイヤガードサーバーを介して何も得られませんでした。コンテナ。
助けてくれてありがとう。
答え1
背景
slirp4netnsルーティングの代わりにソケットを使用するネットワークシミュレータです。コンテナの観点からは、ルーティングはゲートウェイによって実行されますが、ホストは実際にルーティングを実行しません。slirp4netns発信トラフィックを処理するには、コンテナで検出されたフローごとにTCPおよびUDPソケットを動的に開くだけです。
トレースの使用slirp4netns、現在、カールが何をしているのかわかりません。インターフェイスへのバインディングを使用しません。setsockopt(..., SO_BINDTODEVICE, ...)
代わりに、ソケットはインターフェイスのアドレスにバインドされます。したがって、このパスを必ずしも使用する必要はありません。ワーキンググループ1 (SO_BINDTODEVICE
残りのパスがない場合は、デフォルトパスが追加されているかのようにインターフェイスも強制的に使用されます。WireGuardなどのレイヤ3インターフェイスの場合は問題ありません。さらに発生します)使用してわかるパスがないため失敗します。ワーキンググループ1。
包装紙
一つLD_PRELOAD
ラッパーは機能不足を解決するために使用できます。クスリップ。変化クスリップ~のslirp_bind_outbound()
行動が良い選択だったはずですが、変更を選択しましたbind(2)
:壊れやすいですが、実装が簡単です。
ラッパーは、環境変数として提供されたIPアドレス(ターゲットインターフェイスのアドレスである必要があります)にバインドしようとしたことを確認します。この場合、ソケットはOPの期待される動作を使用してslirp4netns
インターフェイスにさらにバインドされます。SO_BINDTODEVICE
,ホストの行動と同様にカールコマンドが実行されています。もちろん、slirp_bind_outbound()
呼び出し以外の理由でバインディングが発生した場合、中断が予想されます。
指示する
最小限の開発環境が必要です。湾岸協力協議会。次のコマンドを使用してコンパイルします。
gcc -o bindtodevicewrapper.so bindtodevicewrapper.c -shared -fPIC -O2 -ldl
次のファイルbindtodevicewrapper.c
:
#define _GNU_SOURCE
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
static int init=0;
static int (*orig_bind)(int, const struct sockaddr *, socklen_t);
static struct in_addr ipv4bindtodevice;
static struct in6_addr ipv6bindtodevice;
static char devname[IFNAMSIZ];
if (!init) {
if ((orig_bind=dlsym(RTLD_NEXT,"bind")) == NULL) {
return -1;
}
memset(&ipv4bindtodevice, 0xff, sizeof ipv4bindtodevice); /* invalid for binding */
memset(&ipv6bindtodevice, 0xff, sizeof ipv6bindtodevice); /* invalid for binding */
if (getenv("WRAPPER_BINDTODEVICE")!=NULL) {
strncpy(devname, getenv("WRAPPER_BINDTODEVICE"), sizeof devname);
if(getenv("WRAPPER_INET")!=NULL)
inet_pton(AF_INET, getenv("WRAPPER_INET"),&ipv4bindtodevice);
if(getenv("WRAPPER_INET6")!=NULL)
inet_pton(AF_INET6, getenv("WRAPPER_INET6"),&ipv6bindtodevice);
}
init=1;
}
if (devname != NULL) {
if (addr->sa_family == AF_INET) {
if (!memcmp(&((struct sockaddr_in *)addr)->sin_addr, &ipv4bindtodevice, sizeof ipv4bindtodevice))
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, devname, strlen(devname)+1);
}
else if (addr->sa_family == AF_INET6) {
if (!memcmp(&((struct sockaddr_in6 *)addr)->sin6_addr, &ipv6bindtodevice, sizeof ipv6bindtodevice))
setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, devname, strlen(devname)+1);
}
}
return orig_bind(sockfd, addr, addrlen);
}
jq
インターフェイスからIPアドレスを推測することは避けてください。それ以外の場合は手動で実行できます(たとえばWRAPPER_INET=10.5.0.2
、10.5.0.2がインターフェイスのアドレスである場合)。ワーキンググループ1。これは\
、読みやすくするために、次のコマンドで区切られた単一行です(単一コマンドのエクスポートされた変数のみを含む)。
WRAPPER_BINDTODEVICE=wg1 \
WRAPPER_INET=$(ip -4 -json addr show dev wg1 | jq -r '.[].addr_info[0].local') \
LD_PRELOAD=./bindtodevicewrapper.so \
podman run --network=slirp4netns:outbound_addr=wg1 -it docker.io/curlimages/curl:latest ifconfig.co
DNSなしでIPv4を使用してテストしました(DNSなしでテストしました)。以下を使用するときは、WRAPPER_INET6=...
IPv6と一緒に使用する必要があります。slirp4netnsしかし、outbound_addr6
テストされていません。特権アクセスは必要ありません。