Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
9e1d71d1b2
|
|||
9c70af4f6f
|
|||
7b8cf96b4a
|
|||
7b6e3a9ad6
|
|||
b117739c49
|
|||
b28c47e64d
|
|||
4b14187dad
|
|||
950b1e5e83
|
|||
bf90a10692 | |||
3e7c564a5a
|
|||
dc73413114
|
|||
104e70f861
|
|||
365c9d0e95
|
|||
5bad772125
|
|||
f3b0c07af2
|
|||
f3d67cc3c2
|
|||
12b5bd2a99
|
|||
b05c44ab9d
|
|||
19535fc75c
|
|||
5e6a9dec93
|
|||
0f89a868bd
|
|||
14b8010fee
|
|||
7fd8614e3c
|
|||
3d958c1e10
|
12
.github/workflows/macos.yml
vendored
Normal file
12
.github/workflows/macos.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
name: macOS
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macOS-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build
|
23
.github/workflows/ubuntu.yml
vendored
Normal file
23
.github/workflows/ubuntu.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: Ubuntu
|
||||||
|
|
||||||
|
on: [push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: go build
|
||||||
|
|
||||||
|
- name: Install dependencies for tests
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install qemu
|
||||||
|
|
||||||
|
- name: Bootstrap
|
||||||
|
run: ./tools/qemu-debian-img/bootstrap.sh
|
||||||
|
|
||||||
|
- name: Test
|
||||||
|
run: go test -parallel 1 -v ./...
|
54
CHANGELOG.md
54
CHANGELOG.md
@ -4,6 +4,60 @@
|
|||||||
|
|
||||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [1.2.0] 2019-11-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Flag for Verbose output. Right now only qemu status messages is
|
||||||
|
implemented.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Kpti settings was not affected for regular runs.
|
||||||
|
|
||||||
|
## [1.1.2] 2019-09-05
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added policykit-1 to rootfs for Ubuntu.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Avoided slow mirrors with use of mirror://mirrors.ubuntu.com for
|
||||||
|
Ubuntu 16.04 and newer.
|
||||||
|
|
||||||
|
## [1.1.1] 2019-08-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- macOS support.
|
||||||
|
|
||||||
|
## [1.1.0] 2019-08-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Global configuration file (~/.out-of-tree/out-of-tree.toml) allow to
|
||||||
|
set up default values for settings.
|
||||||
|
|
||||||
|
- rootfs generator for Ubuntu 14.04.
|
||||||
|
|
||||||
|
- Parameter for setting up docker registry server.
|
||||||
|
|
||||||
|
- Support for (distro-specific) custom docker commands that will be
|
||||||
|
executed before the base template.
|
||||||
|
|
||||||
|
- Parameter for setting up a reliability threshold for exit code.
|
||||||
|
|
||||||
|
- Parameter for setting up global timeout, after which no new tasks
|
||||||
|
will be started.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Spelling in output.
|
||||||
|
|
||||||
|
- Now kernel generation will not fail if there are no directory
|
||||||
|
/lib/modules inside the container.
|
||||||
|
|
||||||
## [1.0.0] 2019-08-20
|
## [1.0.0] 2019-08-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[](https://app.codacy.com/app/jollheef/out-of-tree?utm_source=github.com&utm_medium=referral&utm_content=jollheef/out-of-tree&utm_campaign=Badge_Grade_Dashboard)
|
[](https://app.codacy.com/app/jollheef/out-of-tree?utm_source=github.com&utm_medium=referral&utm_content=jollheef/out-of-tree&utm_campaign=Badge_Grade_Dashboard)
|
||||||
[](https://travis-ci.org/jollheef/out-of-tree)
|
[](https://travis-ci.com/jollheef/out-of-tree)
|
||||||
[](https://goreportcard.com/report/code.dumpstack.io/tools/out-of-tree)
|
[](https://goreportcard.com/report/code.dumpstack.io/tools/out-of-tree)
|
||||||
[](https://out-of-tree.readthedocs.io/en/latest/?badge=latest)
|
[](https://out-of-tree.readthedocs.io/en/latest/?badge=latest)
|
||||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R8W2UQPZ5X5JE&source=url)
|
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=R8W2UQPZ5X5JE&source=url)
|
||||||
@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
out-of-tree kernel {module, exploit} development tool
|
out-of-tree kernel {module, exploit} development tool
|
||||||
|
|
||||||
|
out-of-tree is for automating some routine actions for creating development environments for debugging kernel modules and exploits, generating reliability statistics for exploits, and also provides the ability to easily integrate into CI (Continuous Integration).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
77
config/out-of-tree.go
Normal file
77
config/out-of-tree.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2019 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.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os/user"
|
||||||
|
|
||||||
|
"github.com/naoina/toml"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DockerCommand struct {
|
||||||
|
DistroType DistroType
|
||||||
|
Command string
|
||||||
|
}
|
||||||
|
|
||||||
|
type OutOfTree struct {
|
||||||
|
Kernels string
|
||||||
|
UserKernels string
|
||||||
|
|
||||||
|
Database string
|
||||||
|
|
||||||
|
Qemu struct {
|
||||||
|
Timeout string
|
||||||
|
}
|
||||||
|
|
||||||
|
Docker struct {
|
||||||
|
Timeout string
|
||||||
|
Registry string
|
||||||
|
|
||||||
|
// Commands that will be executed before
|
||||||
|
// the base layer of Dockerfile
|
||||||
|
Commands []DockerCommand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadOutOfTreeConf(path string) (c OutOfTree, err error) {
|
||||||
|
buf, err := readFileAll(path)
|
||||||
|
if err == nil {
|
||||||
|
err = toml.Unmarshal(buf, &c)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It's ok if there's no configuration
|
||||||
|
// then we'll just set default values
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
usr, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Kernels == "" {
|
||||||
|
c.Kernels = usr.HomeDir + "/.out-of-tree/kernels.toml"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.UserKernels == "" {
|
||||||
|
c.UserKernels = usr.HomeDir + "/.out-of-tree/kernels.user.toml"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Database == "" {
|
||||||
|
c.Database = usr.HomeDir + "/.out-of-tree/db.sqlite"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Qemu.Timeout == "" {
|
||||||
|
c.Qemu.Timeout = "1m"
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Docker.Timeout == "" {
|
||||||
|
c.Docker.Timeout = "1m"
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
2
debug.go
2
debug.go
@ -161,7 +161,7 @@ func debugHandler(kcfg config.KernelConfig, workPath, kernRegex, gdb string,
|
|||||||
|
|
||||||
q.Debug(gdb)
|
q.Debug(gdb)
|
||||||
coloredGdbAddress := aurora.BgGreen(aurora.Black(gdb))
|
coloredGdbAddress := aurora.BgGreen(aurora.Black(gdb))
|
||||||
fmt.Printf("[*] gdb runned on %s\n", coloredGdbAddress)
|
fmt.Printf("[*] gdb is listening on %s\n", coloredGdbAddress)
|
||||||
|
|
||||||
err = q.Start()
|
err = q.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,7 +54,7 @@ Overview
|
|||||||
|
|
||||||
$ out-of-tree debug --kernel 'Ubuntu:4.15.0-58-generic'
|
$ out-of-tree debug --kernel 'Ubuntu:4.15.0-58-generic'
|
||||||
[*] KASLR SMEP SMAP
|
[*] KASLR SMEP SMAP
|
||||||
[*] gdb runned on tcp::1234
|
[*] gdb is listening on tcp::1234
|
||||||
[*] build result copied to /tmp/exploit
|
[*] build result copied to /tmp/exploit
|
||||||
|
|
||||||
ssh -o StrictHostKeyChecking=no -p 29308 root@127.133.45.236
|
ssh -o StrictHostKeyChecking=no -p 29308 root@127.133.45.236
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
# - KERNEL: kernel headers path
|
# - KERNEL: kernel headers path
|
||||||
# - TARGET: name of exploit binary that MUST be produced by makefile.
|
# - TARGET: name of exploit binary that MUST be produced by makefile.
|
||||||
# - $(TARGET)_test: name of test binary that MUST be produced by makefile
|
# - $(TARGET)_test: name of test binary that MUST be produced by makefile
|
||||||
# and it's will be runned on a LPE stage. TARGET_TEST MUST accept two argument:
|
# and it's will be executed on a LPE stage. TARGET_TEST MUST accept two argument:
|
||||||
# - Path to exploit binary
|
# - Path to exploit binary
|
||||||
# - File that MUST be created with exploit. It uses for test that exploit works
|
# - File that MUST be created with exploit. It uses for test that exploit works
|
||||||
# correctly.
|
# correctly.
|
||||||
|
116
kernel.go
116
kernel.go
@ -19,7 +19,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/naoina/toml"
|
"github.com/naoina/toml"
|
||||||
"github.com/zcalusic/sysinfo"
|
|
||||||
|
|
||||||
"code.dumpstack.io/tools/out-of-tree/config"
|
"code.dumpstack.io/tools/out-of-tree/config"
|
||||||
)
|
)
|
||||||
@ -109,7 +108,9 @@ func vsyscallAvailable() (available bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateBaseDockerImage(sk config.KernelMask) (err error) {
|
func generateBaseDockerImage(registry string, commands []config.DockerCommand,
|
||||||
|
sk config.KernelMask) (err error) {
|
||||||
|
|
||||||
imagePath, err := dockerImagePath(sk)
|
imagePath, err := dockerImagePath(sk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -128,7 +129,12 @@ func generateBaseDockerImage(sk config.KernelMask) (err error) {
|
|||||||
sk.DistroType.String(), sk.DistroRelease)
|
sk.DistroType.String(), sk.DistroRelease)
|
||||||
os.MkdirAll(imagePath, os.ModePerm)
|
os.MkdirAll(imagePath, os.ModePerm)
|
||||||
|
|
||||||
d += fmt.Sprintf("FROM %s:%s\n",
|
d += "FROM "
|
||||||
|
if registry != "" {
|
||||||
|
d += registry + "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
d += fmt.Sprintf("%s:%s\n",
|
||||||
strings.ToLower(sk.DistroType.String()),
|
strings.ToLower(sk.DistroType.String()),
|
||||||
sk.DistroRelease,
|
sk.DistroRelease,
|
||||||
)
|
)
|
||||||
@ -138,16 +144,38 @@ func generateBaseDockerImage(sk config.KernelMask) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, c := range commands {
|
||||||
|
switch c.DistroType {
|
||||||
|
case config.Ubuntu:
|
||||||
|
d += "RUN " + c.Command + "\n"
|
||||||
|
case config.CentOS:
|
||||||
|
d += "RUN " + c.Command + "\n"
|
||||||
|
case config.Debian:
|
||||||
|
d += "RUN " + c.Command + "\n"
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("%s not yet supported",
|
||||||
|
sk.DistroType.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch sk.DistroType {
|
switch sk.DistroType {
|
||||||
case config.Ubuntu:
|
case config.Ubuntu:
|
||||||
d += "ENV DEBIAN_FRONTEND=noninteractive\n"
|
d += "ENV DEBIAN_FRONTEND=noninteractive\n"
|
||||||
|
if sk.DistroRelease >= "16.04" {
|
||||||
|
from := "http://.*ubuntu/"
|
||||||
|
to := "mirror://mirrors.ubuntu.com/mirrors.txt"
|
||||||
|
file := "/etc/apt/sources.list"
|
||||||
|
s := fmt.Sprintf("sed -i 's;%s;%s;' %s", from, to, file)
|
||||||
|
d += "RUN " + s + "\n"
|
||||||
|
}
|
||||||
d += "RUN apt-get update\n"
|
d += "RUN apt-get update\n"
|
||||||
d += "RUN apt-get install -y build-essential libelf-dev\n"
|
d += "RUN apt-get install -y build-essential libelf-dev\n"
|
||||||
d += "RUN apt-get install -y wget git\n"
|
d += "RUN apt-get install -y wget git\n"
|
||||||
if sk.DistroRelease >= "14.04" {
|
if sk.DistroRelease >= "14.04" {
|
||||||
d += "RUN apt-get install -y libseccomp-dev\n"
|
d += "RUN apt-get install -y libseccomp-dev\n"
|
||||||
}
|
}
|
||||||
d += "RUN mkdir /lib/modules\n"
|
d += "RUN mkdir -p /lib/modules\n"
|
||||||
case config.CentOS:
|
case config.CentOS:
|
||||||
if sk.DistroRelease < "7" && !vsyscall {
|
if sk.DistroRelease < "7" && !vsyscall {
|
||||||
log.Println("Old CentOS requires `vsyscall=emulate` " +
|
log.Println("Old CentOS requires `vsyscall=emulate` " +
|
||||||
@ -397,66 +425,6 @@ func listDockerImages() (diis []dockerImageInfo, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
|
|
||||||
si := sysinfo.SysInfo{}
|
|
||||||
si.GetSysInfo()
|
|
||||||
|
|
||||||
distroType, err := config.NewDistroType(si.OS.Vendor)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := exec.Command("ls", "/lib/modules")
|
|
||||||
rawOutput, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
log.Println(string(rawOutput), err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
kernelsBase := "/boot/"
|
|
||||||
files, err := ioutil.ReadDir(kernelsBase)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// only for compatibility, docker is not really used
|
|
||||||
dii := dockerImageInfo{
|
|
||||||
ContainerName: config.KernelMask{
|
|
||||||
DistroType: distroType,
|
|
||||||
DistroRelease: si.OS.Version,
|
|
||||||
}.DockerName(),
|
|
||||||
}
|
|
||||||
|
|
||||||
rootfs, err := genRootfsImage(dii, download)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, k := range strings.Fields(string(rawOutput)) {
|
|
||||||
ki := config.KernelInfo{
|
|
||||||
DistroType: distroType,
|
|
||||||
DistroRelease: si.OS.Version,
|
|
||||||
KernelRelease: k,
|
|
||||||
|
|
||||||
KernelSource: "/lib/modules/" + k + "/build",
|
|
||||||
|
|
||||||
KernelPath: kernelsBase + genKernelPath(files, k),
|
|
||||||
InitrdPath: kernelsBase + genInitrdPath(files, k),
|
|
||||||
RootFS: rootfs,
|
|
||||||
}
|
|
||||||
|
|
||||||
vmlinux := "/usr/lib/debug/boot/vmlinux-" + k
|
|
||||||
log.Println("vmlinux", vmlinux)
|
|
||||||
if exists(vmlinux) {
|
|
||||||
ki.VmlinuxPath = vmlinux
|
|
||||||
}
|
|
||||||
|
|
||||||
kcfg.Kernels = append(kcfg.Kernels, ki)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateKernelsCfg(host, download bool) (err error) {
|
func updateKernelsCfg(host, download bool) (err error) {
|
||||||
newkcfg := config.KernelConfig{}
|
newkcfg := config.KernelConfig{}
|
||||||
|
|
||||||
@ -573,7 +541,10 @@ func shuffle(a []string) []string {
|
|||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKernels(km config.KernelMask, max int64, download bool) (err error) {
|
func generateKernels(km config.KernelMask, registry string,
|
||||||
|
commands []config.DockerCommand, max int64,
|
||||||
|
download bool) (err error) {
|
||||||
|
|
||||||
log.Println("Generating for kernel mask", km)
|
log.Println("Generating for kernel mask", km)
|
||||||
|
|
||||||
_, err = genRootfsImage(dockerImageInfo{ContainerName: km.DockerName()},
|
_, err = genRootfsImage(dockerImageInfo{ContainerName: km.DockerName()},
|
||||||
@ -582,7 +553,7 @@ func generateKernels(km config.KernelMask, max int64, download bool) (err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = generateBaseDockerImage(km)
|
err = generateBaseDockerImage(registry, commands, km)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -632,7 +603,10 @@ func generateKernels(km config.KernelMask, max int64, download bool) (err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func kernelAutogenHandler(workPath string, max int64, host, download bool) (err error) {
|
func kernelAutogenHandler(workPath, registry string,
|
||||||
|
commands []config.DockerCommand,
|
||||||
|
max int64, host, download bool) (err error) {
|
||||||
|
|
||||||
ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
|
ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -644,7 +618,7 @@ func kernelAutogenHandler(workPath string, max int64, host, download bool) (err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = generateKernels(sk, max, download)
|
err = generateKernels(sk, registry, commands, max, download)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -695,7 +669,9 @@ func kernelDockerRegenHandler(host, download bool) (err error) {
|
|||||||
return updateKernelsCfg(host, download)
|
return updateKernelsCfg(host, download)
|
||||||
}
|
}
|
||||||
|
|
||||||
func kernelGenallHandler(distro, version string, host, download bool) (err error) {
|
func kernelGenallHandler(distro, version, registry string,
|
||||||
|
commands []config.DockerCommand, host, download bool) (err error) {
|
||||||
|
|
||||||
distroType, err := config.NewDistroType(distro)
|
distroType, err := config.NewDistroType(distro)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -706,7 +682,7 @@ func kernelGenallHandler(distro, version string, host, download bool) (err error
|
|||||||
DistroRelease: version,
|
DistroRelease: version,
|
||||||
ReleaseMask: ".*",
|
ReleaseMask: ".*",
|
||||||
}
|
}
|
||||||
err = generateKernels(km, kernelsAll, download)
|
err = generateKernels(km, registry, commands, kernelsAll, download)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
77
kernel_linux.go
Normal file
77
kernel_linux.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.dumpstack.io/tools/out-of-tree/config"
|
||||||
|
"github.com/zcalusic/sysinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
|
||||||
|
si := sysinfo.SysInfo{}
|
||||||
|
si.GetSysInfo()
|
||||||
|
|
||||||
|
distroType, err := config.NewDistroType(si.OS.Vendor)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("ls", "/lib/modules")
|
||||||
|
rawOutput, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(string(rawOutput), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kernelsBase := "/boot/"
|
||||||
|
files, err := ioutil.ReadDir(kernelsBase)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// only for compatibility, docker is not really used
|
||||||
|
dii := dockerImageInfo{
|
||||||
|
ContainerName: config.KernelMask{
|
||||||
|
DistroType: distroType,
|
||||||
|
DistroRelease: si.OS.Version,
|
||||||
|
}.DockerName(),
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs, err := genRootfsImage(dii, download)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, k := range strings.Fields(string(rawOutput)) {
|
||||||
|
ki := config.KernelInfo{
|
||||||
|
DistroType: distroType,
|
||||||
|
DistroRelease: si.OS.Version,
|
||||||
|
KernelRelease: k,
|
||||||
|
|
||||||
|
KernelSource: "/lib/modules/" + k + "/build",
|
||||||
|
|
||||||
|
KernelPath: kernelsBase + genKernelPath(files, k),
|
||||||
|
InitrdPath: kernelsBase + genInitrdPath(files, k),
|
||||||
|
RootFS: rootfs,
|
||||||
|
}
|
||||||
|
|
||||||
|
vmlinux := "/usr/lib/debug/boot/vmlinux-" + k
|
||||||
|
log.Println("vmlinux", vmlinux)
|
||||||
|
if exists(vmlinux) {
|
||||||
|
ki.VmlinuxPath = vmlinux
|
||||||
|
}
|
||||||
|
|
||||||
|
kcfg.Kernels = append(kcfg.Kernels, ki)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
18
kernel_macos.go
Normal file
18
kernel_macos.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"code.dumpstack.io/tools/out-of-tree/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
|
||||||
|
err = errors.New("generate host kernels for macOS is not supported")
|
||||||
|
return
|
||||||
|
}
|
55
main.go
55
main.go
@ -84,7 +84,7 @@ func main() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
app.Author("Mikhail Klementev <root@dumpstack.io>")
|
app.Author("Mikhail Klementev <root@dumpstack.io>")
|
||||||
app.Version("1.0.1")
|
app.Version("1.1.2")
|
||||||
|
|
||||||
pathFlag := app.Flag("path", "Path to work directory")
|
pathFlag := app.Flag("path", "Path to work directory")
|
||||||
path := pathFlag.Default(".").ExistingDir()
|
path := pathFlag.Default(".").ExistingDir()
|
||||||
@ -95,25 +95,36 @@ func main() {
|
|||||||
}
|
}
|
||||||
os.MkdirAll(usr.HomeDir+"/.out-of-tree", os.ModePerm)
|
os.MkdirAll(usr.HomeDir+"/.out-of-tree", os.ModePerm)
|
||||||
|
|
||||||
defaultKcfgPath := usr.HomeDir + "/.out-of-tree/kernels.toml"
|
confPath := usr.HomeDir + "/.out-of-tree/out-of-tree.toml"
|
||||||
|
conf, err := config.ReadOutOfTreeConf(confPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
kcfgPathFlag := app.Flag("kernels", "Path to main kernels config")
|
kcfgPathFlag := app.Flag("kernels", "Path to main kernels config")
|
||||||
kcfgPath := kcfgPathFlag.Default(defaultKcfgPath).String()
|
kcfgPath := kcfgPathFlag.Default(conf.Kernels).String()
|
||||||
|
|
||||||
defaultDbPath := usr.HomeDir + "/.out-of-tree/db.sqlite"
|
|
||||||
dbPathFlag := app.Flag("db", "Path to database")
|
dbPathFlag := app.Flag("db", "Path to database")
|
||||||
dbPath := dbPathFlag.Default(defaultDbPath).String()
|
dbPath := dbPathFlag.Default(conf.Database).String()
|
||||||
|
|
||||||
defaultUserKcfgPath := usr.HomeDir + "/.out-of-tree/kernels.user.toml"
|
|
||||||
userKcfgPathFlag := app.Flag("user-kernels", "User kernels config")
|
userKcfgPathFlag := app.Flag("user-kernels", "User kernels config")
|
||||||
userKcfgPathEnv := userKcfgPathFlag.Envar("OUT_OF_TREE_KCFG")
|
userKcfgPathEnv := userKcfgPathFlag.Envar("OUT_OF_TREE_KCFG")
|
||||||
userKcfgPath := userKcfgPathEnv.Default(defaultUserKcfgPath).String()
|
userKcfgPath := userKcfgPathEnv.Default(conf.UserKernels).String()
|
||||||
|
|
||||||
|
timeoutFlag := app.Flag("timeout", "Timeout after tool will not spawn new tests")
|
||||||
|
timeout := timeoutFlag.Duration()
|
||||||
|
|
||||||
qemuTimeoutFlag := app.Flag("qemu-timeout", "Timeout for qemu")
|
qemuTimeoutFlag := app.Flag("qemu-timeout", "Timeout for qemu")
|
||||||
qemuTimeout := qemuTimeoutFlag.Default("1m").Duration()
|
qemuTimeout := qemuTimeoutFlag.Default(conf.Qemu.Timeout).Duration()
|
||||||
|
|
||||||
dockerTimeoutFlag := app.Flag("docker-timeout", "Timeout for docker")
|
dockerTimeoutFlag := app.Flag("docker-timeout", "Timeout for docker")
|
||||||
dockerTimeout := dockerTimeoutFlag.Default("1m").Duration()
|
dockerTimeout := dockerTimeoutFlag.Default(conf.Docker.Timeout).Duration()
|
||||||
|
|
||||||
|
dockerRegistryFlag := app.Flag("docker-registry", "Registry for docker")
|
||||||
|
dockerRegistry := dockerRegistryFlag.Default(conf.Docker.Registry).String()
|
||||||
|
|
||||||
|
thresholdFlag := app.Flag("threshold", "Reliablity threshold for exit code")
|
||||||
|
threshold := thresholdFlag.Default("1.00").Float64()
|
||||||
|
|
||||||
pewCommand := app.Command("pew", "Build, run and test module/exploit")
|
pewCommand := app.Command("pew", "Build, run and test module/exploit")
|
||||||
|
|
||||||
@ -144,6 +155,9 @@ func main() {
|
|||||||
pewTagFlag := pewCommand.Flag("tag", "Log tagging")
|
pewTagFlag := pewCommand.Flag("tag", "Log tagging")
|
||||||
pewTag := pewTagFlag.String()
|
pewTag := pewTagFlag.String()
|
||||||
|
|
||||||
|
pewVerboseFlag := pewCommand.Flag("verbose", "Show more information")
|
||||||
|
pewVerbose := pewVerboseFlag.Bool()
|
||||||
|
|
||||||
kernelCommand := app.Command("kernel", "Manipulate kernels")
|
kernelCommand := app.Command("kernel", "Manipulate kernels")
|
||||||
kernelNoDownload := kernelCommand.Flag("no-download",
|
kernelNoDownload := kernelCommand.Flag("no-download",
|
||||||
"Do not download qemu image while kernel generation").Bool()
|
"Do not download qemu image while kernel generation").Bool()
|
||||||
@ -253,6 +267,10 @@ func main() {
|
|||||||
log.Fatalln("Only one of disable/enable can be used at once")
|
log.Fatalln("Only one of disable/enable can be used at once")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *yekpti && *nokpti {
|
||||||
|
log.Fatalln("Only one of disable/enable can be used at once")
|
||||||
|
}
|
||||||
|
|
||||||
kcfg, err := config.ReadKernelConfig(*kcfgPath)
|
kcfg, err := config.ReadKernelConfig(*kcfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
@ -279,20 +297,28 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
|
stop := time.Time{} // never stop
|
||||||
|
if *timeout != 0 {
|
||||||
|
stop = time.Now().Add(*timeout)
|
||||||
|
}
|
||||||
|
|
||||||
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
switch kingpin.MustParse(app.Parse(os.Args[1:])) {
|
||||||
case pewCommand.FullCommand():
|
case pewCommand.FullCommand():
|
||||||
err = pewHandler(kcfg, *path, *pewKernel, *pewBinary,
|
err = pewHandler(kcfg, *path, *pewKernel, *pewBinary,
|
||||||
*pewTest, *pewGuess, *qemuTimeout, *dockerTimeout,
|
*pewTest, *pewGuess, stop, *qemuTimeout, *dockerTimeout,
|
||||||
*pewMax, *pewRuns, *pewDist, *pewTag, *pewThreads, db)
|
*pewMax, *pewRuns, *pewDist, *pewTag, *pewThreads,
|
||||||
|
db, *pewVerbose)
|
||||||
case kernelListCommand.FullCommand():
|
case kernelListCommand.FullCommand():
|
||||||
err = kernelListHandler(kcfg)
|
err = kernelListHandler(kcfg)
|
||||||
case kernelAutogenCommand.FullCommand():
|
case kernelAutogenCommand.FullCommand():
|
||||||
err = kernelAutogenHandler(*path, *kernelAutogenMax,
|
err = kernelAutogenHandler(*path, *dockerRegistry,
|
||||||
|
conf.Docker.Commands, *kernelAutogenMax,
|
||||||
*kernelUseHost, !*kernelNoDownload)
|
*kernelUseHost, !*kernelNoDownload)
|
||||||
case kernelDockerRegenCommand.FullCommand():
|
case kernelDockerRegenCommand.FullCommand():
|
||||||
err = kernelDockerRegenHandler(*kernelUseHost, !*kernelNoDownload)
|
err = kernelDockerRegenHandler(*kernelUseHost, !*kernelNoDownload)
|
||||||
case kernelGenallCommand.FullCommand():
|
case kernelGenallCommand.FullCommand():
|
||||||
err = kernelGenallHandler(*distro, *version,
|
err = kernelGenallHandler(*distro, *version,
|
||||||
|
*dockerRegistry, conf.Docker.Commands,
|
||||||
*kernelUseHost, !*kernelNoDownload)
|
*kernelUseHost, !*kernelNoDownload)
|
||||||
case genModuleCommand.FullCommand():
|
case genModuleCommand.FullCommand():
|
||||||
err = genConfig(config.KernelModule)
|
err = genConfig(config.KernelModule)
|
||||||
@ -316,7 +342,8 @@ func main() {
|
|||||||
case logMarkdownCommand.FullCommand():
|
case logMarkdownCommand.FullCommand():
|
||||||
err = logMarkdownHandler(db, *path, *logMarkdownTag)
|
err = logMarkdownHandler(db, *path, *logMarkdownTag)
|
||||||
case packCommand.FullCommand():
|
case packCommand.FullCommand():
|
||||||
err = packHandler(db, *path, kcfg, *packAutogen,
|
err = packHandler(db, *path, *dockerRegistry, stop,
|
||||||
|
conf.Docker.Commands, kcfg, *packAutogen,
|
||||||
!*packNoDownload, *packExploitRuns, *packKernelRuns)
|
!*packNoDownload, *packExploitRuns, *packKernelRuns)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +351,7 @@ func main() {
|
|||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if somethingFailed {
|
if successRate(state) < *threshold {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
pack.go
11
pack.go
@ -15,7 +15,8 @@ import (
|
|||||||
"code.dumpstack.io/tools/out-of-tree/config"
|
"code.dumpstack.io/tools/out-of-tree/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func packHandler(db *sql.DB, path string, kcfg config.KernelConfig,
|
func packHandler(db *sql.DB, path, registry string, stop time.Time,
|
||||||
|
commands []config.DockerCommand, kcfg config.KernelConfig,
|
||||||
autogen, download bool, exploitRuns, kernelRuns int64) (err error) {
|
autogen, download bool, exploitRuns, kernelRuns int64) (err error) {
|
||||||
|
|
||||||
dockerTimeout := time.Minute
|
dockerTimeout := time.Minute
|
||||||
@ -39,7 +40,8 @@ func packHandler(db *sql.DB, path string, kcfg config.KernelConfig,
|
|||||||
|
|
||||||
if autogen {
|
if autogen {
|
||||||
var perRegex int64 = 1
|
var perRegex int64 = 1
|
||||||
err = kernelAutogenHandler(workPath, perRegex, false, download)
|
err = kernelAutogenHandler(workPath, registry,
|
||||||
|
commands, perRegex, false, download)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -48,8 +50,9 @@ func packHandler(db *sql.DB, path string, kcfg config.KernelConfig,
|
|||||||
log.Println(f.Name())
|
log.Println(f.Name())
|
||||||
|
|
||||||
pewHandler(kcfg, workPath, "", "", "", false,
|
pewHandler(kcfg, workPath, "", "", "", false,
|
||||||
dockerTimeout, qemuTimeout,
|
stop, dockerTimeout, qemuTimeout,
|
||||||
kernelRuns, exploitRuns, pathDevNull, tag, threads, db)
|
kernelRuns, exploitRuns, pathDevNull,
|
||||||
|
tag, threads, db, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
50
pew.go
50
pew.go
@ -26,7 +26,17 @@ import (
|
|||||||
"code.dumpstack.io/tools/out-of-tree/qemu"
|
"code.dumpstack.io/tools/out-of-tree/qemu"
|
||||||
)
|
)
|
||||||
|
|
||||||
var somethingFailed = false
|
type runstate struct {
|
||||||
|
Overall, Success float64
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
state runstate
|
||||||
|
)
|
||||||
|
|
||||||
|
func successRate(state runstate) float64 {
|
||||||
|
return state.Success / state.Overall
|
||||||
|
}
|
||||||
|
|
||||||
const pathDevNull = "/dev/null"
|
const pathDevNull = "/dev/null"
|
||||||
|
|
||||||
@ -134,11 +144,12 @@ func testKernelExploit(q *qemu.System, ka config.Artifact,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genOkFail(name string, ok bool) (aurv aurora.Value) {
|
func genOkFail(name string, ok bool) (aurv aurora.Value) {
|
||||||
|
state.Overall += 1
|
||||||
if ok {
|
if ok {
|
||||||
|
state.Success += 1
|
||||||
s := " " + name + " SUCCESS "
|
s := " " + name + " SUCCESS "
|
||||||
aurv = aurora.BgGreen(aurora.Black(s))
|
aurv = aurora.BgGreen(aurora.Black(s))
|
||||||
} else {
|
} else {
|
||||||
somethingFailed = true
|
|
||||||
s := " " + name + " FAILURE "
|
s := " " + name + " FAILURE "
|
||||||
aurv = aurora.BgRed(aurora.Gray(aurora.Bold(s)))
|
aurv = aurora.BgRed(aurora.Gray(aurora.Bold(s)))
|
||||||
}
|
}
|
||||||
@ -292,7 +303,7 @@ func copyTest(q *qemu.System, testPath string, ka config.Artifact) (
|
|||||||
func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
|
func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
|
||||||
ki config.KernelInfo, binaryPath, testPath string,
|
ki config.KernelInfo, binaryPath, testPath string,
|
||||||
qemuTimeout, dockerTimeout time.Duration, dist, tag string,
|
qemuTimeout, dockerTimeout time.Duration, dist, tag string,
|
||||||
db *sql.DB) {
|
db *sql.DB, verbose bool) {
|
||||||
|
|
||||||
defer swg.Done()
|
defer swg.Done()
|
||||||
|
|
||||||
@ -317,6 +328,7 @@ func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
|
|||||||
q.SetKASLR(!ka.Mitigations.DisableKaslr)
|
q.SetKASLR(!ka.Mitigations.DisableKaslr)
|
||||||
q.SetSMEP(!ka.Mitigations.DisableSmep)
|
q.SetSMEP(!ka.Mitigations.DisableSmep)
|
||||||
q.SetSMAP(!ka.Mitigations.DisableSmap)
|
q.SetSMAP(!ka.Mitigations.DisableSmap)
|
||||||
|
q.SetKPTI(!ka.Mitigations.DisableKpti)
|
||||||
|
|
||||||
err = q.Start()
|
err = q.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -325,6 +337,18 @@ func whatever(swg *sizedwaitgroup.SizedWaitGroup, ka config.Artifact,
|
|||||||
}
|
}
|
||||||
defer q.Stop()
|
defer q.Stop()
|
||||||
|
|
||||||
|
if verbose {
|
||||||
|
go func() {
|
||||||
|
for !q.Died {
|
||||||
|
time.Sleep(time.Minute)
|
||||||
|
log.Println(ka.Name, ki.DistroType,
|
||||||
|
ki.DistroRelease, ki.KernelRelease,
|
||||||
|
"still alive")
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
usr, err := user.Current()
|
usr, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@ -380,9 +404,10 @@ func shuffleKernels(a []config.KernelInfo) []config.KernelInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func performCI(ka config.Artifact, kcfg config.KernelConfig, binaryPath,
|
func performCI(ka config.Artifact, kcfg config.KernelConfig, binaryPath,
|
||||||
testPath string, qemuTimeout, dockerTimeout time.Duration,
|
testPath string, stop time.Time,
|
||||||
|
qemuTimeout, dockerTimeout time.Duration,
|
||||||
max, runs int64, dist, tag string, threads int,
|
max, runs int64, dist, tag string, threads int,
|
||||||
db *sql.DB) (err error) {
|
db *sql.DB, verbose bool) (err error) {
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
@ -402,10 +427,13 @@ func performCI(ka config.Artifact, kcfg config.KernelConfig, binaryPath,
|
|||||||
found = true
|
found = true
|
||||||
max--
|
max--
|
||||||
for i := int64(0); i < runs; i++ {
|
for i := int64(0); i < runs; i++ {
|
||||||
|
if !stop.IsZero() && time.Now().After(stop) {
|
||||||
|
break
|
||||||
|
}
|
||||||
swg.Add()
|
swg.Add()
|
||||||
go whatever(&swg, ka, kernel, binaryPath,
|
go whatever(&swg, ka, kernel, binaryPath,
|
||||||
testPath, qemuTimeout, dockerTimeout,
|
testPath, qemuTimeout, dockerTimeout,
|
||||||
dist, tag, db)
|
dist, tag, db, verbose)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,11 +485,12 @@ func genAllKernels() (sk []config.KernelMask, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Now too many parameters, move all of them to some structure
|
||||||
func pewHandler(kcfg config.KernelConfig,
|
func pewHandler(kcfg config.KernelConfig,
|
||||||
workPath, ovrrdKrnl, binary, test string, guess bool,
|
workPath, ovrrdKrnl, binary, test string, guess bool,
|
||||||
qemuTimeout, dockerTimeout time.Duration,
|
stop time.Time, qemuTimeout, dockerTimeout time.Duration,
|
||||||
max, runs int64, dist, tag string, threads int,
|
max, runs int64, dist, tag string, threads int,
|
||||||
db *sql.DB) (err error) {
|
db *sql.DB, verbose bool) (err error) {
|
||||||
|
|
||||||
ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
|
ka, err := config.ReadArtifactConfig(workPath + "/.out-of-tree.toml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -489,8 +518,9 @@ func pewHandler(kcfg config.KernelConfig,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = performCI(ka, kcfg, binary, test, qemuTimeout, dockerTimeout,
|
err = performCI(ka, kcfg, binary, test,
|
||||||
max, runs, dist, tag, threads, db)
|
stop, qemuTimeout, dockerTimeout,
|
||||||
|
max, runs, dist, tag, threads, db, verbose)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ type Kernel struct {
|
|||||||
InitrdPath string
|
InitrdPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
// System describe qemu parameters and runned process
|
// System describe qemu parameters and executed process
|
||||||
type System struct {
|
type System struct {
|
||||||
arch arch
|
arch arch
|
||||||
kernel Kernel
|
kernel Kernel
|
||||||
@ -86,7 +86,7 @@ type System struct {
|
|||||||
Died bool
|
Died bool
|
||||||
sshAddrPort string
|
sshAddrPort string
|
||||||
|
|
||||||
// accessible while qemu is runned
|
// accessible while qemu is running
|
||||||
cmd *exec.Cmd
|
cmd *exec.Cmd
|
||||||
pipe struct {
|
pipe struct {
|
||||||
stdin io.WriteCloser
|
stdin io.WriteCloser
|
||||||
|
@ -47,7 +47,7 @@ ENV IMAGE=/shared/out_of_tree_centos_7.img
|
|||||||
|
|
||||||
RUN mkdir $IMAGEDIR
|
RUN mkdir $IMAGEDIR
|
||||||
|
|
||||||
# Must be runned with --privileged because of /dev/loop
|
# Must be executed with --privileged because of /dev/loop
|
||||||
CMD qemu-img create $IMAGE 2G && \
|
CMD qemu-img create $IMAGE 2G && \
|
||||||
mkfs.ext4 -F $IMAGE && \
|
mkfs.ext4 -F $IMAGE && \
|
||||||
mount -o loop $IMAGE $IMAGEDIR && \
|
mount -o loop $IMAGE $IMAGEDIR && \
|
||||||
|
35
tools/qemu-debian-img/14.04/Dockerfile
Normal file
35
tools/qemu-debian-img/14.04/Dockerfile
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# 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
|
17
tools/qemu-debian-img/14.04/setup.sh
Executable file
17
tools/qemu-debian-img/14.04/setup.sh
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
#!/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
|
@ -25,8 +25,9 @@ ENV RELEASE=bionic
|
|||||||
|
|
||||||
RUN mkdir $IMAGEDIR
|
RUN mkdir $IMAGEDIR
|
||||||
|
|
||||||
# Must be runned with --privileged because of /dev/loop
|
# Must be executed with --privileged because of /dev/loop
|
||||||
CMD debootstrap --include=openssh-server $RELEASE $TMPDIR $REPOSITORY && \
|
CMD debootstrap --include=openssh-server,policykit-1 \
|
||||||
|
$RELEASE $TMPDIR $REPOSITORY && \
|
||||||
/shared/setup.sh $TMPDIR && \
|
/shared/setup.sh $TMPDIR && \
|
||||||
qemu-img create $IMAGE 2G && \
|
qemu-img create $IMAGE 2G && \
|
||||||
mkfs.ext4 -F $IMAGE && \
|
mkfs.ext4 -F $IMAGE && \
|
||||||
|
Reference in New Issue
Block a user