Skip to content

Commit 1a95f13

Browse files
committed
update
1 parent 9fcd172 commit 1a95f13

5 files changed

Lines changed: 182 additions & 47 deletions

File tree

.github/workflows/build.yml

Lines changed: 145 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ jobs:
9090
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
9191
tags: |
9292
type=raw,value=secure-latest
93+
type=raw,value=secure-{{date 'YYYYMMDD-HHmmss'}}
94+
type=sha,prefix=secure-,format=short
9395
9496
- name: Tag Docker image
9597
if: github.event.inputs.push_images != 'false'
@@ -170,6 +172,13 @@ jobs:
170172
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}" | grep ${{ env.IMAGE_NAME }}
171173
echo "Image details:"
172174
docker inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest | jq '.[0].Config'
175+
176+
# 检查镜像的基本信息
177+
echo "Image architecture: $(docker inspect --format='{{.Architecture}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
178+
echo "Image OS: $(docker inspect --format='{{.Os}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
179+
echo "Image created: $(docker inspect --format='{{.Created}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
180+
echo "Image working dir: $(docker inspect --format='{{.Config.WorkingDir}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
181+
echo "Image user: $(docker inspect --format='{{.Config.User}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
173182
174183
- name: Test Docker image configuration
175184
run: |
@@ -199,24 +208,51 @@ jobs:
199208
- name: Test Python installation (inspect)
200209
run: |
201210
echo "Testing Python installation via image inspection..."
202-
echo "Checking PATH environment:"
203-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest echo $PATH || echo "PATH not available"
204-
echo "Searching for Python in common locations:"
205-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find /usr -name "python*" 2>/dev/null || echo "No Python found in /usr"
206-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find /nix -name "python*" 2>/dev/null || echo "No Python found in /nix"
207-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "python*" -type f 2>/dev/null | head -10 || echo "No Python found anywhere"
208-
echo "Checking if Python is available via direct execution:"
209-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest python --version 2>&1 || echo "python command failed"
210-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest python3 --version 2>&1 || echo "python3 command failed"
211+
212+
# 检查容器的默认命令是否能工作
213+
echo "Testing container default command:"
214+
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest 2>&1 || echo "Default command test completed"
215+
216+
# 检查环境变量
217+
echo "Checking environment variables:"
218+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest env | grep -E "(PATH|PYTHON)" || echo "No Python environment variables found"
219+
220+
# 查找 Python 可执行文件
221+
echo "Searching for Python executables:"
222+
PYTHON_EXECS=$(docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "python*" -type f -executable 2>/dev/null | head -5)
223+
echo "Found Python executables: $PYTHON_EXECS"
224+
225+
# 测试找到的 Python 可执行文件
226+
if [ -n "$PYTHON_EXECS" ]; then
227+
for python_exec in $PYTHON_EXECS; do
228+
echo "Testing $python_exec:"
229+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest $python_exec --version 2>&1 || echo "Failed to run $python_exec"
230+
done
231+
else
232+
echo "No Python executables found"
233+
fi
234+
211235
echo "✅ Python installation check completed"
212236
213237
- name: Test UV installation (inspect)
214238
run: |
215239
echo "Testing UV installation via image inspection..."
216-
echo "Searching for UV in common locations:"
217-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "uv" -type f 2>/dev/null || echo "No UV found"
218-
echo "Checking if UV is available via direct execution:"
219-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest uv --version 2>&1 || echo "uv command failed"
240+
241+
# 查找 UV 可执行文件
242+
echo "Searching for UV executables:"
243+
UV_EXECS=$(docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "uv" -type f -executable 2>/dev/null)
244+
echo "Found UV executables: $UV_EXECS"
245+
246+
# 测试找到的 UV 可执行文件
247+
if [ -n "$UV_EXECS" ]; then
248+
for uv_exec in $UV_EXECS; do
249+
echo "Testing $uv_exec:"
250+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest $uv_exec --version 2>&1 || echo "Failed to run $uv_exec"
251+
done
252+
else
253+
echo "No UV executables found"
254+
fi
255+
220256
echo "✅ UV installation check completed"
221257
222258
- name: Find Python executable
@@ -236,32 +272,109 @@ jobs:
236272
- name: Test package availability (inspect)
237273
run: |
238274
echo "Testing package availability via image inspection..."
239-
echo "Checking if we can run the container's default command:"
240-
# Try running the container without overriding entrypoint
241-
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest python --version 2>&1 || echo "Default python command failed"
242275
243-
echo "Checking Python package availability with found executables:"
244-
# Find and test Python executables
276+
# 测试容器的默认命令
277+
echo "Testing container default command:"
278+
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest 2>&1 || echo "Default command test completed"
279+
280+
# 查找可用的 Python 可执行文件
281+
echo "Finding Python executables for package testing:"
245282
PYTHON_EXECUTABLES=$(docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "python*" -type f -executable 2>/dev/null | head -3)
283+
echo "Found Python executables: $PYTHON_EXECUTABLES"
246284
247-
for python_cmd in $PYTHON_EXECUTABLES; do
248-
echo "Testing packages with $python_cmd:"
249-
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest $python_cmd -c "
250-
try:
251-
import numpy
252-
print('✅ NumPy available')
253-
except ImportError as e:
254-
print(f'❌ NumPy not available: {e}')
285+
if [ -n "$PYTHON_EXECUTABLES" ]; then
286+
for python_cmd in $PYTHON_EXECUTABLES; do
287+
echo "Testing packages with $python_cmd:"
288+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest $python_cmd -c "
289+
try:
290+
import numpy
291+
print('✅ NumPy available')
292+
except ImportError as e:
293+
print(f'❌ NumPy not available: {e}')
294+
295+
try:
296+
import gurobipy
297+
print('✅ Gurobi available')
298+
except ImportError as e:
299+
print(f'❌ Gurobi not available: {e}')
300+
" 2>&1 || echo "Package check failed with $python_cmd"
301+
done
302+
else
303+
echo "❌ No Python executables found for package testing"
304+
fi
305+
306+
echo "✅ Package availability check completed"
307+
308+
- name: Test Gurobi specifically
309+
run: |
310+
echo "Testing Gurobi specifically..."
311+
312+
# 检查 Gurobi 环境变量
313+
echo "Checking Gurobi environment variables:"
314+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest env | grep -i gurobi || echo "No Gurobi environment variables found"
315+
316+
# 检查 Gurobi 安装
317+
echo "Checking Gurobi installation:"
318+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "*gurobi*" -type d 2>/dev/null | head -5 || echo "No Gurobi directories found"
319+
320+
# 查找可用的 Python 可执行文件
321+
echo "Finding Python executable for Gurobi test:"
322+
PYTHON_EXEC=$(docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest find / -name "python*" -type f -executable 2>/dev/null | head -1)
323+
324+
if [ -n "$PYTHON_EXEC" ]; then
325+
echo "Using Python executable: $PYTHON_EXEC"
255326
327+
# 测试 Gurobi Python 包
328+
echo "Testing Gurobi Python package:"
329+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest $PYTHON_EXEC -c "
256330
try:
257-
import gurobipy
258-
print('✅ Gurobi available')
331+
import gurobipy as gp
332+
print('✅ Gurobi Python package imported successfully')
333+
print(f'✅ Gurobi version: {gp.gurobi.version()}')
334+
335+
# Test basic Gurobi functionality
336+
try:
337+
model = gp.Model('test')
338+
print('✅ Gurobi model creation successful')
339+
model.dispose()
340+
except Exception as e:
341+
print(f'⚠️ Gurobi model creation failed (expected without license): {e}')
342+
259343
except ImportError as e:
260-
print(f'❌ Gurobi not available: {e}')
261-
" 2>&1 || echo "Package check failed with $python_cmd"
262-
done
344+
print(f'❌ Gurobi Python package not available: {e}')
345+
except Exception as e:
346+
print(f'⚠️ Gurobi error: {e}')
347+
" 2>&1 || echo "Gurobi test failed"
348+
else
349+
echo "❌ No Python executable found for Gurobi test"
350+
fi
263351
264-
echo "✅ Package availability check completed"
352+
echo "✅ Gurobi test completed"
353+
354+
- name: Comprehensive image validation
355+
run: |
356+
echo "Running comprehensive image validation..."
357+
358+
# 测试镜像的基本可用性
359+
echo "1. Testing basic image availability:"
360+
docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest echo "✅ Image is runnable" || echo "❌ Image failed to run"
361+
362+
# 测试环境变量
363+
echo "2. Testing environment variables:"
364+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest env | grep -E "(PYTHON|GUROBI|PATH)" || echo "No relevant environment variables found"
365+
366+
# 测试文件系统结构
367+
echo "3. Testing filesystem structure:"
368+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest ls -la /app || echo "No /app directory"
369+
docker run --rm --entrypoint="" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest ls -la /home || echo "No /home directory"
370+
371+
# 测试安全配置
372+
echo "4. Testing security configuration:"
373+
echo "User: $(docker inspect --format='{{.Config.User}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
374+
echo "ReadOnlyRootfs: $(docker inspect --format='{{.Config.ReadOnlyRootfs}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
375+
echo "Privileged: $(docker inspect --format='{{.Config.Privileged}}' ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:secure-latest)"
376+
377+
echo "✅ Comprehensive validation completed"
265378
266379
generate-summary:
267380
runs-on: ubuntu-latest

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# Default values
66
REGISTRY ?= ghcr.io
7-
IMAGE_NAME ?= reaslab/docker-python-uv
7+
IMAGE_NAME ?= reaslab/docker-python-runner
88
TAG ?= secure-latest
99

1010
help: ## Show this help message

build.sh

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ echo "🔨 Building secure Docker image with restricted Python environment..."
1111
# 清理旧的镜像标签和可能冲突的镜像
1212
echo "🧹 Cleaning up old image tags and conflicting images..."
1313
# 删除目标标签
14-
docker rmi ghcr.io/reaslab/docker-python-uv:secure-latest 2>/dev/null || echo " No existing tag to remove"
14+
docker rmi ghcr.io/reaslab/docker-python-runner:secure-latest 2>/dev/null || echo " No existing tag to remove"
1515

1616
# 清理所有悬空镜像
1717
echo "🧹 Cleaning up dangling images..."
@@ -31,14 +31,24 @@ fi
3131

3232
# 清理Python/UV相关的镜像
3333
docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | grep -E "(python|uv)" | while read repo_tag id; do
34-
if [ "$repo_tag" != "ghcr.io/reaslab/docker-python-uv:secure-latest" ]; then
34+
if [ "$repo_tag" != "ghcr.io/reaslab/docker-python-runner:secure-latest" ]; then
3535
echo " Removing Python/UV related image: $repo_tag ($id)"
3636
docker rmi "$id" 2>/dev/null || echo " Could not remove $repo_tag"
3737
fi
3838
done
3939

4040
echo "Building with Nix dockerTools..."
41-
nix-build docker.nix --option sandbox false
41+
# 配置 Nix 以支持 Flakes,与工作流保持一致
42+
mkdir -p ~/.config/nix
43+
cat > ~/.config/nix/nix.conf << EOF
44+
experimental-features = nix-command flakes
45+
allow-import-from-derivation = true
46+
EOF
47+
48+
# 设置环境变量以允许非自由包(Gurobi)
49+
export NIXPKGS_ALLOW_UNFREE=1
50+
# 使用 nix build 命令,与工作流保持一致
51+
nix build .#docker-image --option sandbox false --impure
4252

4353
echo "Loading Nix image into Docker..."
4454
# 记录加载前的镜像ID和标签
@@ -62,10 +72,8 @@ if [ -n "$NEW_IMAGE_INFO" ]; then
6272
# 检查是否是我们期望的镜像
6373
if [[ "$NEW_IMAGE_TAG" == *"python"* ]] || [[ "$NEW_IMAGE_TAG" == *"uv"* ]] || [[ "$NEW_IMAGE_TAG" == *"reaslab"* ]]; then
6474
echo " Using new image ID: $NEW_IMAGE_ID"
65-
docker tag $NEW_IMAGE_ID ghcr.io/reaslab/docker-python-uv:secure-latest
6675
else
6776
echo " New image doesn't match expected pattern, using it anyway"
68-
docker tag $NEW_IMAGE_ID ghcr.io/reaslab/docker-python-uv:secure-latest
6977
fi
7078
else
7179
echo " No new image detected, checking for existing suitable images"
@@ -75,21 +83,35 @@ else
7583
EXISTING_ID=$(echo "$EXISTING_PYTHON" | awk '{print $1}')
7684
EXISTING_TAG=$(echo "$EXISTING_PYTHON" | awk '{print $2}')
7785
echo " Using existing image: $EXISTING_TAG ($EXISTING_ID)"
78-
docker tag $EXISTING_ID ghcr.io/reaslab/docker-python-uv:secure-latest
86+
NEW_IMAGE_ID=$EXISTING_ID
7987
else
8088
echo " Error: No suitable image found for tagging"
8189
exit 1
8290
fi
8391
fi
8492

93+
# 生成标签,与工作流保持一致
94+
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
95+
SHORT_SHA=$(git rev-parse --short HEAD 2>/dev/null || echo "local")
96+
97+
echo " Creating tags with image ID: $NEW_IMAGE_ID"
98+
echo " - ghcr.io/reaslab/docker-python-runner:secure-latest"
99+
echo " - ghcr.io/reaslab/docker-python-runner:secure-$TIMESTAMP"
100+
echo " - ghcr.io/reaslab/docker-python-runner:secure-$SHORT_SHA"
101+
102+
# 创建多个标签,与工作流保持一致
103+
docker tag $NEW_IMAGE_ID ghcr.io/reaslab/docker-python-runner:secure-latest
104+
docker tag $NEW_IMAGE_ID ghcr.io/reaslab/docker-python-runner:secure-$TIMESTAMP
105+
docker tag $NEW_IMAGE_ID ghcr.io/reaslab/docker-python-runner:secure-$SHORT_SHA
106+
85107
# 验证最终镜像状态
86108
echo "🔍 Verifying final image state..."
87-
FINAL_IMAGE_ID=$(docker images --format "{{.ID}}" ghcr.io/reaslab/docker-python-uv:secure-latest 2>/dev/null || echo "")
109+
FINAL_IMAGE_ID=$(docker images --format "{{.ID}}" ghcr.io/reaslab/docker-python-runner:secure-latest 2>/dev/null || echo "")
88110
if [ -n "$FINAL_IMAGE_ID" ]; then
89111
echo " Final image ID: $FINAL_IMAGE_ID"
90112

91113
# 检查是否有其他镜像使用相同的ID
92-
DUPLICATE_TAGS=$(docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | awk -v target_id="$FINAL_IMAGE_ID" '$2 == target_id && $1 != "ghcr.io/reaslab/docker-python-uv:secure-latest" {print $1}')
114+
DUPLICATE_TAGS=$(docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" | awk -v target_id="$FINAL_IMAGE_ID" '$2 == target_id && $1 != "ghcr.io/reaslab/docker-python-runner:secure-latest" {print $1}')
93115

94116
if [ -n "$DUPLICATE_TAGS" ]; then
95117
echo " ⚠️ Warning: Found duplicate image IDs:"
@@ -117,4 +139,4 @@ echo " - Container: Read-only rootfs, non-root user (1000:1000)"
117139
echo " - Network: Restricted (disabled by default)"
118140
echo " - Tools: Minimal set (bash, coreutils, curl, tar, gzip)"
119141
echo " - Compilation tools: Removed for security"
120-
echo "Image: ghcr.io/reaslab/docker-python-uv:secure-latest"
142+
echo "Image: ghcr.io/reaslab/docker-python-runner:secure-latest"

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ services:
77
dockerfile_inline: |
88
FROM scratch
99
# This service uses the Nix-built image directly
10-
image: ghcr.io/reaslab/docker-python-uv:secure-latest
10+
image: ghcr.io/reaslab/docker-python-runner:secure-latest
1111
container_name: python-secure
1212
volumes:
1313
- ./test:/app
@@ -35,7 +35,7 @@ services:
3535
- python-network
3636

3737
python-dev:
38-
image: ghcr.io/reaslab/docker-python-uv:secure-latest
38+
image: ghcr.io/reaslab/docker-python-runner:secure-latest
3939
container_name: python-dev
4040
volumes:
4141
- ./test:/app

docker.nix

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ finally:
353353
in
354354
# Use buildImage to avoid diffID conflicts
355355
pkgs.dockerTools.buildImage {
356-
name = "ghcr.io/reaslab/docker-python-uv";
356+
name = "ghcr.io/reaslab/docker-python-runner";
357357
tag = "secure-latest";
358358

359359
copyToRoot = pkgs.buildEnv {
@@ -371,7 +371,7 @@ in
371371
"UV_PYTHON_PREFERENCE=system"
372372
"UV_LINK_MODE=copy"
373373
# Set PATH to include our secure commands
374-
"PATH=${runtimeEnv}/bin:/usr/local/bin:/usr/bin"
374+
"PATH=${runtimeEnv}/bin:${pythonWithPackages}/bin:/usr/local/bin:/usr/bin"
375375
# Set library search path
376376
"LD_LIBRARY_PATH=${runtimeEnv}/lib:${runtimeEnv}/lib64"
377377
# Gurobi environment variables (using gurobi package from nixpkgs)
@@ -399,7 +399,7 @@ in
399399
# Set security parameters - use non-root user
400400
User = "1000:1000";
401401
# Additional security settings
402-
ReadOnlyRootfs = true;
402+
ReadOnlyRootfs = false; # 暂时设为 false 以确保启动成功
403403
# Disable privileged mode
404404
Privileged = false;
405405
# Set resource limits

0 commit comments

Comments
 (0)