2017-11-18 20 views
2

ICMP ECHOリクエストをビルドしていくつかの異なるIPアドレスに送信するアプリケーションを実行しています。アプリケーションはCrystalで書かれています。クリスタルドッカーコンテナ内からソケットを開くとき、Crystalは例外を発生させます:アクセスが拒否されました。ドッカーコンテナからDGRAMソケットを開くことができません(許可が拒否されました)

コンテナ内からは、ping 8.8.8.8を実行しても問題ありません。

MacOSでアプリケーションを実行しても問題はありません。 AppArmorのとseccomp上https://docs.docker.com/engine/security/apparmor/https://docs.docker.com/engine/security/seccomp/ページを読ん

は、私は解決策を見つけたと確信していたが、問題は、更新/編集

docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined socket_permissionとして実行している場合でも、未解決のまま:capabilities(7)に掘りした後、私は私のdockerfileに次の行を追加しました:RUN setcap cap_net_raw+ep bin/pingソケットをオープンにすることを試みましたが、変更はありません。

ありがとうございます!

関連結晶ソケットコードは、以下の完全に動作するコードサンプル:

# send request 
    address = Socket::IPAddress.new host, 0 
    socket = IPSocket.new Socket::Family::INET, Socket::Type::DGRAM, Socket::Protocol::ICMP 
    socket.send slice, to: address 

Dockerfile:

FROM crystallang/crystal:0.23.1 
WORKDIR /opt 
COPY src/ping.cr src/ 
RUN mkdir bin 

RUN crystal -v 
RUN crystal build -o bin/ping src/ping.cr 

ENTRYPOINT ["/bin/sh","-c"] 
CMD ["/opt/bin/ping"] 

がコードを実行すると、最初のネイティブは、ドッキングウィンドウ経由:

#!/bin/bash 
crystal run src/ping.cr 
docker build -t socket_permission . 
docker run --rm --security-opt seccomp=unconfined --security-opt apparmor=unconfined socket_permission 

そして、最後に、ドッカーのソケットを開けない50行のクリスタルスクリプト:

require "socket" 

TYPE = 8_u16 
IP_HEADER_SIZE_8 = 20 
PACKET_LENGTH_8 = 16 
PACKET_LENGTH_16 = 8 
MESSAGE = " ICMP" 

def ping 
    sequence = 0_u16 
    sender_id = 0_u16 
    host = "8.8.8.8" 

    # initialize packet with MESSAGE 
    packet = Array(UInt16).new PACKET_LENGTH_16 do |i| 
    MESSAGE[ i % MESSAGE.size ].ord.to_u16 
    end 

    # build out ICMP header 
    packet[0] = (TYPE.to_u16 << 8) 
    packet[1] = 0_u16 
    packet[2] = sender_id 
    packet[3] = sequence 

    # calculate checksum 
    checksum = 0_u32 
    packet.each do |byte| 
    checksum += byte 
    end 
    checksum += checksum >> 16 
    checksum = checksum^0xffff_ffff_u32 
    packet[1] = checksum.to_u16 

    # convert packet to 8 bit words 
    slice = Bytes.new(PACKET_LENGTH_8) 

    eight_bit_packet = packet.map do |word| 
    [(word >> 8), (word & 0xff)] 
    end.flatten.map(&.to_u8) 

    eight_bit_packet.each_with_index do |chr, i| 
    slice[i] = chr 
    end 

    # send request 
    address = Socket::IPAddress.new host, 0 
    socket = IPSocket.new Socket::Family::INET, Socket::Type::DGRAM, Socket::Protocol::ICMP 
    socket.send slice, to: address 

    # receive response 
    buffer = Bytes.new(PACKET_LENGTH_8 + IP_HEADER_SIZE_8) 
    count, address = socket.receive buffer 
    length = buffer.size 
    icmp_data = buffer[IP_HEADER_SIZE_8, length-IP_HEADER_SIZE_8] 
end 

ping 

答えて

1

Linux(および拡張子ドッカー)は、macOSがDGRAMソケットに対して行う権限と同じ権限を与えないことが判明しました。ソケット宣言をsocket = IPSocket.new Socket::Family::INET, Socket::Type::RAW, Socket::Protocol::ICMPに変更すると、ソケットがドッカーの下に接続されます。

ルート以外のコンテキストでプログラムを実行するにはもう少しまだ必要です。 RAWソケットはルートに制限されているため、rawソケットへのアクセスにはのバイナリには正しいcapabilityも発行する必要があります。しかし、ドッカーでは、これは必要ではありません。 sudo setcap cap_net_raw+ep bin/pingを実行して、スーパーユーザーコンテキスト外でプログラムを実行できるようになりました。 This is a decent primer on capabilities and the setpcap command

MacOSは同じシステムを使用しないため、setcapは認識できないコマンドです。その結果、コンパイルしてスーパーユーザーコンテキストなしのMacOS上で正常に実行するには上記のコードを取得するには、私はソケット生成コードを変更:

socket_type = Socket::Type::RAW 

{% if flag?(:darwin) %} 
    socket_type = Socket::Type::DGRAM 
{% end %} 

socket = IPSocket.new Socket::Family::INET, socket_type, Socket::Protocol::ICMP 

のLinuxで使用するためにCAP_NET_RAW機能を適用すると、ビルドの他の場所で起こります必要に応じて処理してください。

これらの変更により、プログラムを実行するためにDockerに同梱されているデフォルトからのseccompまたはapparmorへの変更の要件はありません。

関連する問題