java应用docker部署获取宿主机物理网卡信息方案
java应用docker部署获取宿主机物理网卡信息方案
java应用docker部署获取宿主机物理网卡信息方案
方案概述
本方案适用于 Docker 的 bridge 网络模式,便于为容器分配固定 IP 并支持通过容器名直接进行网络访问,无需切换到 host 模式。
通过文件挂载方式,将宿主机物理网卡的 MAC 地址信息传递到容器,并结合签名机制防止文件被篡改,确保数据安全可靠。
该方案特别适用于 lisense 授权验证等需要容器内获取并信任宿主机物理网卡 MAC 信息的场景。
1. 获取物理网卡MAC地址及签名的自动化脚本
1.1 脚本内容
保存为 /data/mac-auth/get_and_sign_macs.sh
(宿主机):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# 1. 获取所有物理网卡及其MAC地址
cd /sys/class/net
> /data/mac-auth/macs.txt
for iface in *; do
[[ "$iface" =~ ^(lo|docker|br-|veth) ]] && continue
if [ -d "$iface/device" ]; then
mac=$(cat $iface/address)
echo "$iface:$mac" >> /data/mac-auth/macs.txt
fi
done
# 2. 用私钥签名
openssl dgst -sha256 -sign /data/mac-auth/private.key -out /data/mac-auth/macs.sig /data/mac-auth/macs.txt
/data/mac-auth/private.key
为授权方生成的私钥(只需生成一次,妥善保管)。/data/mac-auth/macs.txt
和/data/mac-auth/macs.sig
为输出文件。
1.2 私钥、公钥生成(只需一次)
1
2
openssl genrsa -out /data/mac-auth/private.key 2048
openssl rsa -in /data/mac-auth/private.key -pubout -out /data/mac-auth/public.pem
2. docker-compose 自动化集成
2.1 在主容器 entrypoint/command 自动生成 MAC 文件
推荐将获取物理网卡 MAC 地址及签名的脚本直接集成到主容器(如 java 容器)的 entrypoint 或 command 中。这样可以确保每次主容器启动时,都会自动生成最新的 macs.txt 和 macs.sig 文件,无论是 docker-compose up 还是 docker restart java 都能生效。
关键配置示例
假设你的基础镜像为 alpine,且 /data/mac-auth/get_and_sign_macs.sh、/data/mac-auth/private.key 已挂载到宿主机:
1
2
3
4
5
6
7
8
9
version: '3.8'
services:
java:
image: your-java-image
volumes:
- /sys/class/net:/sys/class/net:ro
- /data/mac-auth:/mac-auth
entrypoint: ["/bin/sh", "-c", "/mac-auth/get_and_sign_macs.sh && java -jar your-app.jar"]
# ... 其他配置
说明:
- 每次 java 容器启动时,都会自动执行 MAC 信息收集与签名脚本,然后启动主应用。
- /mac-auth/macs.txt 和 /mac-auth/macs.sig 会被自动更新为最新内容。
- 公钥已内置在 Java 程序中,无需挂载 public.pem。
- 只需挂载自定义目录(如 /data/mac-auth),避免将整个 /opt 目录映射到容器内。
- 如果基础镜像已自带 openssl,无需重复安装。
2.2 其他自动化方式
如需更高实时性,也可将 get_and_sign_macs.sh 作为 systemd 服务或 cron job,定期/每次重启时自动执行。
3. Java 程序校验签名并获取网卡地址
3.1 公钥生成(只需一次)
1
2
openssl genrsa -out /opt/private.key 2048
openssl rsa -in /opt/private.key -pubout -out /opt/public.pem
3.2 Java 关键代码(公钥内置)
将公钥内容(PEM格式)直接写死在 Java 代码中,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
private static final String PUBLIC_KEY_PEM =
"-----BEGIN PUBLIC KEY-----\n" +
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAn...(省略)...IDAQAB\n" +
"-----END PUBLIC KEY-----";
public static PublicKey loadPublicKeyFromString(String pem) throws Exception {
String key = pem.replaceAll("-----\\w+ PUBLIC KEY-----", "").replaceAll("\\s", "");
byte[] keyBytes = java.util.Base64.getDecoder().decode(key);
java.security.spec.X509EncodedKeySpec spec = new java.security.spec.X509EncodedKeySpec(keyBytes);
return java.security.KeyFactory.getInstance("RSA").generatePublic(spec);
}
public static void main(String[] args) throws Exception {
byte[] macsData = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get("/macs.txt"));
byte[] sigData = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get("/macs.sig"));
PublicKey pubKey = loadPublicKeyFromString(PUBLIC_KEY_PEM);
if (!verifySignature(macsData, sigData, pubKey)) {
throw new SecurityException("MAC地址文件被篡改或签名无效!");
}
Map<String, String> macMap = parseMacs(new String(macsData));
System.out.println(macMap);
}
/**
* 该方法会将 macs.txt 文件内容按行解析为 Map<String, String>,key 为网卡名,value 为对应的 MAC 地址。
*/
public static Map<String, String> parseMacs(String content) {
Map<String, String> macMap = new HashMap<>();
for (String line : content.split("\n")) {
String[] arr = line.trim().split(":");
if (arr.length == 2) {
macMap.put(arr[0], arr[1]);
}
}
return macMap;
}
/**
* 校验macs.txt的签名是否合法。
* @param data macs.txt文件内容的字节数组
* @param sig macs.sig文件内容的字节数组
* @param pubKey 公钥对象
* @return true表示签名校验通过,false表示校验失败
*/
public static boolean verifySignature(byte[] data, byte[] sig, PublicKey pubKey) throws Exception {
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(pubKey);
signature.update(data);
return signature.verify(sig);
}
说明:
- 将
public.pem
文件内容(PEM 格式)复制到PUBLIC_KEY_PEM
字符串中。- Java 程序直接用内置公钥校验签名,无需再挂载 public.pem。
- 如特殊场景下也可通过挂载 public.pem 文件传递公钥,但此方式存在被攻击者同时篡改 macs.sig 和 public.pem 的风险,安全性较低,不推荐用于生产环境。
4. 方案说明与安全建议
- 自动化:每次容器启动时,主服务自动生成并签名物理网卡 MAC 信息,主服务挂载只读文件。
- 安全性:
- 私钥仅存于宿主机,公钥已内置到 Java 程序,无需分发。
- Java 程序校验签名,防止 macs.txt 被篡改。
- 可维护性:如有多台宿主机,均可复用此方案。
5. 目录结构建议
建议在宿主机和容器内保持如下目录结构,便于管理和安全控制:
1
2
3
4
5
6
/data/mac-auth/
get_and_sign_macs.sh # 获取并签名脚本(宿主机)
private.key # 私钥,仅授权方持有(宿主机)
public.pem # 公钥(如需挂载方式,可选)
macs.txt # 物理网卡MAC信息(每次容器启动自动生成)
macs.sig # macs.txt 的签名文件(每次容器启动自动生成)
- 容器内通过挂载
/mac-auth/macs.txt
和/mac-auth/macs.sig
只读访问。 - 推荐将公钥直接内置到 Java 程序中,避免挂载 public.pem。
get_and_sign_macs.sh
和private.key
仅应保存在宿主机,防止泄露。/sys/class/net
以只读方式挂载到容器,便于脚本获取物理网卡信息。- 强烈建议仅挂载专用目录(如
/data/mac-auth
),避免将整个/opt
目录映射到容器内,提升安全性和可维护性。
6. 参考
7. 常见问题与补充说明
- Q: 如果物理网卡名不是 ens18,脚本能否识别?
A: 能,脚本会自动过滤虚拟网卡,只输出有/sys/class/net/<iface>/device
的物理网卡。 - Q: 如果有多块物理网卡?
A:macs.txt
会输出所有物理网卡及其 MAC 地址,Java 端可遍历 map 处理。 - Q: 文件被篡改怎么办?
A: Java 校验签名,签名不通过即拒绝授权。 - Q: 公钥如何分发?
A: 公钥已内置到 Java 程序,无需分发。
本文由作者按照 CC BY 4.0 进行授权