Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sockets 从docker容器中打开DGRAM套接字失败(权限被拒绝)_Sockets_Docker_Tcp_Crystal Lang_Seccomp - Fatal编程技术网

Sockets 从docker容器中打开DGRAM套接字失败(权限被拒绝)

Sockets 从docker容器中打开DGRAM套接字失败(权限被拒绝),sockets,docker,tcp,crystal-lang,seccomp,Sockets,Docker,Tcp,Crystal Lang,Seccomp,我正在运行一个应用程序,它构建ICMP回显请求并将其发送到几个不同的ip地址。应用程序是用水晶编写的。尝试从crystal docker容器中打开套接字时,crystal引发异常:权限被拒绝 从容器内部运行ping 8.8.8.8没有问题 在macos上运行应用程序,我没有问题 阅读apparmor和seccomp上的和页面,我确信我找到了解决方案,但问题仍然没有解决,即使以docker run--rm--security opt seccomp=unconfined--security opt

我正在运行一个应用程序,它构建ICMP回显请求并将其发送到几个不同的ip地址。应用程序是用水晶编写的。尝试从crystal docker容器中打开套接字时,crystal引发异常:权限被拒绝

从容器内部运行
ping 8.8.8.8
没有问题

在macos上运行应用程序,我没有问题

阅读apparmor和seccomp上的和页面,我确信我找到了解决方案,但问题仍然没有解决,即使以docker run--rm--security opt seccomp=unconfined--security opt apparmor=unconfined socket\u权限运行

更新/编辑:在深入研究了
功能(7)
之后,我在dockerfile中添加了以下行:
运行setcap cap\u net\u 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"]
先运行本机代码,然后通过docker运行代码:

#!/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行的crystal脚本无法在docker中打开套接字:

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
需要“套接字”
类型=8_u16
IP_标题_大小_8=20
数据包长度8=16
数据包长度16=8
MESSAGE=“ICMP”
德平
序列=0_u16
发送方id=0\u 16
host=“8.8.8.8”
#用消息初始化数据包
数据包=数组(UInt16)。新数据包长度为16|
消息[i%MESSAGE.size].ord.to_16
结束
#构建ICMP标头
数据包[0]=(TYPE.to_u16>16
校验和=校验和^0xffff\U ffff\U u32
数据包[1]=校验和到_u16
#将数据包转换为8位字
切片=字节。新建(数据包长度为8)
八位数据包=packet.map do |字|
[(word>>8),(word&0xff)]
结束.展平.映射(&.至_u8)
八位数据包。每个数据包都有索引do | chr,i|
切片[i]=chr
结束
#发送请求
地址=套接字::IPAddress.new主机,0
socket=IPSocket.new socket::Family::INET,socket::Type::DGRAM,socket::Protocol::ICMP
socket.send切片,发送到:地址
#接收响应
buffer=Bytes.new(数据包长度8+IP头大小8)
计数,地址=socket.receive缓冲区
长度=缓冲区大小
icmp_数据=缓冲区[IP_标头_大小_8,长度-IP_标头_大小_8]
结束
发出砰的声响

答案是Linux(扩展名为docker)不提供macOS对DGRAM套接字的相同权限。将套接字声明更改为
socket=IPSocket.new socket::Family::INET,socket::Type::RAW,socket::Protocol::ICMP
允许套接字在docker下连接

在非根上下文中运行程序还需要一点时间。由于原始套接字限制为根,因此还必须为访问原始套接字发出正确的二进制文件,
CAP\u NET\u raw
。但是,在docker中,这是不必要的。我可以通过运行
sudo,使程序在超级用户上下文之外运行设置cap_net_raw+ep bin/ping

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\u NET\u RAW
功能

通过这些更改,我看不到为了运行该程序而需要对Docker附带的默认版本中的seccomp或apparmor进行任何更改