내 Wireguard 인터페이스만 사용하도록 Podman 컨테이너를 어떻게 구성합니까?

내 Wireguard 인터페이스만 사용하도록 Podman 컨테이너를 어떻게 구성합니까?

이 설정을 사용하여 예상대로 작동하는 wg-quickLinux 책벌레 컴퓨터에 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 인터페이스의 경우 문제가 없습니다. 기본 경로가 없고 올바른 게이트웨이가 없는 레이어 2 인터페이스의 경우 레이어 인터페이스를 켜면 문제가 조금 더 발생합니다) 사용하여 알 수 있는 경로가 없기 때문에 실패합니다.워킹 그룹 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테스트되지 않았습니다. 권한 있는 액세스가 필요하지 않습니다.

관련 정보