1
0

39 Commits

Author SHA1 Message Date
fb536f5292 fix: skip retracted kernels 2024-12-02 17:18:07 +00:00
82f186fe71 ci: increase disk space 2024-10-17 22:45:59 +00:00
8999a65f4e ci: archive logs 2024-10-17 22:06:55 +00:00
426bd3864a ci: generate CentOS images 2024-10-17 22:03:01 +00:00
e6ae8a9c2f fix(qemu images): set centos 7 repos 2024-10-17 21:59:49 +00:00
82e03b79fc fix(qemu images): add appstream repo for OracleLinux 2024-10-17 21:19:42 +00:00
081b534bd2 ci: update ssh keys 2024-10-17 19:10:28 +00:00
eb04c74c1b fix(qemu images): fix sources.list 2024-10-17 18:24:41 +00:00
8f34ec0be0 fix(qemu images): use apt-get 2024-10-17 18:13:55 +00:00
2daa111196 feat(qemu images): generate all ubuntu images 2024-10-17 18:05:01 +00:00
48854bf40d ci(qemu images): bump version for newly generated images 2024-10-17 17:52:40 +00:00
8d0941b406 ci: wait for cloud-init to finish 2024-10-17 17:50:08 +00:00
bd0160aa85 ci: rename images-related workflows 2024-10-17 17:24:42 +00:00
1a0578c541 fix(qemu images): set sshd UseDNS=no 2024-10-17 17:20:46 +00:00
2df0d81782 fix(container): prevent base image pull for non-registry containers 2024-10-17 12:28:53 +00:00
77547cedce fix(client): set push.default=current for git 2024-10-09 18:57:25 +00:00
24ec99bacd feat: more logs by default 2024-10-09 18:54:25 +00:00
354b1cbedd ci: set timeout to 5m 2024-10-09 18:52:31 +00:00
e96cfac95c fix: use text ok/fail in log query 2024-10-09 09:54:23 +00:00
9bb15afa21 fix: disable multithreading when realtime output is enabled 2024-10-09 09:38:05 +00:00
27abdc3687 fix: do not print output on error if real-time output is enabled 2024-10-09 09:17:21 +00:00
c53e0cc99b fix: trim control commands from qemu logs 2024-10-08 22:36:12 +00:00
ef4a9364a1 docs: update examples 2024-10-08 15:03:44 +00:00
0bc66ec025 docs: remove outdated screenshot 2024-10-08 14:59:10 +00:00
1814fe1144 ci: set timeout to 3m 2024-10-08 14:54:23 +00:00
77442a31b1 feat: retry on internal error 2024-10-08 14:50:47 +00:00
4e3313b6db feat: parameter to write per-run logs to directory 2024-10-08 12:01:53 +00:00
287ef19530 fix: pass container by pointer to allow changing logs 2024-10-08 12:01:35 +00:00
5bb4e3ff45 feat: realtime output 2024-10-08 08:43:37 +00:00
fee3b44c6e feat: parameter to display the output of successful stages 2024-10-07 20:59:16 +00:00
a852e2d9e9 feat: show relevant qemu output 2024-10-07 20:40:58 +00:00
7cb5877fd0 refactor: logs 2024-10-07 16:47:39 +00:00
b32c097446 feat!: harmonise distro/release/kernel parameters across commands
BREAKING CHANGE: Parameters for the kernel command are changed

from

--distro= --ver= --kernel=

to

--distro-id= --distro-release= --kernel-regex=
2024-10-07 14:53:10 +00:00
77aecc7548 fix: make build errors easier to read 2024-10-07 13:31:02 +00:00
20cd32243d feat!: replace filtering of containers by name to distro/release 2024-10-06 17:01:31 +00:00
a7ecc354a9 feat!: prepend/append commands to dockerfile
BREAKING CHANGE: Command definition in the configuration has been changed

from

[[docker.commands]]
distro = { id = "Ubuntu" }
command = "echo runs before the base layer"

to

[[docker.commands.prepend]]
distro = { id = "Ubuntu" }
command = "echo runs before the base layer"
2024-10-06 16:11:29 +00:00
cba1abc7f4 feat: command for the update of containers 2024-10-06 15:49:19 +00:00
3f0c28014c feat: add command aliases 2024-10-06 15:34:14 +00:00
c3c97c3828 fix: untag duplicate image in case of docker 2024-10-06 15:33:35 +00:00
35 changed files with 733 additions and 261 deletions

View File

@ -14,6 +14,7 @@ on:
- ".github/workflows/macos.yml" - ".github/workflows/macos.yml"
- ".github/workflows/debian-cache.yml" - ".github/workflows/debian-cache.yml"
- "docs/**" - "docs/**"
- 'tools/**'
- ".readthedocs.yaml" - ".readthedocs.yaml"
- "README.md" - "README.md"
@ -125,7 +126,7 @@ jobs:
echo 'distro = { id = "${{ matrix.os.distro }}", release = "${{ matrix.os.release }}" }' >> examples/kernel-module/.out-of-tree.toml echo 'distro = { id = "${{ matrix.os.distro }}", release = "${{ matrix.os.release }}" }' >> examples/kernel-module/.out-of-tree.toml
echo 'kernel = { regex = ".*" }' >> examples/kernel-module/.out-of-tree.toml echo 'kernel = { regex = ".*" }' >> examples/kernel-module/.out-of-tree.toml
echo '[qemu]' >> examples/kernel-module/.out-of-tree.toml echo '[qemu]' >> examples/kernel-module/.out-of-tree.toml
echo 'timeout = "10m"' >> examples/kernel-module/.out-of-tree.toml echo 'timeout = "5m"' >> examples/kernel-module/.out-of-tree.toml
echo 'after_start_timeout = "10s"' >> examples/kernel-module/.out-of-tree.toml echo 'after_start_timeout = "10s"' >> examples/kernel-module/.out-of-tree.toml
echo 'modprobe uio || modprobe 9p || modprobe xfs' >> examples/kernel-module/test.sh echo 'modprobe uio || modprobe 9p || modprobe xfs' >> examples/kernel-module/test.sh
@ -142,7 +143,7 @@ jobs:
echo 'WorkingDirectory=/root/test' >> test.service echo 'WorkingDirectory=/root/test' >> test.service
echo 'TimeoutStopSec=1' >> test.service echo 'TimeoutStopSec=1' >> test.service
echo 'ExecStart=/usr/local/bin/out-of-tree kernel --no-prebuilt-containers autogen --threads=8 --max=64 --shuffle' >> test.service echo 'ExecStart=/usr/local/bin/out-of-tree kernel --no-prebuilt-containers autogen --threads=8 --max=64 --shuffle' >> test.service
echo 'ExecStart=/usr/local/bin/out-of-tree pew --qemu-timeout=10m --threads=4 --include-internal-errors' >> test.service echo 'ExecStart=/usr/local/bin/out-of-tree pew --threads=4 --include-internal-errors' >> test.service
scp test.service root@$IP:/etc/systemd/system/test.service scp test.service root@$IP:/etc/systemd/system/test.service

87
.github/workflows/images-centos.yml vendored Normal file
View File

@ -0,0 +1,87 @@
name: CentOS images
on:
workflow_dispatch:
push:
paths:
- 'tools/qemu-centos-img/**'
- '.github/workflows/images-centos.yml'
concurrency:
group: ${{ github.workflow_ref }}
cancel-in-progress: true
jobs:
images-centos:
name: Qemu Images
runs-on: ubuntu-latest
steps:
- uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: create droplet
run: doctl compute droplet create --ssh-keys='b4:4c:66:7d:be:19:25:43:1c:e0:02:61:9f:49:12:94,37:46:77:a8:4a:96:3b:20:16:46:35:04:95:ca:0c:5c' --tag-name=github-actions ga-out-of-tree-images-centos-$GITHUB_SHA --size s-1vcpu-1gb --image ubuntu-22-04-x64 --wait
# TODO Move to common script
- name: generate images
shell: bash
run: |
sleep 1m
IP=$(doctl compute droplet list --tag-name=github-actions --format "Name,Public IPv4" | grep -v ID | grep ga-out-of-tree-images-centos-$GITHUB_SHA | awk '{print $2}')
while ! ssh -o StrictHostKeyChecking=accept-new root@$IP echo
do
sleep 1s
done
ssh root@$IP "cloud-init status --wait | grep done"
ssh root@$IP apt-get update
ssh root@$IP apt-get install -y git podman s3cmd
ssh root@$IP git clone https://github.com/out-of-tree/out-of-tree
ssh root@$IP "cd out-of-tree && git checkout $GITHUB_SHA"
ssh root@$IP "echo -e '[Unit]\nDescription=CentOS image generator and uploader\n[Service]\nRemainAfterExit=yes\nStandardError=append:/var/log/images-centos.log\nStandardOutput=append:/var/log/images-centos.log\nType=oneshot' >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo Environment=HOST=fra1.digitaloceanspaces.com >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo Environment=HOST_BUCKET=out-of-tree.fra1.digitaloceanspaces.com >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo Environment=ACCESS_KEY=${{ secrets.DIGITALOCEAN_SPACES_ACCESS_KEY }} >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo Environment=SECRET_KEY=${{ secrets.DIGITALOCEAN_SPACES_SECRET_KEY }} >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-centos-img/6/generate.sh' >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-centos-img/7/generate.sh' >> /etc/systemd/system/images-centos.service"
ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-centos-img/8/generate.sh' >> /etc/systemd/system/images-centos.service"
ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-centos-img/*/*.tar.gz s3://out-of-tree/3.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-centos.service'
ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-centos.service"
ssh root@$IP systemctl daemon-reload
ssh root@$IP systemctl start images-centos --no-block
while ! ssh root@$IP systemctl show images-centos -p SubState --value | grep -E '(failed|exited)'
do
sleep 3m
done
scp root@$IP:/var/log/images-centos.log .
ssh root@$IP systemctl is-active images-centos
- name: Archive logs
if: always()
uses: actions/upload-artifact@v4
with:
name: images-centos-log
path: images-centos.log
- name: delete droplet
if: always()
run: doctl compute droplet delete -f ga-out-of-tree-images-centos-$GITHUB_SHA

View File

@ -1,4 +1,4 @@
name: Debian name: Debian images
on: on:
workflow_dispatch: workflow_dispatch:
@ -25,7 +25,7 @@ jobs:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: create droplet - name: create droplet
run: doctl compute droplet create --ssh-keys='b4:4c:66:7d:be:19:25:43:1c:e0:02:61:9f:49:12:94' --tag-name=github-actions ga-out-of-tree-images-debian-$GITHUB_SHA --size s-1vcpu-1gb --image ubuntu-22-04-x64 --wait run: doctl compute droplet create --ssh-keys='b4:4c:66:7d:be:19:25:43:1c:e0:02:61:9f:49:12:94,37:46:77:a8:4a:96:3b:20:16:46:35:04:95:ca:0c:5c' --tag-name=github-actions ga-out-of-tree-images-debian-$GITHUB_SHA --size s-1vcpu-1gb --image ubuntu-22-04-x64 --wait
# TODO Move to common script # TODO Move to common script
- name: generate images - name: generate images
@ -40,8 +40,7 @@ jobs:
sleep 1s sleep 1s
done done
sleep 5m ssh root@$IP "cloud-init status --wait | grep done"
ssh root@$IP pkill apt-get || true
ssh root@$IP apt-get update ssh root@$IP apt-get update
ssh root@$IP apt-get install -y git podman s3cmd ssh root@$IP apt-get install -y git podman s3cmd
@ -57,7 +56,7 @@ jobs:
ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-debian-img/generate-images.sh' >> /etc/systemd/system/images-debian.service" ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-debian-img/generate-images.sh' >> /etc/systemd/system/images-debian.service"
ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-debian-img/*.tar.gz s3://out-of-tree/1.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-debian.service' ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-debian-img/*.tar.gz s3://out-of-tree/3.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-debian.service'
ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-debian.service" ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-debian.service"

View File

@ -1,4 +1,4 @@
name: Oracle Linux name: Oracle Linux images
on: on:
workflow_dispatch: workflow_dispatch:
@ -25,7 +25,7 @@ jobs:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: create droplet - name: create droplet
run: doctl compute droplet create --ssh-keys='b4:4c:66:7d:be:19:25:43:1c:e0:02:61:9f:49:12:94' --tag-name=github-actions ga-out-of-tree-images-oraclelinux-$GITHUB_SHA --size s-1vcpu-1gb --image ubuntu-22-04-x64 --wait run: doctl compute droplet create --ssh-keys='b4:4c:66:7d:be:19:25:43:1c:e0:02:61:9f:49:12:94,37:46:77:a8:4a:96:3b:20:16:46:35:04:95:ca:0c:5c' --tag-name=github-actions ga-out-of-tree-images-oraclelinux-$GITHUB_SHA --size s-1vcpu-2gb --image ubuntu-22-04-x64 --wait
# TODO Move to common script # TODO Move to common script
- name: generate images - name: generate images
@ -40,8 +40,7 @@ jobs:
sleep 1s sleep 1s
done done
sleep 5m ssh root@$IP "cloud-init status --wait | grep done"
ssh root@$IP pkill apt-get || true
ssh root@$IP apt-get update ssh root@$IP apt-get update
ssh root@$IP apt-get install -y git podman s3cmd ssh root@$IP apt-get install -y git podman s3cmd
@ -57,7 +56,7 @@ jobs:
ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-oraclelinux-img/generate-images.sh' >> /etc/systemd/system/images-oraclelinux.service" ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-oraclelinux-img/generate-images.sh' >> /etc/systemd/system/images-oraclelinux.service"
ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-oraclelinux-img/*.tar.gz s3://out-of-tree/1.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-oraclelinux.service' ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-oraclelinux-img/*.tar.gz s3://out-of-tree/3.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-oraclelinux.service'
ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-oraclelinux.service" ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-oraclelinux.service"
@ -74,6 +73,13 @@ jobs:
ssh root@$IP systemctl is-active images-oraclelinux ssh root@$IP systemctl is-active images-oraclelinux
- name: Archive logs
if: always()
uses: actions/upload-artifact@v4
with:
name: images-oraclelinux-log
path: images-oraclelinux.log
- name: delete droplet - name: delete droplet
if: always() if: always()
run: doctl compute droplet delete -f ga-out-of-tree-images-oraclelinux-$GITHUB_SHA run: doctl compute droplet delete -f ga-out-of-tree-images-oraclelinux-$GITHUB_SHA

View File

@ -58,7 +58,7 @@ jobs:
ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-ubuntu-img/generate-images.py' >> /etc/systemd/system/images-ubuntu.service" ssh root@$IP "echo 'ExecStart=/root/out-of-tree/tools/qemu-ubuntu-img/generate-images.py' >> /etc/systemd/system/images-ubuntu.service"
ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-ubuntu-img/*.tar.gz s3://out-of-tree/1.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-ubuntu.service' ssh root@$IP 'echo ExecStart=/bin/sh -c \"s3cmd put --acl-public /root/out-of-tree/tools/qemu-ubuntu-img/*.tar.gz s3://out-of-tree/3.0.0/ --host=\$HOST --host-bucket=\$HOST_BUCKET --access_key=\$ACCESS_KEY --secret_key=\$SECRET_KEY\" >> /etc/systemd/system/images-ubuntu.service'
ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-ubuntu.service" ssh root@$IP "echo TimeoutStopSec=1 >> /etc/systemd/system/images-ubuntu.service"

View File

@ -203,7 +203,7 @@ jobs:
cp ../examples/kernel-module/{module.c,Makefile,test.sh} . cp ../examples/kernel-module/{module.c,Makefile,test.sh} .
../out-of-tree --log-level=debug kernel list-remote --distro=${{ matrix.os.distro }} --ver=${{ matrix.os.release }} ../out-of-tree --log-level=debug kernel list-remote --distro-id=${{ matrix.os.distro }} --distro-release=${{ matrix.os.release }}
../out-of-tree --log-level=debug kernel autogen --max=1 --shuffle ../out-of-tree --log-level=debug kernel autogen --max=1 --shuffle
../out-of-tree --log-level=debug pew --qemu-timeout=20m --include-internal-errors ../out-of-tree --log-level=debug pew --qemu-timeout=20m --include-internal-errors

View File

@ -8,8 +8,6 @@
*out-of-tree* was created to reduce the complexity of the environment for developing, testing and debugging Linux kernel exploits and out-of-tree kernel modules (hence the name "out-of-tree"). *out-of-tree* was created to reduce the complexity of the environment for developing, testing and debugging Linux kernel exploits and out-of-tree kernel modules (hence the name "out-of-tree").
![Screenshot](https://cloudflare-ipfs.com/ipfs/Qmb88fgdDjbWkxz91sWsgmoZZNfVThnCtj37u3mF2s3T3T)
## Installation ## Installation
### GNU/Linux (with [Nix](https://nixos.org/nix/)) ### GNU/Linux (with [Nix](https://nixos.org/nix/))
@ -42,9 +40,9 @@ Read [documentation](https://out-of-tree.readthedocs.io) for further info.
## Examples ## Examples
Generate all Ubuntu 22.04 kernels: Download all Ubuntu 24.04 kernels:
$ out-of-tree kernel genall --distro=Ubuntu --ver=22.04 $ out-of-tree kernel genall --distro-id=Ubuntu --distro-release=24.04
Run tests based on .out-of-tree.toml definitions: Run tests based on .out-of-tree.toml definitions:
@ -52,8 +50,8 @@ Run tests based on .out-of-tree.toml definitions:
Test with a specific kernel: Test with a specific kernel:
$ out-of-tree pew --kernel='Ubuntu:5.4.0-29-generic' $ out-of-tree pew --realtime-output --distro-id=ubuntu --kernel-regex=6.8.0-41-generic
Run debug environment: Run debug environment:
$ out-of-tree debug --kernel='Ubuntu:5.4.0-29-generic' $ out-of-tree debug --distro-id=ubuntu --distro-release=24.04 --kernel-regex=6.8.0-41-generic

View File

@ -12,7 +12,6 @@ import (
"github.com/naoina/toml" "github.com/naoina/toml"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"code.dumpstack.io/tools/out-of-tree/config/dotfiles" "code.dumpstack.io/tools/out-of-tree/config/dotfiles"
"code.dumpstack.io/tools/out-of-tree/distro" "code.dumpstack.io/tools/out-of-tree/distro"
@ -241,8 +240,9 @@ func (ka Artifact) Supported(ki distro.KernelInfo) (supported bool, err error) {
return return
} }
// TODO too many parameters
func (ka Artifact) Process(slog zerolog.Logger, ki distro.KernelInfo, func (ka Artifact) Process(slog zerolog.Logger, ki distro.KernelInfo,
endless bool, cBinary, outputOnSuccess, realtimeOutput, endless bool, cBinary,
cEndlessStress string, cEndlessTimeout time.Duration, cEndlessStress string, cEndlessTimeout time.Duration,
dump func(q *qemu.System, ka Artifact, ki distro.KernelInfo, dump func(q *qemu.System, ka Artifact, ki distro.KernelInfo,
result *Result)) { result *Result)) {
@ -334,12 +334,22 @@ func (ka Artifact) Process(slog zerolog.Logger, ki distro.KernelInfo,
// TODO: build should return structure // TODO: build should return structure
start := time.Now() start := time.Now()
result.BuildDir, result.BuildArtifact, result.Build.Output, err = result.BuildDir, result.BuildArtifact, result.Build.Output, err =
Build(slog, tmp, ka, ki, ka.Docker.Timeout.Duration) Build(slog, tmp, ka, ki, ka.Docker.Timeout.Duration, realtimeOutput)
slog.Debug().Str("duration", time.Since(start).String()). slog.Debug().Str("duration", time.Since(start).String()).
Msg("build done") Msg("build done")
if err != nil { if err != nil {
log.Error().Err(err).Msg("build") if !realtimeOutput {
slog.Error().Err(err).Msgf("build failure\n%v\n", result.Build.Output)
} else {
slog.Error().Err(err).Msg("build failure")
}
return return
} else {
if outputOnSuccess && !realtimeOutput {
slog.Info().Msgf("build success\n%v\n", result.Build.Output)
} else {
slog.Info().Msg("build success")
}
} }
result.Build.Ok = true result.Build.Ok = true
} else { } else {
@ -361,6 +371,8 @@ func (ka Artifact) Process(slog zerolog.Logger, ki distro.KernelInfo,
ka.Qemu.Timeout.Duration = time.Minute ka.Qemu.Timeout.Duration = time.Minute
} }
slog.Info().Msg("wait for vm initialisation")
err = q.WaitForSSH(ka.Qemu.Timeout.Duration) err = q.WaitForSSH(ka.Qemu.Timeout.Duration)
if err != nil { if err != nil {
result.InternalError = err result.InternalError = err
@ -397,11 +409,40 @@ func (ka Artifact) Process(slog zerolog.Logger, ki distro.KernelInfo,
return return
} }
var qemuTestOutput string
q.SetQemuOutputHandler(func(s string) {
if realtimeOutput {
fmt.Printf("kmsg: %s\n", s)
} else {
qemuTestOutput += s + "\n"
}
})
if realtimeOutput {
q.SetCommandsOutputHandler(func(s string) {
fmt.Printf("test: %s\n", s)
})
}
start := time.Now() start := time.Now()
copyArtifactAndTest(slog, q, ka, &result, remoteTest) slog.Info().Msg("copy artifact and run test")
copyArtifactAndTest(slog, q, ka, &result, remoteTest, outputOnSuccess, realtimeOutput)
slog.Debug().Str("duration", time.Since(start).String()). slog.Debug().Str("duration", time.Since(start).String()).
Msgf("test completed (success: %v)", result.Test.Ok) Msgf("test completed (success: %v)", result.Test.Ok)
if result.Build.Ok && !realtimeOutput {
if !result.Run.Ok || !result.Test.Ok {
slog.Error().Msgf("qemu output\n%v\n", qemuTestOutput)
} else if outputOnSuccess {
slog.Info().Msgf("qemu output\n%v\n", qemuTestOutput)
}
}
if realtimeOutput {
q.CloseCommandsOutputHandler()
}
q.CloseQemuOutputHandler()
if !endless { if !endless {
return return
} }

View File

@ -111,7 +111,7 @@ func buildPreload(workPath, tmp string, ki distro.KernelInfo,
dockerTimeout = ka.Docker.Timeout.Duration dockerTimeout = ka.Docker.Timeout.Duration
} }
_, af, _, err = Build(log.Logger, tmp, ka, ki, dockerTimeout) _, af, _, err = Build(log.Logger, tmp, ka, ki, dockerTimeout, false)
return return
} }

View File

@ -103,7 +103,7 @@ func applyPatches(src string, ka Artifact) (err error) {
} }
func Build(flog zerolog.Logger, tmp string, ka Artifact, func Build(flog zerolog.Logger, tmp string, ka Artifact,
ki distro.KernelInfo, dockerTimeout time.Duration) ( ki distro.KernelInfo, dockerTimeout time.Duration, realtimeOutput bool) (
outdir, outpath, output string, err error) { outdir, outpath, output string, err error) {
target := strings.Replace(ka.Name, " ", "_", -1) target := strings.Replace(ka.Name, " ", "_", -1)
@ -157,9 +157,19 @@ func Build(flog zerolog.Logger, tmp string, ka Artifact,
c.Args = append(c.Args, "--network", "none") c.Args = append(c.Args, "--network", "none")
if realtimeOutput {
c.SetCommandsOutputHandler(func(s string) {
fmt.Printf("%s\n", s)
})
}
output, err = c.Run(outdir, []string{ output, err = c.Run(outdir, []string{
buildCommand + " && chmod -R 777 /work", buildCommand + " && chmod -R 777 /work",
}) })
if realtimeOutput {
c.CloseCommandsOutputHandler()
}
} else { } else {
cmd := exec.Command("bash", "-c", "cd "+outdir+" && "+ cmd := exec.Command("bash", "-c", "cd "+outdir+" && "+
buildCommand) buildCommand)
@ -281,7 +291,7 @@ func CopyFile(sourcePath, destinationPath string) (err error) {
} }
func copyArtifactAndTest(slog zerolog.Logger, q *qemu.System, ka Artifact, func copyArtifactAndTest(slog zerolog.Logger, q *qemu.System, ka Artifact,
res *Result, remoteTest string) (err error) { res *Result, remoteTest string, outputOnSuccess, realtimeOutput bool) (err error) {
// Copy all test files to the remote machine // Copy all test files to the remote machine
for _, f := range ka.TestFiles { for _, f := range ka.TestFiles {
@ -313,8 +323,7 @@ func copyArtifactAndTest(slog zerolog.Logger, q *qemu.System, ka Artifact,
res.Test.Output, err = testKernelModule(q, ka, remoteTest) res.Test.Output, err = testKernelModule(q, ka, remoteTest)
if err != nil { if err != nil {
slog.Error().Err(err).Msg(res.Test.Output) break
return
} }
res.Test.Ok = true res.Test.Ok = true
case KernelExploit: case KernelExploit:
@ -327,16 +336,14 @@ func copyArtifactAndTest(slog zerolog.Logger, q *qemu.System, ka Artifact,
res.Test.Output, err = testKernelExploit(q, ka, remoteTest, res.Test.Output, err = testKernelExploit(q, ka, remoteTest,
remoteExploit) remoteExploit)
if err != nil { if err != nil {
slog.Error().Err(err).Msg(res.Test.Output) break
return
} }
res.Run.Ok = true // does not really used res.Run.Ok = true // does not really used
res.Test.Ok = true res.Test.Ok = true
case Script: case Script:
res.Test.Output, err = runScript(q, remoteTest) res.Test.Output, err = runScript(q, remoteTest)
if err != nil { if err != nil {
slog.Error().Err(err).Msg(res.Test.Output) break
return
} }
res.Run.Ok = true res.Run.Ok = true
res.Test.Ok = true res.Test.Ok = true
@ -344,7 +351,20 @@ func copyArtifactAndTest(slog zerolog.Logger, q *qemu.System, ka Artifact,
slog.Fatal().Msg("Unsupported artifact type") slog.Fatal().Msg("Unsupported artifact type")
} }
slog.Info().Msgf("\n%v\n", res.Test.Output) if err != nil || !res.Test.Ok {
if !realtimeOutput {
slog.Error().Err(err).Msgf("test failure\n%v\n", res.Test.Output)
} else {
slog.Error().Err(err).Msg("test failure")
}
return
}
if outputOnSuccess && !realtimeOutput {
slog.Info().Msgf("test success\n%v\n", res.Test.Output)
} else {
slog.Info().Msg("test success")
}
_, err = q.Command("root", "echo") _, err = q.Command("root", "echo")
if err != nil { if err != nil {

View File

@ -198,9 +198,11 @@ func (c Client) PushRepo(repo api.Repo) (err error) {
remote := fmt.Sprintf("git://%s/%s", addr, repo.Name) remote := fmt.Sprintf("git://%s/%s", addr, repo.Name)
log.Debug().Msgf("git proxy remote: %v", remote) log.Debug().Msgf("git proxy remote: %v", remote)
raw, err := exec.Command("git", "--work-tree", repo.Path, "push", "--force", remote). raw, err := exec.Command("git", "-c", "push.default=current",
"--work-tree", repo.Path, "push", "--force", remote).
CombinedOutput() CombinedOutput()
if err != nil { if err != nil {
log.Error().Msgf("push repo %v\n%v", repo, string(raw))
return return
} }

View File

@ -1,39 +1,74 @@
// Copyright 2023 Mikhail Klementev. All rights reserved. // Copyright 2024 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license // Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file. // (or later) that can be found in the LICENSE file.
package cmd package cmd
import ( import (
"errors"
"fmt" "fmt"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"code.dumpstack.io/tools/out-of-tree/container" "code.dumpstack.io/tools/out-of-tree/container"
"code.dumpstack.io/tools/out-of-tree/distro"
) )
type ContainerCmd struct { type ContainerCmd struct {
Filter string `help:"filter by name"` DistroID string `help:"filter by distribution"`
DistroRelease string `help:"filter by distribution release"`
List ContainerListCmd `cmd:"" help:"list containers"` List ContainerListCmd `cmd:"" help:"list containers"`
Update ContainerUpdateCmd `cmd:"" help:"update containers"`
Save ContainerSaveCmd `cmd:"" help:"save containers"` Save ContainerSaveCmd `cmd:"" help:"save containers"`
Cleanup ContainerCleanupCmd `cmd:"" help:"cleanup containers"` Cleanup ContainerCleanupCmd `cmd:"" help:"cleanup containers"`
RealtimeOutput RealtimeContainerOutputFlag `help:"show realtime output"`
} }
func (cmd ContainerCmd) Containers() (names []string) { type RealtimeContainerOutputFlag bool
func (f RealtimeContainerOutputFlag) AfterApply() (err error) {
container.Stdout = bool(f)
return
}
func (cmd ContainerCmd) Containers() (diis []container.Image, err error) {
images, err := container.Images() images, err := container.Images()
if err != nil { if err != nil {
log.Fatal().Err(err).Msg("") return
}
var dt distro.Distro
if cmd.DistroID != "" {
dt.ID, err = distro.NewID(cmd.DistroID)
if err != nil {
return
}
if cmd.DistroRelease != "" {
dt.Release = cmd.DistroRelease
}
} else if cmd.DistroRelease != "" {
err = errors.New("--distro-release has no use on its own")
return
} }
for _, img := range images { for _, img := range images {
if cmd.Filter != "" && !strings.Contains(img.Name, cmd.Filter) { if dt.ID != distro.None && dt.ID != img.Distro.ID {
log.Debug().Msgf("skip %s", img.Name)
continue continue
} }
names = append(names, img.Name)
if dt.Release != "" && dt.Release != img.Distro.Release {
log.Debug().Msgf("skip %s", img.Name)
continue
}
log.Debug().Msgf("append %s", img.Name)
diis = append(diis, img)
} }
return return
} }
@ -41,9 +76,40 @@ func (cmd ContainerCmd) Containers() (names []string) {
type ContainerListCmd struct{} type ContainerListCmd struct{}
func (cmd ContainerListCmd) Run(containerCmd *ContainerCmd) (err error) { func (cmd ContainerListCmd) Run(containerCmd *ContainerCmd) (err error) {
for _, name := range containerCmd.Containers() { images, err := containerCmd.Containers()
fmt.Println(name) if err != nil {
return
} }
for _, img := range images {
fmt.Printf("%s\n", img.Distro.String())
}
return
}
type ContainerUpdateCmd struct{}
func (cmd ContainerUpdateCmd) Run(g *Globals, containerCmd *ContainerCmd) (err error) {
images, err := containerCmd.Containers()
if err != nil {
return
}
container.UseCache = false
container.UsePrebuilt = false
// TODO move from all commands to main command line handler
container.Commands = g.Config.Docker.Commands
container.Registry = g.Config.Docker.Registry
container.Timeout = g.Config.Docker.Timeout.Duration
for _, img := range images {
_, err = img.Distro.Packages()
if err != nil {
return
}
}
return return
} }
@ -52,13 +118,18 @@ type ContainerSaveCmd struct {
} }
func (cmd ContainerSaveCmd) Run(containerCmd *ContainerCmd) (err error) { func (cmd ContainerSaveCmd) Run(containerCmd *ContainerCmd) (err error) {
for _, name := range containerCmd.Containers() { images, err := containerCmd.Containers()
nlog := log.With().Str("name", name).Logger() if err != nil {
return
}
output := filepath.Join(cmd.OutDir, name+".tar") for _, img := range images {
nlog := log.With().Str("name", img.Name).Logger()
output := filepath.Join(cmd.OutDir, img.Name+".tar")
nlog.Info().Msgf("saving to %v", output) nlog.Info().Msgf("saving to %v", output)
err = container.Save(name, output) err = container.Save(img.Name, output)
if err != nil { if err != nil {
return return
} }
@ -81,9 +152,14 @@ func (cmd ContainerSaveCmd) Run(containerCmd *ContainerCmd) (err error) {
type ContainerCleanupCmd struct{} type ContainerCleanupCmd struct{}
func (cmd ContainerCleanupCmd) Run(containerCmd *ContainerCmd) (err error) { func (cmd ContainerCleanupCmd) Run(containerCmd *ContainerCmd) (err error) {
images, err := containerCmd.Containers()
if err != nil {
return
}
var output []byte var output []byte
for _, name := range containerCmd.Containers() { for _, img := range images {
output, err = exec.Command(container.Runtime, "image", "rm", name). output, err = exec.Command(container.Runtime, "image", "rm", img.Name).
CombinedOutput() CombinedOutput()
if err != nil { if err != nil {
log.Error().Err(err).Str("output", string(output)).Msg("") log.Error().Err(err).Str("output", string(output)).Msg("")

View File

@ -193,7 +193,8 @@ func (cmd *DebugCmd) Run(g *Globals) (err error) {
return return
} }
} else { } else {
buildDir, outFile, output, err = artifact.Build(log.Logger, tmp, ka, ki, g.Config.Docker.Timeout.Duration) buildDir, outFile, output, err = artifact.Build(
log.Logger, tmp, ka, ki, g.Config.Docker.Timeout.Duration, false)
if err != nil { if err != nil {
log.Error().Err(err).Msg(output) log.Error().Err(err).Msg(output)
return return

View File

@ -39,6 +39,8 @@ type KernelCmd struct {
ContainerTimeout time.Duration `help:"container timeout"` ContainerTimeout time.Duration `help:"container timeout"`
RealtimeOutput RealtimeContainerOutputFlag `help:"show realtime output"`
List KernelListCmd `cmd:"" help:"list kernels"` List KernelListCmd `cmd:"" help:"list kernels"`
ListRemote KernelListRemoteCmd `cmd:"" help:"list remote kernels"` ListRemote KernelListRemoteCmd `cmd:"" help:"list remote kernels"`
Autogen KernelAutogenCmd `cmd:"" help:"generate kernels based on the current config"` Autogen KernelAutogenCmd `cmd:"" help:"generate kernels based on the current config"`
@ -265,8 +267,8 @@ func (cmd *KernelListCmd) Run(g *Globals) (err error) {
} }
type KernelListRemoteCmd struct { type KernelListRemoteCmd struct {
Distro string `required:"" help:"distribution"` DistroID string `required:"" help:"distribution"`
Ver string `help:"distro version"` DistroRelease string `help:"distro version"`
} }
func (cmd *KernelListRemoteCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) { func (cmd *KernelListRemoteCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
@ -279,13 +281,13 @@ func (cmd *KernelListRemoteCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error
container.UsePrebuilt = kernelCmd.PrebuiltContainers container.UsePrebuilt = kernelCmd.PrebuiltContainers
distroType, err := distro.NewID(cmd.Distro) distroType, err := distro.NewID(cmd.DistroID)
if err != nil { if err != nil {
return return
} }
km := artifact.Target{ km := artifact.Target{
Distro: distro.Distro{ID: distroType, Release: cmd.Ver}, Distro: distro.Distro{ID: distroType, Release: cmd.DistroRelease},
Kernel: artifact.Kernel{Regex: ".*"}, Kernel: artifact.Kernel{Regex: ".*"},
} }
@ -336,12 +338,12 @@ func (cmd *KernelAutogenCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
} }
type KernelGenallCmd struct { type KernelGenallCmd struct {
Distro string `help:"distribution"` DistroID string `help:"distribution"`
Ver string `help:"distro version"` DistroRelease string `help:"distro version"`
} }
func (cmd *KernelGenallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) { func (cmd *KernelGenallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
distroType, err := distro.NewID(cmd.Distro) distroType, err := distro.NewID(cmd.DistroID)
if err != nil { if err != nil {
return return
} }
@ -357,7 +359,7 @@ func (cmd *KernelGenallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
continue continue
} }
if cmd.Ver != "" && dist.Release != cmd.Ver { if cmd.DistroRelease != "" && dist.Release != cmd.DistroRelease {
continue continue
} }
@ -376,13 +378,13 @@ func (cmd *KernelGenallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
} }
type KernelInstallCmd struct { type KernelInstallCmd struct {
Distro string `required:"" help:"distribution"` DistroID string `required:"" help:"distribution"`
Ver string `required:"" help:"distro version"` DistroRelease string `required:"" help:"distro version"`
Kernel string `required:"" help:"kernel release mask"` KernelRegex string `required:"" help:"kernel release mask"`
} }
func (cmd *KernelInstallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) { func (cmd *KernelInstallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
distroType, err := distro.NewID(cmd.Distro) distroType, err := distro.NewID(cmd.DistroID)
if err != nil { if err != nil {
return return
} }
@ -390,8 +392,8 @@ func (cmd *KernelInstallCmd) Run(kernelCmd *KernelCmd, g *Globals) (err error) {
kernel.SetSigintHandler(&kernelCmd.shutdown) kernel.SetSigintHandler(&kernelCmd.shutdown)
km := artifact.Target{ km := artifact.Target{
Distro: distro.Distro{ID: distroType, Release: cmd.Ver}, Distro: distro.Distro{ID: distroType, Release: cmd.DistroRelease},
Kernel: artifact.Kernel{Regex: cmd.Kernel}, Kernel: artifact.Kernel{Regex: cmd.KernelRegex},
} }
err = kernelCmd.Generate(g, km) err = kernelCmd.Generate(g, km)
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
// Copyright 2023 Mikhail Klementev. All rights reserved. // Copyright 2024 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license // Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file. // (or later) that can be found in the LICENSE file.
@ -212,7 +212,12 @@ func center(s string, w int) string {
} }
func genOkFailCentered(name string, ok bool) (aurv aurora.Value) { func genOkFailCentered(name string, ok bool) (aurv aurora.Value) {
name = center(name, 10) if ok {
name += " OK"
} else {
name += " FAIL"
}
name = center(name, 14)
if ok { if ok {
aurv = aurora.BgGreen(aurora.Black(name)) aurv = aurora.BgGreen(aurora.Black(name))
} else { } else {
@ -225,7 +230,7 @@ func logLogEntry(l logEntry) {
distroInfo := fmt.Sprintf("%s-%s {%s}", l.Distro.ID, distroInfo := fmt.Sprintf("%s-%s {%s}", l.Distro.ID,
l.Distro.Release, l.KernelRelease) l.Distro.Release, l.KernelRelease)
artifactInfo := fmt.Sprintf("{[%s] %s}", l.Type, l.Name) artifactInfo := fmt.Sprintf("%s", l.Name)
timestamp := l.Timestamp.Format("2006-01-02 15:04") timestamp := l.Timestamp.Format("2006-01-02 15:04")
@ -257,7 +262,10 @@ func logLogEntry(l logEntry) {
additional = "(timeout)" additional = "(timeout)"
} }
colored := aurora.Sprintf("[%4d %4s] [%s] %s %-70s: %s %s", if len(distroInfo) > 40 {
distroInfo = distroInfo[:40]
}
colored := aurora.Sprintf("[%4d %4s] [%s] %s %-40s: %s %s",
l.ID, l.Tag, timestamp, artifactInfo, distroInfo, status, l.ID, l.Tag, timestamp, artifactInfo, distroInfo, status,
additional) additional)

View File

@ -12,6 +12,7 @@ import (
"math/rand" "math/rand"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings" "strings"
"time" "time"
@ -86,6 +87,12 @@ type PewCmd struct {
Threshold float64 `help:"reliablity threshold for exit code" default:"1.00"` Threshold float64 `help:"reliablity threshold for exit code" default:"1.00"`
IncludeInternalErrors bool `help:"count internal errors as part of the success rate"` IncludeInternalErrors bool `help:"count internal errors as part of the success rate"`
InternalErrorsRetries int `help:"amount of retries on internal errors" default:"3"`
OutputOnSuccess bool `help:"show output on success"`
RealtimeOutput bool `help:"show realtime output"`
LogDir string `help:"write logs to directory"`
Endless bool `help:"endless tests"` Endless bool `help:"endless tests"`
EndlessTimeout time.Duration `help:"timeout between tests" default:"1m"` EndlessTimeout time.Duration `help:"timeout between tests" default:"1m"`
@ -161,6 +168,11 @@ func (cmd *PewCmd) Run(g *Globals) (err error) {
cmd.useRemote = g.Remote cmd.useRemote = g.Remote
cmd.remoteAddr = g.RemoteAddr cmd.remoteAddr = g.RemoteAddr
if cmd.RealtimeOutput && cmd.Threads != 1 {
log.Warn().Msg("realtime output disables multithreading")
cmd.Threads = 1
}
if cmd.useRemote { if cmd.useRemote {
c := client.Client{RemoteAddr: cmd.remoteAddr} c := client.Client{RemoteAddr: cmd.remoteAddr}
cmd.Kcfg.Kernels, err = c.Kernels() cmd.Kcfg.Kernels, err = c.Kernels()
@ -400,39 +412,54 @@ func (cmd PewCmd) testArtifact(swg *sizedwaitgroup.SizedWaitGroup,
defer swg.Done() defer swg.Done()
logdir := "logs/" + cmd.Tag var logDirWriter *zerolog.ConsoleWriter
err := os.MkdirAll(logdir, os.ModePerm) if cmd.LogDir != "" {
if err != nil { logdir := filepath.Join(cmd.LogDir, cmd.Tag)
log.Error().Err(err).Msgf("mkdir %s", logdir) err := os.MkdirAll(logdir, os.ModePerm)
return if err != nil {
} log.Error().Err(err).Msgf("mkdir %s", logdir)
return
}
logfile := fmt.Sprintf("logs/%s/%s-%s-%s.log", logfile := fmt.Sprintf("logs/%s/%s-%s-%s.log",
cmd.Tag, cmd.Tag,
ki.Distro.ID.String(), ki.Distro.ID.String(),
ki.Distro.Release, ki.Distro.Release,
ki.KernelRelease, ki.KernelRelease,
) )
f, err := os.Create(logfile) f, err := os.Create(logfile)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("create %s", logfile) log.Error().Err(err).Msgf("create %s", logfile)
return return
} }
defer f.Close() defer f.Close()
slog := zerolog.New(zerolog.MultiLevelWriter( logDirWriter = &zerolog.ConsoleWriter{
&ConsoleWriter,
&FileWriter,
&zerolog.ConsoleWriter{
Out: f, Out: f,
FieldsExclude: []string{ FieldsExclude: []string{
"distro_release", "distro_release",
"distro_type", "distro_type",
"kernel", "kernel",
"command",
"workdir",
}, },
NoColor: true, NoColor: true,
}, }
)) }
var slog zerolog.Logger
if logDirWriter != nil {
slog = zerolog.New(zerolog.MultiLevelWriter(
&ConsoleWriter,
&FileWriter,
logDirWriter,
))
} else {
slog = zerolog.New(zerolog.MultiLevelWriter(
&ConsoleWriter,
&FileWriter,
))
}
switch LogLevel { switch LogLevel {
case zerolog.TraceLevel, zerolog.DebugLevel: case zerolog.TraceLevel, zerolog.DebugLevel:
@ -445,12 +472,33 @@ func (cmd PewCmd) testArtifact(swg *sizedwaitgroup.SizedWaitGroup,
Str("kernel", ki.KernelRelease). Str("kernel", ki.KernelRelease).
Logger() Logger()
ka.Process(slog, ki, retriesLeft := cmd.InternalErrorsRetries
cmd.Endless, cmd.Binary, cmd.EndlessStress, cmd.EndlessTimeout, var stop bool
func(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo, result *artifact.Result) { for !stop {
dumpResult(q, ka, ki, result, cmd.Dist, cmd.Tag, cmd.Binary, cmd.DB) ka.Process(slog, ki, cmd.OutputOnSuccess, cmd.RealtimeOutput,
}, cmd.Endless, cmd.Binary, cmd.EndlessStress, cmd.EndlessTimeout,
) func(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo, res *artifact.Result) {
if res.InternalError == nil {
cmd.dumpResult(q, ka, ki, res)
stop = true
return
}
q.Log.Warn().Err(res.InternalError).
Str("panic", fmt.Sprintf("%v", q.KernelPanic)).
Str("timeout", fmt.Sprintf("%v", q.KilledByTimeout)).
Int("retries_left", retriesLeft).
Msg("internal")
if retriesLeft == 0 {
state.InternalErrors += 1
stop = true
return
}
retriesLeft -= 1
},
)
}
} }
func shuffleKernels(a []distro.KernelInfo) []distro.KernelInfo { func shuffleKernels(a []distro.KernelInfo) []distro.KernelInfo {
@ -546,76 +594,70 @@ func genOkFail(name string, ok bool) (aurv aurora.Value) {
return return
} }
func dumpResult(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo, func (cmd PewCmd) dumpResult(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo, res *artifact.Result) {
res *artifact.Result, dist, tag, binary string, db *sql.DB) { state.Overall += 1
// TODO refactor if res.Test.Ok {
state.Success += 1
}
if res.InternalError != nil { colored := ""
q.Log.Warn().Err(res.InternalError). switch ka.Type {
Str("panic", fmt.Sprintf("%v", q.KernelPanic)). case artifact.KernelExploit:
Str("timeout", fmt.Sprintf("%v", q.KilledByTimeout)). colored = aurora.Sprintf("%s %s",
Msg("internal") genOkFail("BUILD", res.Build.Ok),
res.InternalErrorString = res.InternalError.Error() genOkFail("LPE", res.Test.Ok))
state.InternalErrors += 1 case artifact.KernelModule:
colored = aurora.Sprintf("%s %s %s",
genOkFail("BUILD", res.Build.Ok),
genOkFail("INSMOD", res.Run.Ok),
genOkFail("TEST", res.Test.Ok))
case artifact.Script:
colored = aurora.Sprintf("%s",
genOkFail("", res.Test.Ok))
}
additional := ""
if q.KernelPanic {
additional = "(panic)"
} else if q.KilledByTimeout {
additional = "(timeout)"
}
if additional != "" {
q.Log.Info().Msgf("%v %v", colored, additional)
} else { } else {
colored := "" q.Log.Info().Msgf("%v", colored)
state.Overall += 1
if res.Test.Ok {
state.Success += 1
}
switch ka.Type {
case artifact.KernelExploit:
colored = aurora.Sprintf("%s %s",
genOkFail("BUILD", res.Build.Ok),
genOkFail("LPE", res.Test.Ok))
case artifact.KernelModule:
colored = aurora.Sprintf("%s %s %s",
genOkFail("BUILD", res.Build.Ok),
genOkFail("INSMOD", res.Run.Ok),
genOkFail("TEST", res.Test.Ok))
case artifact.Script:
colored = aurora.Sprintf("%s",
genOkFail("", res.Test.Ok))
}
additional := ""
if q.KernelPanic {
additional = "(panic)"
} else if q.KilledByTimeout {
additional = "(timeout)"
}
if additional != "" {
q.Log.Info().Msgf("%v %v", colored, additional)
} else {
q.Log.Info().Msgf("%v", colored)
}
} }
err := addToLog(db, q, ka, ki, res, tag) err := addToLog(cmd.DB, q, ka, ki, res, cmd.Tag)
if err != nil { if err != nil {
q.Log.Warn().Err(err).Msgf("[db] addToLog (%v)", ka) q.Log.Error().Err(err).Msgf("[db] addToLog (%v)", ka)
} }
if binary == "" && dist != pathDevNull { if cmd.Binary != "" {
err = os.MkdirAll(dist, os.ModePerm) return
if err != nil { }
log.Warn().Err(err).Msgf("os.MkdirAll (%v)", ka)
}
path := fmt.Sprintf("%s/%s-%s-%s", dist, ki.Distro.ID, if cmd.Dist == pathDevNull { // why?
ki.Distro.Release, ki.KernelRelease) return
if ka.Type != artifact.KernelExploit { }
path += ".ko"
}
err = artifact.CopyFile(res.BuildArtifact, path) err = os.MkdirAll(cmd.Dist, os.ModePerm)
if err != nil { if err != nil {
log.Warn().Err(err).Msgf("copy file (%v)", ka) log.Error().Err(err).Msgf("os.MkdirAll (%v)", ka)
} return
}
path := fmt.Sprintf("%s/%s-%s-%s", cmd.Dist, ki.Distro.ID,
ki.Distro.Release, ki.KernelRelease)
if ka.Type != artifact.KernelExploit {
path += ".ko"
}
err = artifact.CopyFile(res.BuildArtifact, path)
if err != nil {
log.Error().Err(err).Msgf("copy file (%v)", ka)
return
} }
} }

View File

@ -35,9 +35,12 @@ type OutOfTree struct {
Timeout artifact.Duration Timeout artifact.Duration
Registry string Registry string
// Commands that will be executed before // Commands that are executed before (prepend) and after (append) the
// the base layer of Dockerfile // base layer of the Dockerfile.
Commands []distro.Command Commands struct {
Prepend []distro.Command
Append []distro.Command
}
} }
} }

View File

@ -14,6 +14,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"github.com/cavaliergopher/grab/v3" "github.com/cavaliergopher/grab/v3"
@ -32,7 +33,12 @@ var Registry = ""
var Timeout time.Duration var Timeout time.Duration
var Commands []distro.Command // Commands that are executed before (prepend) and after (append) the
// base layer of the Dockerfile.
var Commands struct {
Prepend []distro.Command
Append []distro.Command
}
var UseCache = true var UseCache = true
@ -40,6 +46,8 @@ var UsePrebuilt = true
var Prune = true var Prune = true
var Stdout = false
type Image struct { type Image struct {
Name string Name string
Distro distro.Distro Distro distro.Distro
@ -97,13 +105,23 @@ func Load(localpath string, name string) (err error) {
return return
} }
cmd = exec.Command(Runtime, "tag", "localhost/"+name, name) if strings.Contains(Runtime, "docker") {
log.Debug().Msgf("%v", cmd) var err2 error
cmd = exec.Command(Runtime, "tag", "localhost/"+name, name)
log.Debug().Msgf("%v", cmd)
raw, err = cmd.CombinedOutput() raw, err2 = cmd.CombinedOutput()
if err != nil { if err2 != nil {
log.Debug().Err(err).Msg(string(raw)) log.Debug().Err(err2).Msg(string(raw))
return }
cmd = exec.Command(Runtime, "rmi", "localhost/"+name)
log.Debug().Msgf("%v", cmd)
raw, err2 = cmd.CombinedOutput()
if err2 != nil {
log.Debug().Err(err2).Msg(string(raw))
}
} }
return return
@ -160,7 +178,15 @@ type Container struct {
// Additional arguments // Additional arguments
Args []string Args []string
// Base of container is local-only
LocalBase bool
Log zerolog.Logger Log zerolog.Logger
commandsOutput struct {
listener chan string
mu sync.Mutex
}
} }
func New(dist distro.Distro) (c Container, err error) { func New(dist distro.Distro) (c Container, err error) {
@ -219,6 +245,43 @@ func NewFromKernelInfo(ki distro.KernelInfo) (
return return
} }
// c.SetCommandsOutputHandler(func(s string) { fmt.Println(s) })
// defer c.CloseCommandsOutputHandler()
func (c *Container) SetCommandsOutputHandler(handler func(s string)) {
c.commandsOutput.mu.Lock()
defer c.commandsOutput.mu.Unlock()
c.commandsOutput.listener = make(chan string)
go func(l chan string) {
for m := range l {
if m != "" {
handler(m)
}
}
}(c.commandsOutput.listener)
}
func (c *Container) CloseCommandsOutputHandler() {
c.commandsOutput.mu.Lock()
defer c.commandsOutput.mu.Unlock()
close(c.commandsOutput.listener)
c.commandsOutput.listener = nil
}
func (c *Container) handleCommandsOutput(m string) {
if c.commandsOutput.listener == nil {
return
}
c.commandsOutput.mu.Lock()
defer c.commandsOutput.mu.Unlock()
if c.commandsOutput.listener != nil {
c.commandsOutput.listener <- m
}
}
func (c Container) Name() string { func (c Container) Name() string {
return c.name return c.name
} }
@ -286,9 +349,15 @@ func (c Container) Build(image string, envs, runs []string) (err error) {
} }
cf += image + "\n" cf += image + "\n"
for _, c := range Commands { for _, cmd := range Commands.Prepend {
// TODO check for distro type if cmd.Distro.ID != distro.None && cmd.Distro.ID != c.dist.ID {
cf += "RUN " + c.Command + "\n" continue
}
if cmd.Distro.Release != "" && cmd.Distro.Release != c.dist.Release {
continue
}
cf += "RUN " + cmd.Command + "\n"
} }
for _, e := range envs { for _, e := range envs {
@ -299,6 +368,17 @@ func (c Container) Build(image string, envs, runs []string) (err error) {
cf += "RUN " + c + "\n" cf += "RUN " + c + "\n"
} }
for _, cmd := range Commands.Append {
if cmd.Distro.ID != distro.None && cmd.Distro.ID != c.dist.ID {
continue
}
if cmd.Distro.Release != "" && cmd.Distro.Release != c.dist.Release {
continue
}
cf += "RUN " + cmd.Command + "\n"
}
buf, err := os.ReadFile(cfile) buf, err := os.ReadFile(cfile)
if err != nil { if err != nil {
err = os.WriteFile(cfile, []byte(cf), os.ModePerm) err = os.WriteFile(cfile, []byte(cf), os.ModePerm)
@ -351,7 +431,10 @@ func (c Container) build(imagePath string) (output string, err error) {
args := []string{"build"} args := []string{"build"}
if !UseCache { if !UseCache {
args = append(args, "--pull", "--no-cache") if !c.LocalBase {
args = append(args, "--pull")
}
args = append(args, "--no-cache")
} }
args = append(args, "-t", c.name, imagePath) args = append(args, "-t", c.name, imagePath)
@ -376,6 +459,10 @@ func (c Container) build(imagePath string) (output string, err error) {
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
m := scanner.Text() m := scanner.Text()
if Stdout {
fmt.Println(m)
}
c.handleCommandsOutput(m)
output += m + "\n" output += m + "\n"
flog.Trace().Str("stdout", m).Msg("") flog.Trace().Str("stdout", m).Msg("")
} }
@ -385,7 +472,7 @@ func (c Container) build(imagePath string) (output string, err error) {
return return
} }
func (c Container) Run(workdir string, cmds []string) (out string, err error) { func (c *Container) Run(workdir string, cmds []string) (out string, err error) {
flog := c.Log.With(). flog := c.Log.With().
Str("workdir", workdir). Str("workdir", workdir).
Str("command", fmt.Sprintf("%v", cmds)). Str("command", fmt.Sprintf("%v", cmds)).
@ -449,16 +536,17 @@ func (c Container) Run(workdir string, cmds []string) (out string, err error) {
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
m := scanner.Text() m := scanner.Text()
if Stdout {
fmt.Println(m)
}
c.handleCommandsOutput(m)
out += m + "\n" out += m + "\n"
flog.Trace().Str("stdout", m).Msg("") flog.Trace().Str("container stdout", m).Msg("")
} }
}() }()
err = cmd.Wait() err = cmd.Wait()
if err != nil { if err != nil {
e := fmt.Sprintf("error `%v` for cmd `%v` with output `%v`",
err, cmds, out)
err = errors.New(e)
return return
} }

View File

@ -131,7 +131,7 @@ func (pj *jobProcessor) Process(res *Resources) (err error) {
var result *artifact.Result var result *artifact.Result
var dq *qemu.System var dq *qemu.System
pj.job.Artifact.Process(pj.log, pj.job.Target, false, "", "", 0, pj.job.Artifact.Process(pj.log, pj.job.Target, false, false, false, "", "", 0,
func(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo, func(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo,
res *artifact.Result) { res *artifact.Result) {

View File

@ -57,6 +57,8 @@ func (suse OpenSUSE) Packages() (pkgs []string, err error) {
if err != nil { if err != nil {
return return
} }
c.LocalBase = true
} else if strings.HasPrefix(suse.release, "13") { } else if strings.HasPrefix(suse.release, "13") {
name = "opensuse:13" name = "opensuse:13"
cnturl := cache.ContainerURL("openSUSE-13.2") cnturl := cache.ContainerURL("openSUSE-13.2")
@ -64,6 +66,8 @@ func (suse OpenSUSE) Packages() (pkgs []string, err error) {
if err != nil { if err != nil {
return return
} }
c.LocalBase = true
} else if strings.HasPrefix(suse.release, "42") { } else if strings.HasPrefix(suse.release, "42") {
name = "opensuse/leap:42" name = "opensuse/leap:42"
} else if strings.HasPrefix(suse.release, "15") { } else if strings.HasPrefix(suse.release, "15") {
@ -83,7 +87,32 @@ func (suse OpenSUSE) Packages() (pkgs []string, err error) {
return return
} }
pkgs = append(pkgs, strings.Fields(output)...) // TODO Find a way for non-interactive installation of
// retracted kernels
retracted := []string{
"5.14.21-150400.24.49.3",
"5.14.21-150400.24.84.1",
"5.14.21-150500.55.22.1",
"5.3.18-150300.59.81.1",
"5.3.18-59.30.1",
"5.3.18-lp152.98.1",
}
for _, k := range strings.Fields(output) {
skip := false
for _, rk := range retracted {
if rk == k {
skip = true
break
}
}
if skip {
continue
}
pkgs = append(pkgs, k)
}
return return
} }

View File

@ -35,13 +35,13 @@ type CLI struct {
cmd.Globals cmd.Globals
Pew cmd.PewCmd `cmd:"" help:"build, run, and test module/exploit"` Pew cmd.PewCmd `cmd:"" help:"build, run, and test module/exploit"`
Kernel cmd.KernelCmd `cmd:"" help:"manipulate kernels"` Kernel cmd.KernelCmd `cmd:"" aliases:"kernels" help:"manipulate kernels"`
Debug cmd.DebugCmd `cmd:"" help:"debug environment"` Debug cmd.DebugCmd `cmd:"" help:"debug environment"`
Log cmd.LogCmd `cmd:"" help:"query logs"` Log cmd.LogCmd `cmd:"" help:"query logs"`
Pack cmd.PackCmd `cmd:"" help:"exploit pack test"` Pack cmd.PackCmd `cmd:"" help:"exploit pack test"`
Gen cmd.GenCmd `cmd:"" help:"generate .out-of-tree.toml skeleton"` Gen cmd.GenCmd `cmd:"" help:"generate .out-of-tree.toml skeleton"`
Image cmd.ImageCmd `cmd:"" help:"manage images"` Image cmd.ImageCmd `cmd:"" aliases:"images" help:"manage images"`
Container cmd.ContainerCmd `cmd:"" help:"manage containers"` Container cmd.ContainerCmd `cmd:"" aliases:"containers" help:"manage containers"`
Distro cmd.DistroCmd `cmd:"" help:"distro-related helpers"` Distro cmd.DistroCmd `cmd:"" help:"distro-related helpers"`
Daemon cmd.DaemonCmd `cmd:"" help:"run daemon"` Daemon cmd.DaemonCmd `cmd:"" help:"run daemon"`

View File

@ -101,6 +101,16 @@ type System struct {
Stdout, Stderr string Stdout, Stderr string
qemuOutput struct {
listener chan string
mu sync.Mutex
}
commandsOutput struct {
listener chan string
mu sync.Mutex
}
// accessible after qemu is closed // accessible after qemu is closed
exitErr error exitErr error
@ -138,6 +148,80 @@ func NewSystem(arch arch, kernel Kernel, drivePath string) (q *System, err error
return return
} }
// q.SetQemuOutputHandler(func(s string) { fmt.Println(s) })
// defer q.CloseQemuOutputHandler()
func (q *System) SetQemuOutputHandler(handler func(s string)) {
q.qemuOutput.mu.Lock()
defer q.qemuOutput.mu.Unlock()
q.qemuOutput.listener = make(chan string)
go func(l chan string) {
for m := range l {
if m != "" {
handler(m)
}
}
}(q.qemuOutput.listener)
}
func (q *System) CloseQemuOutputHandler() {
q.qemuOutput.mu.Lock()
defer q.qemuOutput.mu.Unlock()
close(q.qemuOutput.listener)
q.qemuOutput.listener = nil
}
func (q *System) handleQemuOutput(m string) {
if q.qemuOutput.listener == nil {
return
}
q.qemuOutput.mu.Lock()
defer q.qemuOutput.mu.Unlock()
if q.qemuOutput.listener != nil {
q.qemuOutput.listener <- m
}
}
// q.SetCommandsOutputHandler(func(s string) { fmt.Println(s) })
// defer q.CloseCommandsOutputHandler()
func (q *System) SetCommandsOutputHandler(handler func(s string)) {
q.commandsOutput.mu.Lock()
defer q.commandsOutput.mu.Unlock()
q.commandsOutput.listener = make(chan string)
go func(l chan string) {
for m := range l {
if m != "" {
handler(m)
}
}
}(q.commandsOutput.listener)
}
func (q *System) CloseCommandsOutputHandler() {
q.commandsOutput.mu.Lock()
defer q.commandsOutput.mu.Unlock()
close(q.commandsOutput.listener)
q.commandsOutput.listener = nil
}
func (q *System) handleCommandsOutput(m string) {
if q.commandsOutput.listener == nil {
return
}
q.commandsOutput.mu.Lock()
defer q.commandsOutput.mu.Unlock()
if q.commandsOutput.listener != nil {
q.commandsOutput.listener <- m
}
}
func (q *System) SetSSHAddrPort(addr string, port int) (err error) { func (q *System) SetSSHAddrPort(addr string, port int) (err error) {
// TODO validate // TODO validate
q.SSH.AddrPort = fmt.Sprintf("%s:%d", addr, port) q.SSH.AddrPort = fmt.Sprintf("%s:%d", addr, port)
@ -312,7 +396,8 @@ func (q *System) Start() (err error) {
go func() { go func() {
scanner := bufio.NewScanner(q.pipe.stdout) scanner := bufio.NewScanner(q.pipe.stdout)
for scanner.Scan() { for scanner.Scan() {
m := scanner.Text() m := strings.TrimSpace(scanner.Text())
q.handleQemuOutput(m)
q.Stdout += m + "\n" q.Stdout += m + "\n"
q.Log.Trace().Str("stdout", m).Msg("qemu") q.Log.Trace().Str("stdout", m).Msg("qemu")
go q.checkOopsPanic(m) go q.checkOopsPanic(m)
@ -322,7 +407,8 @@ func (q *System) Start() (err error) {
go func() { go func() {
scanner := bufio.NewScanner(q.pipe.stderr) scanner := bufio.NewScanner(q.pipe.stderr)
for scanner.Scan() { for scanner.Scan() {
m := scanner.Text() m := strings.TrimSpace(scanner.Text())
q.handleQemuOutput(m)
q.Stderr += m + "\n" q.Stderr += m + "\n"
q.Log.Trace().Str("stderr", m).Msg("qemu") q.Log.Trace().Str("stderr", m).Msg("qemu")
} }
@ -474,7 +560,8 @@ func (q System) Command(user, cmd string) (output string, err error) {
scanner := bufio.NewScanner(stdout) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
m := scanner.Text() m := strings.TrimSpace(scanner.Text())
q.handleCommandsOutput(m)
output += m + "\n" output += m + "\n"
flog.Trace().Str("stdout", m).Msg("qemu command") flog.Trace().Str("stdout", m).Msg("qemu command")
} }
@ -487,7 +574,8 @@ func (q System) Command(user, cmd string) (output string, err error) {
scanner := bufio.NewScanner(stderr) scanner := bufio.NewScanner(stderr)
for scanner.Scan() { for scanner.Scan() {
m := scanner.Text() m := strings.TrimSpace(scanner.Text())
q.handleCommandsOutput(m)
output += m + "\n" output += m + "\n"
// Note: it prints stderr as stdout // Note: it prints stderr as stdout
flog.Trace().Str("stdout", m).Msg("qemu command") flog.Trace().Str("stdout", m).Msg("qemu command")

View File

@ -28,6 +28,8 @@ RUN sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/UseDNS/d' $TMPDIR/etc/ssh/sshd_config
RUN echo UseDNS no >> $TMPDIR/etc/ssh/sshd_config
# network workaround # network workaround
RUN chmod +x $TMPDIR/etc/rc.local RUN chmod +x $TMPDIR/etc/rc.local

View File

@ -4,6 +4,6 @@ set -eux
cd "$(dirname "$0")" cd "$(dirname "$0")"
sudo docker build -t gen-centos6-image . sudo podman build -t gen-centos6-image .
sudo docker run --privileged -v $(pwd):/shared -t gen-centos6-image sudo podman run --privileged -v $(pwd):/shared -t gen-centos6-image
tar -Szcf out_of_tree_centos_6.img.tar.gz out_of_tree_centos_6.img tar -Szcf out_of_tree_centos_6.img.tar.gz out_of_tree_centos_6.img

View File

@ -13,6 +13,11 @@
# #
FROM centos:7 FROM centos:7
RUN sed -i 's/enabled=1/enabled=0/' /etc/yum.repos.d/* || true
RUN sed -i 's/name/enabled=0\nname/' /etc/yum.repos.d/* || true
RUN echo -e '[7.9.2009-os]\nbaseurl=https://vault.centos.org/7.9.2009/os/$basearch/\ngpgcheck=0' >> /etc/yum.repos.d/oot.repo
RUN echo -e '[7.9.2009-updates]\nbaseurl=https://vault.centos.org/7.9.2009/updates/$basearch/\ngpgcheck=0' >> /etc/yum.repos.d/oot.repo
RUN yum -y update RUN yum -y update
RUN yum -y groupinstall "Development Tools" RUN yum -y groupinstall "Development Tools"
RUN yum -y install qemu-img e2fsprogs RUN yum -y install qemu-img e2fsprogs
@ -21,13 +26,13 @@ ENV TMPDIR=/tmp/centos
RUN yum --installroot=$TMPDIR \ RUN yum --installroot=$TMPDIR \
--releasever=7 \ --releasever=7 \
--disablerepo='*' \
--enablerepo=base \
-y groupinstall Base -y groupinstall Base
RUN rm $TMPDIR/etc/yum.repos.d/*
RUN cp /etc/yum.repos.d/* $TMPDIR/etc/yum.repos.d/
RUN yum --installroot=$TMPDIR \ RUN yum --installroot=$TMPDIR \
--releasever=7 \ --releasever=7 \
--disablerepo='*' \
--enablerepo=base \
-y install openssh-server openssh-clients -y install openssh-server openssh-clients
RUN chroot $TMPDIR /bin/sh -c 'useradd -m user' RUN chroot $TMPDIR /bin/sh -c 'useradd -m user'
@ -37,6 +42,8 @@ RUN sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/UseDNS/d' $TMPDIR/etc/ssh/sshd_config
RUN echo UseDNS no >> $TMPDIR/etc/ssh/sshd_config
# network workaround # network workaround
RUN chmod +x $TMPDIR/etc/rc.local RUN chmod +x $TMPDIR/etc/rc.local

View File

@ -0,0 +1,9 @@
#!/bin/sh
set -eux
cd "$(dirname "$0")"
sudo podman build -t gen-centos7-image .
sudo podman run --privileged -v $(pwd):/shared -t gen-centos7-image
tar -Szcf out_of_tree_centos_7.img.tar.gz out_of_tree_centos_7.img

View File

@ -28,6 +28,8 @@ RUN sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/UseDNS/d' $TMPDIR/etc/ssh/sshd_config
RUN echo UseDNS no >> $TMPDIR/etc/ssh/sshd_config
# network workaround # network workaround
RUN chmod +x $TMPDIR/etc/rc.local RUN chmod +x $TMPDIR/etc/rc.local

View File

@ -4,6 +4,6 @@ set -eux
cd "$(dirname "$0")" cd "$(dirname "$0")"
sudo docker build -t gen-centos8-image . sudo podman build -t gen-centos8-image .
sudo docker run --privileged -v $(pwd):/shared -t gen-centos8-image sudo podman run --privileged -v $(pwd):/shared -t gen-centos8-image
tar -Szcf out_of_tree_centos_8.img.tar.gz out_of_tree_centos_8.img tar -Szcf out_of_tree_centos_8.img.tar.gz out_of_tree_centos_8.img

View File

@ -11,6 +11,8 @@ sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
sed -i '/UseDNS/d' $TMPDIR/etc/ssh/sshd_config
echo UseDNS no >> $TMPDIR/etc/ssh/sshd_config
echo '#!/bin/sh' > $TMPDIR/etc/rc.local echo '#!/bin/sh' > $TMPDIR/etc/rc.local
echo 'dhclient' >> $TMPDIR/etc/rc.local echo 'dhclient' >> $TMPDIR/etc/rc.local

View File

@ -13,6 +13,7 @@ RUN yum --installroot=$TMPDIR \
--releasever=_VERSION_ \ --releasever=_VERSION_ \
--disablerepo='*' \ --disablerepo='*' \
--enablerepo=ol_VERSION__baseos_latest \ --enablerepo=ol_VERSION__baseos_latest \
--enablerepo=ol_VERSION__appstream \
-y groupinstall Base -y groupinstall Base
RUN cp /etc/yum.repos.d/* $TMPDIR/etc/yum.repos.d/ RUN cp /etc/yum.repos.d/* $TMPDIR/etc/yum.repos.d/
@ -21,6 +22,7 @@ RUN yum --installroot=$TMPDIR \
--releasever=_VERSION_ \ --releasever=_VERSION_ \
--disablerepo='*' \ --disablerepo='*' \
--enablerepo=ol_VERSION__baseos_latest \ --enablerepo=ol_VERSION__baseos_latest \
--enablerepo=ol_VERSION__appstream \
-y install openssh-server openssh-clients dhclient yum -y install openssh-server openssh-clients dhclient yum
RUN chroot $TMPDIR /bin/sh -c 'useradd -m user' RUN chroot $TMPDIR /bin/sh -c 'useradd -m user'
@ -30,6 +32,8 @@ RUN sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config RUN sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config RUN echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
RUN sed -i '/UseDNS/d' $TMPDIR/etc/ssh/sshd_config
RUN echo UseDNS no >> $TMPDIR/etc/ssh/sshd_config
# network workaround # network workaround
RUN chmod +x $TMPDIR/etc/rc.local RUN chmod +x $TMPDIR/etc/rc.local

View File

@ -11,9 +11,11 @@ for version in 6 7 8 9; do
if [[ $version -eq 6 ]]; then if [[ $version -eq 6 ]]; then
sed -i 's/baseos_latest/u10_base/' $version/Dockerfile sed -i 's/baseos_latest/u10_base/' $version/Dockerfile
sed -i '/appstream/d' $version/Dockerfile
fi fi
if [[ $version -eq 7 ]]; then if [[ $version -eq 7 ]]; then
sed -i 's/baseos_latest/u9_base/' $version/Dockerfile sed -i 's/baseos_latest/u9_base/' $version/Dockerfile
sed -i '/appstream/d' $version/Dockerfile
fi fi
podman build -t gen-oraclelinux${version}-image $version podman build -t gen-oraclelinux${version}-image $version

View File

@ -1,35 +0,0 @@
# Copyright 2018 Mikhail Klementev. All rights reserved.
# Use of this source code is governed by a AGPLv3 license
# (or later) that can be found in the LICENSE file.
#
# Usage:
#
# $ docker build -t gen-ubuntu1404-image .
# $ docker run --privileged -v $(pwd):/shared -t gen-ubuntu1404-image
#
# ubuntu1404.img will be created in current directory. You can change $(pwd) to
# different directory to use different destination for image.
#
FROM ubuntu:14.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update
RUN apt-get install -y debootstrap qemu
ENV TMPDIR=/tmp/ubuntu
ENV IMAGEDIR=/tmp/image
ENV IMAGE=/shared/out_of_tree_ubuntu_14__04.img
ENV REPOSITORY=http://archive.ubuntu.com/ubuntu
ENV RELEASE=trusty
RUN mkdir $IMAGEDIR
# Must be executed with --privileged because of /dev/loop
CMD debootstrap --include=openssh-server,policykit-1 \
$RELEASE $TMPDIR $REPOSITORY && \
/shared/setup.sh $TMPDIR && \
qemu-img create $IMAGE 2G && \
mkfs.ext4 -F $IMAGE && \
mount -o loop $IMAGE $IMAGEDIR && \
cp -a $TMPDIR/* $IMAGEDIR/ && \
umount $IMAGEDIR

View File

@ -1,17 +0,0 @@
#!/bin/sh -eux
# Copyright 2018 Mikhail Klementev. All rights reserved.
# Use of this source code is governed by a AGPLv3 license
# (or later) that can be found in the LICENSE file.
TMPDIR=$1
chroot $TMPDIR /bin/sh -c 'useradd -m user'
sed -i 's/root:\*:/root::/' $TMPDIR/etc/shadow
sed -i 's/user:!!:/user::/' $TMPDIR/etc/shadow
echo auth sufficient pam_permit.so > $TMPDIR/etc/pam.d/sshd
sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
echo '#!/bin/sh' > $TMPDIR/etc/rc.local
echo 'dhclient eth0' >> $TMPDIR/etc/rc.local
chmod +x $TMPDIR/etc/rc.local

View File

@ -7,27 +7,29 @@ script_dir = os.path.dirname(os.path.realpath(__file__))
os.chdir(script_dir) os.chdir(script_dir)
releases = [ releases = [
# ('12.04', 'precies'), ('12.04', 'precise', 'http://old-releases.ubuntu.com/ubuntu'),
# ('14.04', 'trusty'), ('14.04', 'trusty', 'http://archive.ubuntu.com/ubuntu'),
# ('16.04', 'xenial'), ('16.04', 'xenial', 'http://archive.ubuntu.com/ubuntu'),
# ('18.04', 'bionic'), ('18.04', 'bionic', 'http://archive.ubuntu.com/ubuntu'),
# ('20.04', 'focal'), ('20.04', 'focal', 'http://archive.ubuntu.com/ubuntu'),
# ('22.04', 'jammy'), ('22.04', 'jammy', 'http://archive.ubuntu.com/ubuntu'),
('24.04', 'noble'), ('24.04', 'noble', 'http://archive.ubuntu.com/ubuntu')
] ]
template = ''' template = '''
FROM ubuntu:{version} FROM ubuntu:{version}
RUN sed -i 's;http://archive.ubuntu.com/ubuntu;{repository};' /etc/apt/sources.list
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN apt update RUN apt-get update
RUN apt install -y debootstrap qemu-utils RUN apt-get install -y debootstrap qemu-utils
RUN apt install -y linux-image-generic RUN apt-get install -y linux-image-generic
ENV TMPDIR=/tmp/ubuntu ENV TMPDIR=/tmp/ubuntu
ENV IMAGEDIR=/tmp/image ENV IMAGEDIR=/tmp/image
ENV IMAGE=/shared/out_of_tree_ubuntu_{img_version}.img ENV IMAGE=/shared/out_of_tree_ubuntu_{img_version}.img
ENV REPOSITORY=http://archive.ubuntu.com/ubuntu ENV REPOSITORY={repository}
ENV RELEASE={codename} ENV RELEASE={codename}
RUN mkdir $IMAGEDIR RUN mkdir $IMAGEDIR
@ -47,7 +49,7 @@ def run_cmd(cmd):
print(f"+ {cmd}") print(f"+ {cmd}")
subprocess.run(cmd, shell=True, check=True, executable='/bin/bash') subprocess.run(cmd, shell=True, check=True, executable='/bin/bash')
for version, codename in releases: for version, codename, repository in releases:
numeric_version = version.replace('.', '') numeric_version = version.replace('.', '')
img_version=version.replace(".","__") img_version=version.replace(".","__")
@ -55,6 +57,7 @@ for version, codename in releases:
version=version, version=version,
img_version=img_version, img_version=img_version,
codename=codename, codename=codename,
repository=repository,
numeric_version=numeric_version) numeric_version=numeric_version)
os.makedirs(str(version), exist_ok=True) os.makedirs(str(version), exist_ok=True)

View File

@ -11,6 +11,8 @@ sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
sed -i '/UseDNS/d' $TMPDIR/etc/ssh/sshd_config
echo UseDNS no >> $TMPDIR/etc/ssh/sshd_config
echo '#!/bin/sh' > $TMPDIR/etc/rc.local echo '#!/bin/sh' > $TMPDIR/etc/rc.local
echo 'dhclient || dhcpcd' >> $TMPDIR/etc/rc.local echo 'dhclient || dhcpcd' >> $TMPDIR/etc/rc.local