-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from kubefirst/cexec2
cexec2 removing scratch
- Loading branch information
Showing
3 changed files
with
212 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# syntax=docker/dockerfile:1 | ||
|
||
FROM golang:1.21-alpine as cexec | ||
RUN apk add --no-cache git ca-certificates gcc linux-headers musl-dev | ||
COPY . /src | ||
WORKDIR /src/cexec | ||
RUN --mount=type=cache,sharing=locked,id=gomod,target=/go/pkg/mod/cache \ | ||
--mount=type=cache,sharing=locked,id=goroot,target=/root/.cache/go-build \ | ||
CGO_ENABLED=1 GOOS=linux go build -a -ldflags "-linkmode external -extldflags '-static' -s -w" -o cexec | ||
|
||
FROM golang:1.21-alpine | ||
COPY --from=cexec /src/cexec/cexec /usr/bin/cexec | ||
ENTRYPOINT ["/usr/bin/cexec"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
``` | ||
cexec (update docs) | ||
``` | ||
|
||
The `cexec` action performs *execution* either within a [chroot](https://en.wikipedia.org/wiki/Chroot) environment | ||
or within the base filesystem. The primary use-case is being able to provision | ||
files/an Operating System to disk and then being able to execute something that | ||
perhaps resides within that filesystem. | ||
|
||
```yaml | ||
actions: | ||
- name: "Install Grub" | ||
image: quay.io/tinkerbell/actions/cexec:latest | ||
timeout: 90 | ||
environment: | ||
BLOCK_DEVICE: /dev/sda3 | ||
FS_TYPE: ext4 | ||
CHROOT: y | ||
CMD_LINE: "grub-install --root-directory=/boot /dev/sda" | ||
``` | ||
In order to execute multiple commands (seperated by a semi-colon) we will | ||
need to leverage a shell. We do this by passing `sh -c` as a `DEFAULT_INTERPRETER`. | ||
This interpreter will then parse your commands. | ||
|
||
```yaml | ||
actions: | ||
- name: "Update packages" | ||
image: quay.io/tinkerbell/actions/cexec:latest | ||
timeout: 90 | ||
environment: | ||
BLOCK_DEVICE: /dev/sda3 | ||
FS_TYPE: ext4 | ||
CHROOT: y | ||
DEFAULT_INTERPRETER: "/bin/sh -c" | ||
CMD_LINE: "apt-get -y update; apt-get -y upgrade" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
"path/filepath" | ||
"strings" | ||
"syscall" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
const mountAction = "/mountAction" | ||
|
||
func main() { | ||
fmt.Printf("CEXEC - Chroot Exec\n------------------------\n") | ||
|
||
// Parse the environment variables that are passed into the action | ||
blockDevice := os.Getenv("BLOCK_DEVICE") | ||
filesystemType := os.Getenv("FS_TYPE") | ||
chroot := os.Getenv("CHROOT") | ||
defaultInterpreter := os.Getenv("DEFAULT_INTERPRETER") | ||
cmdLine := os.Getenv("CMD_LINE") | ||
|
||
var exitChroot func() error | ||
|
||
if blockDevice == "" { | ||
log.Fatalf("No Block Device speified with Environment Variable [BLOCK_DEVICE]") | ||
} | ||
|
||
// Create the /mountAction mountpoint (no folders exist previously in scratch container) | ||
err := os.Mkdir(mountAction, os.ModeDir) | ||
if err != nil { | ||
log.Fatalf("Error creating the action Mountpoint [%s]", mountAction) | ||
} | ||
|
||
// Mount the block device to the /mountAction point | ||
err = syscall.Mount(blockDevice, mountAction, filesystemType, 0, "") | ||
if err != nil { | ||
log.Fatalf("Mounting [%s] -> [%s] error [%v]", blockDevice, mountAction, err) | ||
} | ||
log.Infof("Mounted [%s] -> [%s]", blockDevice, mountAction) | ||
|
||
if chroot != "" { | ||
err = MountSpecialDirs() | ||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
log.Infoln("Changing root before executing command") | ||
exitChroot, err = Chroot(mountAction) | ||
if err != nil { | ||
log.Fatalf("Error changing root to [%s]", mountAction) | ||
} | ||
} | ||
|
||
if defaultInterpreter != "" { | ||
// Split the interpreter by space, in the event that the default intprepretter has flags. | ||
di := strings.Split(defaultInterpreter, " ") | ||
if len(di) == 0 { | ||
log.Fatalf("Error parsing [\"DEFAULT_INTERPETER\"] [%s]\n", defaultInterpreter) | ||
} | ||
// Look for default shell intepreter | ||
_, err = os.Stat(di[0]) | ||
if os.IsNotExist(err) { | ||
log.Fatalf("Unable to find the [\"DEFAULT_INTERPETER\"] [%s], check chroot and interpreter path", defaultInterpreter) | ||
} | ||
di = append(di, cmdLine) | ||
cmd := exec.Command(di[0], di[1:]...) | ||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr | ||
debugCMD := fmt.Sprintf("%s %v", di[0], di[1:]) | ||
err = cmd.Start() | ||
if err != nil { | ||
log.Fatalf("Error starting [%s] [%v]", debugCMD, err) | ||
} | ||
err = cmd.Wait() | ||
if err != nil { | ||
log.Fatalf("Error running [%s] [%v]", debugCMD, err) | ||
} | ||
} else { | ||
// Format the cmdLine string into separate execution tasks | ||
commandLines := strings.Split(cmdLine, ";") | ||
for x := range commandLines { | ||
command := strings.Split(commandLines[x], " ") | ||
cmd := exec.Command(command[0], command[1:]...) | ||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr | ||
debugCMD := fmt.Sprintf("%s %v", command[0], command[1:]) | ||
err = cmd.Start() | ||
if err != nil { | ||
log.Fatalf("Error starting [%s] [%v]", debugCMD, err) | ||
} | ||
err = cmd.Wait() | ||
if err != nil { | ||
log.Fatalf("Error running [%s] [%v]", debugCMD, err) | ||
} | ||
} | ||
} | ||
|
||
if chroot != "" { | ||
err = exitChroot() | ||
if err != nil { | ||
log.Errorf("Error exiting root from [%s], execution continuing", mountAction) | ||
} | ||
} | ||
} | ||
|
||
// Chroot handles changing the root, and returning a function to return back to the present directory. | ||
func Chroot(path string) (func() error, error) { | ||
cwd, err := os.Getwd() | ||
if err != nil { | ||
return nil, err | ||
} | ||
root, err := os.Open(cwd) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if err := syscall.Chroot(path); err != nil { | ||
root.Close() | ||
return nil, err | ||
} | ||
|
||
// set the working directory inside container | ||
if err := syscall.Chdir("/"); err != nil { | ||
root.Close() | ||
return nil, err | ||
} | ||
|
||
return func() error { | ||
defer root.Close() | ||
if err := root.Chdir(); err != nil { | ||
return err | ||
} | ||
return syscall.Chroot(".") | ||
}, nil | ||
} | ||
|
||
// MountSpecialDirs ensures that /dev /proc /sys exist in the chroot. | ||
func MountSpecialDirs() error { | ||
// Mount dev | ||
dev := filepath.Join(mountAction, "dev") | ||
|
||
if err := syscall.Mount("none", dev, "devtmpfs", syscall.MS_RDONLY, ""); err != nil { | ||
return fmt.Errorf("couldn't mount /dev to %v: %w", dev, err) | ||
} | ||
|
||
// Mount proc | ||
proc := filepath.Join(mountAction, "proc") | ||
|
||
if err := syscall.Mount("none", proc, "proc", syscall.MS_RDONLY, ""); err != nil { | ||
return fmt.Errorf("couldn't mount /proc to %v: %w", proc, err) | ||
} | ||
|
||
// Mount sys | ||
sys := filepath.Join(mountAction, "sys") | ||
|
||
if err := syscall.Mount("none", sys, "sysfs", syscall.MS_RDONLY, ""); err != nil { | ||
return fmt.Errorf("couldn't mount /sys to %v: %w", sys, err) | ||
} | ||
|
||
return nil | ||
} |