1
0

Remove bootstrap, download images on-demand

This commit is contained in:
dump_stack() 2019-08-20 09:09:38 +00:00
parent 86ad71f230
commit 1ffd68601c
Signed by: dump_stack
GPG Key ID: BE44DA8C062D87DC
10 changed files with 160 additions and 130 deletions

View File

@ -53,6 +53,9 @@
- Added ability to change amount of memory/CPUs and set qemu timeout - Added ability to change amount of memory/CPUs and set qemu timeout
in artifact definition (`.out-of-tree.toml`). in artifact definition (`.out-of-tree.toml`).
- Now images downloading while `kernel autogen`, bootstrap is not
required anymore.
### Changed ### Changed
- Now if there's no base image found — out-of-tree will try to use - Now if there's no base image found — out-of-tree will try to use
@ -79,6 +82,9 @@
- *Kernel factory* is removed completely in favor of incremental - *Kernel factory* is removed completely in favor of incremental
Dockerfiles. Dockerfiles.
- `bootstrap` is not doing anything anymore. It'll be removed in next
release.
### Fixed ### Fixed
- Command `timeout` is not required anymore. - Command `timeout` is not required anymore.

View File

@ -39,7 +39,6 @@ Also check out [docker post-installation steps](https://docs.docker.com/install/
## Build from source ## Build from source
$ go get -u code.dumpstack.io/tools/out-of-tree $ go get -u code.dumpstack.io/tools/out-of-tree
$ out-of-tree bootstrap
Then you can check it on kernel module example: Then you can check it on kernel module example:

View File

@ -1,7 +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.
package main
const imagesURL = "https://github.com/jollheef/out-of-tree/releases/download/v0.2/images.tar.gz"

View File

@ -1,83 +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.
package main
import (
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"os/user"
)
// inspired by Edd Turtle code
func downloadFile(filepath string, url string) (err error) {
out, err := os.Create(filepath)
if err != nil {
return
}
defer out.Close()
resp, err := http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
_, err = io.Copy(out, resp.Body)
return
}
func unpackTar(archive, destination string) (err error) {
cmd := exec.Command("tar", "xf", archive)
cmd.Dir = destination + "/"
rawOutput, err := cmd.CombinedOutput()
if err != nil {
// I don't like when some errors printed inside
// So if you know way to do it better - FIXME please
log.Println("Unpack images error:", string(rawOutput), err)
return
}
return
}
func bootstrapHandler() (err error) {
log.Println("Download images...")
usr, err := user.Current()
if err != nil {
return
}
imagesPath := usr.HomeDir + "/.out-of-tree/images/"
os.MkdirAll(imagesPath, os.ModePerm)
tmp, err := ioutil.TempDir("/tmp/", "out-of-tree_")
if err != nil {
log.Println("Temporary directory creation error:", err)
return
}
defer os.RemoveAll(tmp)
imagesArchive := tmp + "/images.tar.gz"
err = downloadFile(imagesArchive, imagesURL)
if err != nil {
log.Println("Download file error:", err)
return
}
err = unpackTar(imagesArchive, imagesPath)
if err != nil {
log.Println("Unpack images error:", err)
}
log.Println("Success!")
return
}

View File

@ -55,10 +55,6 @@ Build *out-of-tree*::
security implications. Check *Docker* documentation for more security implications. Check *Docker* documentation for more
information. information.
Bootstrap::
$ out-of-tree bootstrap
Test that everything works:: Test that everything works::
$ cd $GOPATH/src/code.dumpstack.io/tools/out-of-tree/examples/kernel-exploit $ cd $GOPATH/src/code.dumpstack.io/tools/out-of-tree/examples/kernel-exploit

7
images.config.go Normal file
View File

@ -0,0 +1,7 @@
// 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 main
const imagesBaseURL = "https://out-of-tree.fra1.digitaloceanspaces.com/1.0.0/"

82
images.go Normal file
View File

@ -0,0 +1,82 @@
// 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 main
import (
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
)
// inspired by Edd Turtle code
func downloadFile(filepath string, url string) (err error) {
out, err := os.Create(filepath)
if err != nil {
return
}
defer out.Close()
resp, err := http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
break
case http.StatusForbidden, http.StatusNotFound:
err = fmt.Errorf("Cannot download %s. It looks like you need "+
"to generate it manually and place it "+
"to ~/.out-of-tree/images/. "+
"Check documentation for additional information.", url)
return
default:
err = fmt.Errorf("Something weird happens while "+
"download file: %d", resp.StatusCode)
return
}
_, err = io.Copy(out, resp.Body)
return
}
func unpackTar(archive, destination string) (err error) {
// NOTE: If you're change anything in tar command please check also
// BSD tar (or if you're using macOS, do not forget to check GNU Tar)
// Also make sure that sparse files are extracting correctly
cmd := exec.Command("tar", "-Sxf", archive)
cmd.Dir = destination + "/"
rawOutput, err := cmd.CombinedOutput()
if err != nil {
err = fmt.Errorf("%v: %s", err, rawOutput)
return
}
return
}
func downloadImage(path, file string) (err error) {
tmp, err := ioutil.TempDir("/tmp/", "out-of-tree_")
if err != nil {
return
}
defer os.RemoveAll(tmp)
archive := tmp + "/" + file + ".tar.gz"
url := imagesBaseURL + file + ".tar.gz"
err = downloadFile(archive, url)
if err != nil {
return
}
err = unpackTar(archive, path)
return
}

View File

@ -259,13 +259,24 @@ func genInitrdPath(files []os.FileInfo, kname string) string {
return "unknown" return "unknown"
} }
func genRootfsImage(d dockerImageInfo) string { func genRootfsImage(d dockerImageInfo, download bool) (rootfs string, err error) {
usr, err := user.Current() usr, err := user.Current()
if err != nil { if err != nil {
return fmt.Sprintln(err) return
} }
imageFile := d.ContainerName + ".img" imageFile := d.ContainerName + ".img"
return usr.HomeDir + "/.out-of-tree/images/" + imageFile
imagesPath := usr.HomeDir + "/.out-of-tree/images/"
os.MkdirAll(imagesPath, os.ModePerm)
rootfs = imagesPath + imageFile
if !exists(rootfs) {
if download {
log.Println(imageFile, "not exists, start downloading...")
err = downloadImage(imagesPath, imageFile)
}
}
return
} }
type dockerImageInfo struct { type dockerImageInfo struct {
@ -309,7 +320,7 @@ func listDockerImages() (diis []dockerImageInfo, err error) {
return return
} }
func genHostKernels() (kcfg config.KernelConfig, err error) { func genHostKernels(download bool) (kcfg config.KernelConfig, err error) {
si := sysinfo.SysInfo{} si := sysinfo.SysInfo{}
si.GetSysInfo() si.GetSysInfo()
@ -339,6 +350,11 @@ func genHostKernels() (kcfg config.KernelConfig, err error) {
}.DockerName(), }.DockerName(),
} }
rootfs, err := genRootfsImage(dii, download)
if err != nil {
return
}
for _, k := range strings.Fields(string(rawOutput)) { for _, k := range strings.Fields(string(rawOutput)) {
ki := config.KernelInfo{ ki := config.KernelInfo{
DistroType: distroType, DistroType: distroType,
@ -349,7 +365,7 @@ func genHostKernels() (kcfg config.KernelConfig, err error) {
KernelPath: kernelsBase + genKernelPath(files, k), KernelPath: kernelsBase + genKernelPath(files, k),
InitrdPath: kernelsBase + genInitrdPath(files, k), InitrdPath: kernelsBase + genInitrdPath(files, k),
RootFS: genRootfsImage(dii), RootFS: rootfs,
} }
vmlinux := "/usr/lib/debug/boot/vmlinux-" + k vmlinux := "/usr/lib/debug/boot/vmlinux-" + k
@ -364,12 +380,12 @@ func genHostKernels() (kcfg config.KernelConfig, err error) {
return return
} }
func updateKernelsCfg(host bool) (err error) { func updateKernelsCfg(host, download bool) (err error) {
newkcfg := config.KernelConfig{} newkcfg := config.KernelConfig{}
if host { if host {
// Get host kernels // Get host kernels
newkcfg, err = genHostKernels() newkcfg, err = genHostKernels(download)
if err != nil { if err != nil {
return return
} }
@ -382,7 +398,7 @@ func updateKernelsCfg(host bool) (err error) {
} }
for _, d := range dockerImages { for _, d := range dockerImages {
err = genDockerKernels(d, &newkcfg) err = genDockerKernels(d, &newkcfg, download)
if err != nil { if err != nil {
log.Println("gen kernels", d.ContainerName, ":", err) log.Println("gen kernels", d.ContainerName, ":", err)
continue continue
@ -419,8 +435,8 @@ func updateKernelsCfg(host bool) (err error) {
return return
} }
func genDockerKernels(dii dockerImageInfo, newkcfg *config.KernelConfig) ( func genDockerKernels(dii dockerImageInfo, newkcfg *config.KernelConfig,
err error) { download bool) (err error) {
name := dii.ContainerName name := dii.ContainerName
cmd := exec.Command("docker", "run", name, "ls", "/lib/modules") cmd := exec.Command("docker", "run", name, "ls", "/lib/modules")
@ -440,6 +456,11 @@ func genDockerKernels(dii dockerImageInfo, newkcfg *config.KernelConfig) (
return return
} }
rootfs, err := genRootfsImage(dii, download)
if err != nil {
return
}
for _, k := range strings.Fields(string(rawOutput)) { for _, k := range strings.Fields(string(rawOutput)) {
ki := config.KernelInfo{ ki := config.KernelInfo{
DistroType: dii.DistroType, DistroType: dii.DistroType,
@ -449,7 +470,7 @@ func genDockerKernels(dii dockerImageInfo, newkcfg *config.KernelConfig) (
KernelPath: kernelsBase + genKernelPath(files, k), KernelPath: kernelsBase + genKernelPath(files, k),
InitrdPath: kernelsBase + genInitrdPath(files, k), InitrdPath: kernelsBase + genInitrdPath(files, k),
RootFS: genRootfsImage(dii), RootFS: rootfs,
} }
newkcfg.Kernels = append(newkcfg.Kernels, ki) newkcfg.Kernels = append(newkcfg.Kernels, ki)
} }
@ -475,8 +496,15 @@ func shuffle(a []string) []string {
return a return a
} }
func generateKernels(km config.KernelMask, max int64) (err error) { func generateKernels(km config.KernelMask, 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()},
download)
if err != nil {
return
}
err = generateBaseDockerImage(km) err = generateBaseDockerImage(km)
if err != nil { if err != nil {
return return
@ -519,7 +547,7 @@ func generateKernels(km config.KernelMask, max int64) (err error) {
return return
} }
func kernelAutogenHandler(workPath string, max int64, host bool) (err error) { func kernelAutogenHandler(workPath string, 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
@ -531,17 +559,17 @@ func kernelAutogenHandler(workPath string, max int64, host bool) (err error) {
return return
} }
err = generateKernels(sk, max) err = generateKernels(sk, max, download)
if err != nil { if err != nil {
return return
} }
} }
err = updateKernelsCfg(host) err = updateKernelsCfg(host, download)
return return
} }
func kernelDockerRegenHandler(host bool) (err error) { func kernelDockerRegenHandler(host, download bool) (err error) {
dockerImages, err := listDockerImages() dockerImages, err := listDockerImages()
if err != nil { if err != nil {
return return
@ -579,10 +607,10 @@ func kernelDockerRegenHandler(host bool) (err error) {
} }
} }
return updateKernelsCfg(host) return updateKernelsCfg(host, download)
} }
func kernelGenallHandler(distro, version string, host bool) (err error) { func kernelGenallHandler(distro, version string, host, download bool) (err error) {
distroType, err := config.NewDistroType(distro) distroType, err := config.NewDistroType(distro)
if err != nil { if err != nil {
return return
@ -593,10 +621,10 @@ func kernelGenallHandler(distro, version string, host bool) (err error) {
DistroRelease: version, DistroRelease: version,
ReleaseMask: ".*", ReleaseMask: ".*",
} }
err = generateKernels(km, kernelsAll) err = generateKernels(km, kernelsAll, download)
if err != nil { if err != nil {
return return
} }
return updateKernelsCfg(host) return updateKernelsCfg(host, download)
} }

28
main.go
View File

@ -145,6 +145,8 @@ func main() {
pewTag := pewTagFlag.String() pewTag := pewTagFlag.String()
kernelCommand := app.Command("kernel", "Manipulate kernels") kernelCommand := app.Command("kernel", "Manipulate kernels")
kernelNoDownload := kernelCommand.Flag("no-download",
"Do not download qemu image while kernel generation").Bool()
kernelUseHost := kernelCommand.Flag("host", "Use also host kernels").Bool() kernelUseHost := kernelCommand.Flag("host", "Use also host kernels").Bool()
kernelListCommand := kernelCommand.Command("list", "List kernels") kernelListCommand := kernelCommand.Command("list", "List kernels")
kernelAutogenCommand := kernelCommand.Command("autogen", kernelAutogenCommand := kernelCommand.Command("autogen",
@ -187,8 +189,7 @@ func main() {
nosmap := debugCommand.Flag("disable-smap", "Disable SMAP").Bool() nosmap := debugCommand.Flag("disable-smap", "Disable SMAP").Bool()
nokpti := debugCommand.Flag("disable-kpti", "Disable KPTI").Bool() nokpti := debugCommand.Flag("disable-kpti", "Disable KPTI").Bool()
bootstrapCommand := app.Command("bootstrap", bootstrapCommand := app.Command("bootstrap", "Apparently nothing")
"Create directories && download images")
logCommand := app.Command("log", "Logs") logCommand := app.Command("log", "Logs")
@ -209,6 +210,8 @@ func main() {
packCommand := app.Command("pack", "Exploit pack test") packCommand := app.Command("pack", "Exploit pack test")
packAutogen := packCommand.Flag("autogen", "Kernel autogeneration").Bool() packAutogen := packCommand.Flag("autogen", "Kernel autogeneration").Bool()
packNoDownload := packCommand.Flag("no-download",
"Do not download qemu image while kernel generation").Bool()
packExploitRuns := packCommand.Flag("exploit-runs", packExploitRuns := packCommand.Flag("exploit-runs",
"Amount of runs of each exploit").Default("4").Int64() "Amount of runs of each exploit").Default("4").Int64()
packKernelRuns := packCommand.Flag("kernel-runs", packKernelRuns := packCommand.Flag("kernel-runs",
@ -228,12 +231,6 @@ func main() {
os.Exit(1) os.Exit(1)
} }
if !exists(usr.HomeDir + "/.out-of-tree/images") {
log.Println("No ~/.out-of-tree/images: " +
"Probably you need to run `out-of-tree bootstrap`" +
" for downloading basic images")
}
if !exists(usr.HomeDir + "/.out-of-tree/kernels.toml") { if !exists(usr.HomeDir + "/.out-of-tree/kernels.toml") {
log.Println("No ~/.out-of-tree/kernels.toml: Probably you " + log.Println("No ~/.out-of-tree/kernels.toml: Probably you " +
"need to run `out-of-tree kernel autogen` in " + "need to run `out-of-tree kernel autogen` in " +
@ -290,11 +287,13 @@ func main() {
case kernelListCommand.FullCommand(): case kernelListCommand.FullCommand():
err = kernelListHandler(kcfg) err = kernelListHandler(kcfg)
case kernelAutogenCommand.FullCommand(): case kernelAutogenCommand.FullCommand():
err = kernelAutogenHandler(*path, *kernelAutogenMax, *kernelUseHost) err = kernelAutogenHandler(*path, *kernelAutogenMax,
*kernelUseHost, !*kernelNoDownload)
case kernelDockerRegenCommand.FullCommand(): case kernelDockerRegenCommand.FullCommand():
err = kernelDockerRegenHandler(*kernelUseHost) err = kernelDockerRegenHandler(*kernelUseHost, !*kernelNoDownload)
case kernelGenallCommand.FullCommand(): case kernelGenallCommand.FullCommand():
err = kernelGenallHandler(*distro, *version, *kernelUseHost) err = kernelGenallHandler(*distro, *version,
*kernelUseHost, !*kernelNoDownload)
case genModuleCommand.FullCommand(): case genModuleCommand.FullCommand():
err = genConfig(config.KernelModule) err = genConfig(config.KernelModule)
case genExploitCommand.FullCommand(): case genExploitCommand.FullCommand():
@ -304,7 +303,10 @@ func main() {
*dockerTimeout, *yekaslr, *yesmep, *yesmap, *yekpti, *dockerTimeout, *yekaslr, *yesmep, *yesmap, *yekpti,
*nokaslr, *nosmep, *nosmap, *nokpti) *nokaslr, *nosmep, *nosmap, *nokpti)
case bootstrapCommand.FullCommand(): case bootstrapCommand.FullCommand():
err = bootstrapHandler() fmt.Println("bootstrap is no more required, " +
"now images downloading on-demand")
fmt.Println("please, remove it from any automation scripts, " +
"because it'll be removed in the next release")
case logQueryCommand.FullCommand(): case logQueryCommand.FullCommand():
err = logHandler(db, *path, *logTag, *logNum, *logRate) err = logHandler(db, *path, *logTag, *logNum, *logRate)
case logDumpCommand.FullCommand(): case logDumpCommand.FullCommand():
@ -315,7 +317,7 @@ func main() {
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, kcfg, *packAutogen,
*packExploitRuns, *packKernelRuns) !*packNoDownload, *packExploitRuns, *packKernelRuns)
} }
if err != nil { if err != nil {

View File

@ -16,7 +16,7 @@ import (
) )
func packHandler(db *sql.DB, path string, kcfg config.KernelConfig, func packHandler(db *sql.DB, path string, kcfg config.KernelConfig,
autogen bool, exploitRuns, kernelRuns int64) (err error) { autogen, download bool, exploitRuns, kernelRuns int64) (err error) {
dockerTimeout := time.Minute dockerTimeout := time.Minute
qemuTimeout := time.Minute qemuTimeout := time.Minute
@ -39,7 +39,7 @@ 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) err = kernelAutogenHandler(workPath, perRegex, false, download)
if err != nil { if err != nil {
return return
} }