Skip to content

Commit

Permalink
refactor common docker client
Browse files Browse the repository at this point in the history
  • Loading branch information
niqdev committed Oct 5, 2023
1 parent ef8d8d4 commit 43b1fa6
Show file tree
Hide file tree
Showing 15 changed files with 282 additions and 203 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,10 @@ hckctl box parrot --provider cloud

Prerequisites
* start the retired [Postman](https://app.hackthebox.com/machines/Postman) machine in your account
* edit your local configurations
* edit your vpn network config
```bash
vim ${HOME}/.config/hck/config.yml

# edit vpn network
network:
vpn:
- name: htb
Expand All @@ -67,8 +66,11 @@ Prerequisites

Start the auto-exploitation box
```bash
# TODO
hckctl box parrot-sec
# exploit the machine and spawn a reverse shell
hckctl task htb-postman --network-vpn htb
hckctl box htb-postman --network-vpn htb
```

### Lab (preview)
Expand Down Expand Up @@ -108,7 +110,7 @@ tail -F ${HOME}/.local/state/hck/task/log/task-rustscan-*

Prerequisites
* start the retired [Lame](https://app.hackthebox.com/machines/Lame) and [Knife](https://app.hackthebox.com/machines/Knife) machines in your account
* edit your network vpn config (see box example above)
* edit your vpn network config (see box example above)

Run tasks against the vulnerable machine
```bash
Expand Down Expand Up @@ -281,6 +283,7 @@ TODO
- update directories to exclude in `resolvePath` e.g. charts
- add filters and review output e.g. table
* box
- print/event shared directory
- review tty resize
- implement copy ???
- kube: add distroless support
Expand Down
1 change: 1 addition & 0 deletions internal/command/box/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,6 @@ func newCreateOptions(info *template.TemplateInfo[boxModel.BoxV1], labels common
Size: size,
Labels: allLabels,
NetworkInfo: networkInfo,
ShareDir: configRef.Config.Common.ShareDir,
}, nil
}
4 changes: 2 additions & 2 deletions pkg/box/docker/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import (
"github.com/pkg/errors"

boxModel "github.com/hckops/hckctl/pkg/box/model"
"github.com/hckops/hckctl/pkg/client/docker"
commonDocker "github.com/hckops/hckctl/pkg/common/docker"
commonModel "github.com/hckops/hckctl/pkg/common/model"
"github.com/hckops/hckctl/pkg/event"
)

type DockerBoxClient struct {
client *docker.DockerClient
docker *commonDocker.DockerCommonClient
clientOpts *commonModel.DockerOptions
eventBus *event.EventBus
}
Expand Down
104 changes: 49 additions & 55 deletions pkg/box/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,69 +9,61 @@ import (

boxModel "github.com/hckops/hckctl/pkg/box/model"
"github.com/hckops/hckctl/pkg/client/docker"
commonDocker "github.com/hckops/hckctl/pkg/common/docker"
commonModel "github.com/hckops/hckctl/pkg/common/model"
"github.com/hckops/hckctl/pkg/schema"
)

func newDockerBoxClient(commonOpts *boxModel.CommonBoxOptions, dockerOpts *commonModel.DockerOptions) (*DockerBoxClient, error) {
commonOpts.EventBus.Publish(newInitDockerClientEvent())

dockerClient, err := docker.NewDockerClient()
dockerClient, err := commonDocker.NewDockerCommonClient(commonOpts.EventBus, dockerOpts)
if err != nil {
return nil, errors.Wrap(err, "error docker box")
return nil, errors.Wrap(err, "error docker box client")
}

return &DockerBoxClient{
client: dockerClient,
docker: dockerClient,
clientOpts: dockerOpts,
eventBus: commonOpts.EventBus,
}, nil
}

func (box *DockerBoxClient) close() error {
box.eventBus.Publish(newCloseDockerClientEvent())
box.eventBus.Close()
return box.client.Close()
return box.docker.Client.Close()
}

// TODO limit resources by size?
func (box *DockerBoxClient) createBox(opts *boxModel.CreateOptions) (*boxModel.BoxInfo, error) {

imageName := opts.Template.Image.Name()
imagePullOpts := &docker.ImagePullOpts{
ImageName: imageName,
OnImagePullCallback: func() {
box.eventBus.Publish(newImagePullDockerLoaderEvent(imageName))
},
}
box.eventBus.Publish(newImagePullDockerEvent(imageName))
if err := box.client.ImagePull(imagePullOpts); err != nil {
// try to use an existing image if exists
if box.clientOpts.IgnoreImagePullError {
box.eventBus.Publish(newImagePullIgnoreDockerEvent(imageName))
} else {
// do not allow offline
// boxName
containerName := opts.Template.GenerateName()

// vpn sidecar
var hostname string
var networkMode string
if opts.NetworkInfo.Vpn != nil {
if sidecarContainerId, err := box.docker.StartVpnSidecar(containerName, opts.NetworkInfo.Vpn); err != nil {
return nil, err
} else {
// fixes conflicting options: hostname and the network mode
hostname = ""
networkMode = docker.ContainerNetworkMode(sidecarContainerId)
// remove sidecar on exit
// TODO defer box.docker.Client.ContainerRemove(sidecarContainerId)
}
} else {
hostname = containerName
networkMode = docker.DefaultNetworkMode()
}

// cleanup obsolete nightly images
imageRemoveOpts := &docker.ImageRemoveOpts{
OnImageRemoveCallback: func(imageId string) {
box.eventBus.Publish(newImageRemoveDockerEvent(imageId))
},
OnImageRemoveErrorCallback: func(imageId string, err error) {
// ignore error: keep images used by existing containers
box.eventBus.Publish(newImageRemoveIgnoreDockerEvent(imageId, err))
},
}
if err := box.client.ImageRemoveDangling(imageRemoveOpts); err != nil {
imageName := opts.Template.Image.Name()
if err := box.docker.PullImageOffline(imageName, func() {
box.eventBus.Publish(newImagePullDockerLoaderEvent(imageName))
}); err != nil {
return nil, err
}

// boxName
containerName := opts.Template.GenerateName()

// TODO https://github.com/dperson/openvpn-client/issues/210
var containerEnv []docker.ContainerEnv
for _, e := range opts.Template.EnvironmentVariables() {
containerEnv = append(containerEnv, docker.ContainerEnv{Key: e.Key, Value: e.Value})
Expand All @@ -84,7 +76,7 @@ func (box *DockerBoxClient) createBox(opts *boxModel.CreateOptions) (*boxModel.B

containerConfig, err := docker.BuildContainerConfig(&docker.ContainerConfigOpts{
ImageName: imageName,
Hostname: containerName,
Hostname: hostname,
Env: containerEnv,
Ports: containerPorts,
Labels: opts.Labels,
Expand All @@ -99,13 +91,13 @@ func (box *DockerBoxClient) createBox(opts *boxModel.CreateOptions) (*boxModel.B
box.publishPortInfo(networkMap, containerName, port)
}
hostConfig, err := docker.BuildHostConfig(&docker.ContainerHostConfigOpts{
NetworkMode: docker.DefaultNetworkMode(),
NetworkMode: networkMode,
Ports: containerPorts,
OnPortBindCallback: onPortBindCallback,
Volumes: []docker.ContainerVolume{
{
HostDir: opts.ShareDir,
ContainerDir: commonModel.MountedShareDir,
ContainerDir: commonModel.MountShareDir,
},
},
})
Expand All @@ -114,20 +106,22 @@ func (box *DockerBoxClient) createBox(opts *boxModel.CreateOptions) (*boxModel.B
}

networkName := box.clientOpts.NetworkName
networkId, err := box.client.NetworkUpsert(networkName)
networkId, err := box.docker.Client.NetworkUpsert(networkName)
if err != nil {
return nil, err
}
box.eventBus.Publish(newNetworkUpsertDockerEvent(networkName, networkId))

containerOpts := &docker.ContainerCreateOpts{
ContainerName: containerName,
ContainerConfig: containerConfig,
HostConfig: hostConfig,
NetworkingConfig: docker.BuildNetworkingConfig(networkName, networkId), // all on the same network
WaitStatus: false,
OnContainerCreateCallback: func(string) error { return nil },
OnContainerWaitCallback: func(string) error { return nil },
ContainerName: containerName,
ContainerConfig: containerConfig,
HostConfig: hostConfig,
NetworkingConfig: docker.BuildNetworkingConfig(networkName, networkId), // all on the same network
WaitStatus: false,
CaptureInterrupt: false, // TODO ???
OnContainerInterruptCallback: func(string) {}, // TODO ???
OnContainerCreateCallback: func(string) error { return nil },
OnContainerWaitCallback: func(string) error { return nil },
OnContainerStatusCallback: func(status string) {
box.eventBus.Publish(newContainerCreateStatusDockerEvent(status))
},
Expand All @@ -139,7 +133,7 @@ func (box *DockerBoxClient) createBox(opts *boxModel.CreateOptions) (*boxModel.B
},
}
// boxId
containerId, err := box.client.ContainerCreate(containerOpts)
containerId, err := box.docker.Client.ContainerCreate(containerOpts)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -182,7 +176,7 @@ func (box *DockerBoxClient) execBox(template *boxModel.BoxV1, info *boxModel.Box
box.eventBus.Publish(newContainerRestartDockerEvent(info.Id, status))
},
}
if err := box.client.ContainerRestart(restartsOpts); err != nil {
if err := box.docker.Client.ContainerRestart(restartsOpts); err != nil {
return err
}

Expand All @@ -196,7 +190,7 @@ func (box *DockerBoxClient) execBox(template *boxModel.BoxV1, info *boxModel.Box

// already printed for temporary box
if !deleteOnExit {
containerDetails, err := box.client.ContainerInspect(info.Id)
containerDetails, err := box.docker.Client.ContainerInspect(info.Id)
if err != nil {
return err
}
Expand Down Expand Up @@ -230,7 +224,7 @@ func (box *DockerBoxClient) execBox(template *boxModel.BoxV1, info *boxModel.Box
box.eventBus.Publish(newContainerExecExitDockerEvent(info.Id))

if deleteOnExit {
if err := box.client.ContainerRemove(info.Id); err != nil {
if err := box.docker.Client.ContainerRemove(info.Id); err != nil {
box.eventBus.Publish(newContainerExecErrorDockerEvent(info.Id, errors.Wrap(err, "error container exec remove")))
}
}
Expand All @@ -240,7 +234,7 @@ func (box *DockerBoxClient) execBox(template *boxModel.BoxV1, info *boxModel.Box
},
}

return box.client.ContainerExec(execOpts)
return box.docker.Client.ContainerExec(execOpts)
}

func (box *DockerBoxClient) publishPortInfo(networkMap map[string]boxModel.BoxPort, containerName string, containerPort docker.ContainerPort) {
Expand All @@ -266,7 +260,7 @@ func (box *DockerBoxClient) logsBox(containerId string, streamOpts *commonModel.
box.eventBus.Publish(newContainerExecErrorDockerEvent(containerId, err))
},
}
return box.client.ContainerLogs(opts)
return box.docker.Client.ContainerLogs(opts)
}

func (box *DockerBoxClient) describeBox(name string) (*boxModel.BoxDetails, error) {
Expand All @@ -276,7 +270,7 @@ func (box *DockerBoxClient) describeBox(name string) (*boxModel.BoxDetails, erro
}

box.eventBus.Publish(newContainerInspectDockerEvent(info.Id))
containerInfo, err := box.client.ContainerInspect(info.Id)
containerInfo, err := box.docker.Client.ContainerInspect(info.Id)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -345,7 +339,7 @@ func boxLabel() string {

func (box *DockerBoxClient) listBoxes() ([]boxModel.BoxInfo, error) {

containers, err := box.client.ContainerList(boxModel.BoxPrefixName, boxLabel())
containers, err := box.docker.Client.ContainerList(boxModel.BoxPrefixName, boxLabel())
if err != nil {
return nil, err
}
Expand All @@ -371,7 +365,7 @@ func (box *DockerBoxClient) deleteBoxes(names []string) ([]string, error) {
// all or filter
if len(names) == 0 || slices.Contains(names, boxInfo.Name) {

if err := box.client.ContainerRemove(boxInfo.Id); err == nil {
if err := box.docker.Client.ContainerRemove(boxInfo.Id); err == nil {
deleted = append(deleted, boxInfo.Name)
box.eventBus.Publish(newContainerRemoveDockerEvent(boxInfo.Id))
} else {
Expand Down
8 changes: 0 additions & 8 deletions pkg/box/docker/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ func (e *dockerBoxEvent) String() string {
return e.value
}

func newInitDockerClientEvent() *dockerBoxEvent {
return &dockerBoxEvent{kind: event.LogDebug, value: "init docker client"}
}

func newCloseDockerClientEvent() *dockerBoxEvent {
return &dockerBoxEvent{kind: event.LogDebug, value: "close docker client"}
}

func newImagePullDockerEvent(imageName string) *dockerBoxEvent {
return &dockerBoxEvent{kind: event.LogInfo, value: fmt.Sprintf("image pull: imageName=%s", imageName)}
}
Expand Down
10 changes: 7 additions & 3 deletions pkg/box/model/provider.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package model

import (
"github.com/hckops/hckctl/pkg/common/model"
)

type BoxProvider string

const (
Docker BoxProvider = "docker"
Kubernetes BoxProvider = "kube"
Cloud BoxProvider = "cloud"
Docker BoxProvider = model.DockerProvider
Kubernetes BoxProvider = model.KubernetesProvider
Cloud BoxProvider = model.CloudProvider
)

func (p BoxProvider) String() string {
Expand Down
2 changes: 1 addition & 1 deletion pkg/client/docker/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func BuildHostConfig(opts *ContainerHostConfigOpts) (*container.HostConfig, erro
if opts.Volumes != nil {
for _, volume := range opts.Volumes {

if err := util.CreateDir(volume.HostDir); err != nil {
if err := util.CreateBaseDir(volume.HostDir); err != nil {
return nil, errors.Wrap(err, "error docker local volume")
}

Expand Down
Loading

0 comments on commit 43b1fa6

Please sign in to comment.