diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75976c8..0a1831a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -134,7 +134,7 @@ jobs: - uses: docker/setup-buildx-action@v3 # NOSONAR githubactions:S7637 - verified action creator - name: Log in to the container registry - if: github.event_name != 'pull_request' + if: true uses: docker/login-action@v3 # NOSONAR githubactions:S7637 - verified action creator with: registry: ghcr.io @@ -157,7 +157,7 @@ jobs: uses: docker/build-push-action@v6 # NOSONAR githubactions:S7637 - verified action creator with: context: ./ - push: ${{ github.event_name != 'pull_request' }} + push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64 @@ -244,7 +244,7 @@ jobs: version: latest - name: "[build] Upload packages to gcp internal repositories" - if: github.event_name != 'pull_request' + if: true working-directory: ./dist run: | for deb in $(find * -name '*.deb'); do diff --git a/Dockerfile b/Dockerfile index c46ab5e..8f5688a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -81,11 +81,10 @@ COPY --from=build /app/extension /extension COPY --from=build /app/licenses /licenses #We declare the javaagent directory a volume, so we can write the heartbeat to it, when read only -VOLUME /javaagent COPY --chown=$USER_UID:$USER_GID javaagents/download/target/javaagent /javaagent -ENV STEADYBIT_EXTENSION_JAVA_AGENT_PATH=/javaagent +ENV STEADYBIT_EXTENSION_JAVA_AGENT_PATH=/javaagent:/tmp/javaagent EXPOSE 8085 8081 ENTRYPOINT ["/extension"] diff --git a/extjvm/action_commons_test.go b/extjvm/action_commons_test.go index d0b7fbf..ea34a62 100644 --- a/extjvm/action_commons_test.go +++ b/extjvm/action_commons_test.go @@ -2,15 +2,16 @@ package extjvm import ( "fmt" + "io" + "os/exec" + "strconv" + "time" + "github.com/shirou/gopsutil/v4/process" "github.com/steadybit/action-kit/go/action_kit_api/v2" "github.com/steadybit/extension-jvm/extjvm/jvm" "github.com/steadybit/extension-jvm/extjvm/jvm/starttime" "github.com/stretchr/testify/mock" - "io" - "os/exec" - "strconv" - "time" ) type mockJavaFacade struct { @@ -153,12 +154,12 @@ func (f *FakeJvm) VmArgs() string { return "" } -func (f *FakeJvm) UserId() string { - return "" +func (f *FakeJvm) UserId() int { + return -1 } -func (f *FakeJvm) GroupId() string { - return "" +func (f *FakeJvm) GroupId() int { + return -1 } func (f *FakeJvm) Path() string { diff --git a/extjvm/container/container.go b/extjvm/container/container.go index 10ebd52..50e14da 100644 --- a/extjvm/container/container.go +++ b/extjvm/container/container.go @@ -26,7 +26,7 @@ var ( func Exec(ctx context.Context, containerId string, name string, args ...string) *exec.Cmd { runtime := path.Base(cfg.Path) - runtimeArgs := []string{"--root", cfg.Root, "exec", containerId, name} + runtimeArgs := []string{"--root", cfg.Root, "exec", "--user", "0:0", containerId, name} if cfg.Debug { runtimeArgs = append([]string{"--debug"}, runtimeArgs...) } diff --git a/extjvm/jvm/java_process_inspector_test.go b/extjvm/jvm/java_process_inspector_test.go index a29170a..03b8d38 100644 --- a/extjvm/jvm/java_process_inspector_test.go +++ b/extjvm/jvm/java_process_inspector_test.go @@ -1,15 +1,15 @@ package jvm import ( - "github.com/steadybit/extension-jvm/extjvm/jvm/hsperf" - "github.com/steadybit/extension-jvm/extjvm/jvm/test" - "github.com/steadybit/extension-jvm/extjvm/utils" - "github.com/stretchr/testify/assert" "os" - "strconv" "strings" "testing" "time" + + "github.com/steadybit/extension-jvm/extjvm/jvm/hsperf" + "github.com/steadybit/extension-jvm/extjvm/jvm/test" + "github.com/steadybit/extension-jvm/extjvm/utils" + "github.com/stretchr/testify/assert" ) var hostname string @@ -37,8 +37,8 @@ func Test_should_inspect_host_process_via_process(t *testing.T) { case j := <-inspector.JavaVms: assert.Equal(t, jvm.Pid(), j.Pid()) assert.Contains(t, j.CommandLine(), "60000") - assert.Equal(t, strconv.Itoa(os.Geteuid()), j.UserId()) - assert.Equal(t, strconv.Itoa(os.Geteuid()), j.UserId()) + assert.Equal(t, os.Getuid(), j.UserId()) + assert.Equal(t, os.Getgid(), j.GroupId()) assert.Equal(t, hostname, j.Hostname()) assert.True(t, j.IsRunning()) assert.Condition(t, func() bool { @@ -81,8 +81,8 @@ func Test_should_inspect_host_process_via_hsperf(t *testing.T) { case j := <-inspector.JavaVms: assert.Equal(t, jvm.Pid(), j.Pid()) assert.Contains(t, j.CommandLine(), "60000") - assert.Equal(t, strconv.Itoa(os.Geteuid()), j.UserId()) - assert.Equal(t, strconv.Itoa(os.Geteuid()), j.UserId()) + assert.Equal(t, os.Getuid(), j.UserId()) + assert.Equal(t, os.Getgid(), j.GroupId()) assert.Equal(t, hostname, j.Hostname()) assert.True(t, j.IsRunning()) assert.Condition(t, func() bool { diff --git a/extjvm/jvm/jvm_attach.go b/extjvm/jvm/jvm_attach.go index 3d645db..d35547d 100644 --- a/extjvm/jvm/jvm_attach.go +++ b/extjvm/jvm/jvm_attach.go @@ -5,11 +5,13 @@ import ( "fmt" "os" "os/exec" - "os/user" "runtime" + "strings" + "sync" "time" "github.com/rs/zerolog/log" + "github.com/steadybit/extension-jvm/extjvm/utils" ) type Attachment interface { @@ -23,12 +25,36 @@ const ( initJarName = "javaagent-init.jar" ) +var ( + javaagentWorkDir string + javaagentWorkDirOnce = sync.OnceFunc(func() { + paths := strings.SplitN(os.Getenv("STEADYBIT_EXTENSION_JAVA_AGENT_PATH"), ":", 2) + if len(paths) == 0 { + panic("STEADYBIT_EXTENSION_JAVA_AGENT_PATH not set") + } + + if len(paths) == 1 { + javaagentWorkDir = paths[0] + return + } + + // A single time we copy the javaagent files to a writable location, as there will be also the heartbeat file created + javaagentWorkDir = paths[1] + if out, err := utils.RootCommandContext(context.Background(), "mkdir", "-p", javaagentWorkDir).CombinedOutput(); err != nil { + panic("Could not create javaagent working directory: " + err.Error() + ":\n" + string(out)) + } + + if err := os.CopyFS(javaagentWorkDir, os.DirFS(paths[0])); err != nil { + panic("Could not copy javaagent: " + err.Error()) + } + + log.Info().Str("dir", javaagentWorkDir).Msg("prepared javaagent directory") + }) +) + func javaagentPath() string { - pathByEnv := os.Getenv("STEADYBIT_EXTENSION_JAVA_AGENT_PATH") - if pathByEnv != "" { - return pathByEnv - } - panic("STEADYBIT_EXTENSION_JAVA_AGENT_PATH not set") + javaagentWorkDirOnce() + return javaagentWorkDir } func GetAttachment(jvm JavaVm) Attachment { @@ -66,7 +92,7 @@ func externalAttach(jvm JavaVm, agentJar, initJar string, heartbeatFile string, } if needsUserSwitch(jvm) { - attachCommand = append(attachCommand, "uid="+jvm.UserId(), "gid="+jvm.GroupId()) + attachCommand = append(attachCommand, fmt.Sprintf("uid=%d", jvm.UserId()), fmt.Sprintf("gid=%d", jvm.GroupId())) } var ctx, cancel = context.WithTimeout(context.Background(), time.Duration(60)*time.Second) @@ -86,16 +112,11 @@ func externalAttach(jvm JavaVm, agentJar, initJar string, heartbeatFile string, } func needsUserSwitch(jvm JavaVm) bool { - if jvm.UserId() == "" || jvm.GroupId() == "" { + if jvm.UserId() == -1 || jvm.GroupId() == -1 { return false } - current, err := user.Current() - if err != nil { - log.Warn().Err(err).Msg("Could not determine current user") - return false - } - return jvm.UserId() != current.Uid || jvm.GroupId() != current.Gid + return jvm.UserId() != os.Getuid() || jvm.GroupId() != os.Getgid() } func getExecutable(jvm JavaVm) string { diff --git a/extjvm/jvm/types.go b/extjvm/jvm/types.go index 5859692..8f91c22 100644 --- a/extjvm/jvm/types.go +++ b/extjvm/jvm/types.go @@ -4,11 +4,12 @@ import ( "context" "errors" "fmt" + "strings" + "github.com/rs/zerolog/log" "github.com/shirou/gopsutil/v4/process" "github.com/steadybit/extension-jvm/extjvm/jvm/starttime" "github.com/steadybit/extension-kit/extruntime" - "strings" ) type JavaVm interface { @@ -16,8 +17,8 @@ type JavaVm interface { CommandLine() string MainClass() string ClassPath() string - UserId() string - GroupId() string + UserId() int + GroupId() int Path() string Hostname() string HostFQDN() string @@ -43,8 +44,8 @@ type defaultJavaVm struct { vmVendor string vmName string vmArgs string - userId string - groupId string + userId int + groupId int path string discoveredVia string hostname string @@ -68,11 +69,11 @@ func (vm defaultJavaVm) ClassPath() string { return vm.classPath } -func (vm defaultJavaVm) UserId() string { +func (vm defaultJavaVm) UserId() int { return vm.userId } -func (vm defaultJavaVm) GroupId() string { +func (vm defaultJavaVm) GroupId() int { return vm.groupId } @@ -126,11 +127,15 @@ func newJavaVm(p *process.Process, via string) *defaultJavaVm { } if uids, err := p.Uids(); err == nil && len(uids) > 0 { - vm.userId = fmt.Sprintf("%d", uids[0]) + vm.userId = int(uids[0]) + } else { + vm.userId = -1 } if gids, err := p.Gids(); err == nil && len(gids) > 0 { - vm.groupId = fmt.Sprintf("%d", gids[0]) + vm.groupId = int(gids[0]) + } else { + vm.groupId = -1 } if processPath, err := getProcessPath(context.Background(), p); err == nil && processPath != "" { @@ -155,13 +160,12 @@ func (vm defaultJavaVm) StartTime() starttime.Time { } func (vm defaultJavaVm) ToDebugString() string { - return fmt.Sprintf("JavaVm{pid=%d, discoveredVia=%s, commandLine=%s, mainClass=%s, classpath=%s, vmVersion=%s, vmVendor=%s, vmName=%s, vmArgs=%s, userId=%s, groupId=%s, path=%s}", + return fmt.Sprintf("JavaVm{pid=%d, discoveredVia=%s, commandLine=%s, mainClass=%s, classpath=%s, vmVersion=%s, vmVendor=%s, vmName=%s, vmArgs=%s, userId=%d, groupId=%d, path=%s}", vm.p.Pid, vm.discoveredVia, vm.commandLine, vm.mainClass, vm.classPath, vm.vmVersion, vm.vmVendor, vm.vmName, vm.vmArgs, vm.userId, vm.groupId, vm.path) } func (vm defaultJavaVm) ToInfoString() string { - return fmt.Sprintf("JavaVm{pid=%d, discoveredVia=%s, mainClass=%s}", - vm.p.Pid, vm.discoveredVia, vm.mainClass) + return fmt.Sprintf("JavaVm{pid=%d (%d:%d), discoveredVia=%s, mainClass=%s}", vm.p.Pid, vm.userId, vm.groupId, vm.discoveredVia, vm.mainClass) } type defaultJavaVmInContainer struct { @@ -179,11 +183,10 @@ func (vm defaultJavaVmInContainer) PidInContainer() int32 { } func (vm defaultJavaVmInContainer) ToDebugString() string { - return fmt.Sprintf("JavaVm{pid=%d, containerId=%s, pidInContainer=%d, discoveredVia=%s, commandLine=%s, mainClass=%s, classpath=%s, vmVersion=%s, vmVendor=%s, vmName=%s, vmArgs=%s, userId=%s, groupId=%s, path=%s}", + return fmt.Sprintf("JavaVm{pid=%d, containerId=%s, pidInContainer=%d, discoveredVia=%s, commandLine=%s, mainClass=%s, classpath=%s, vmVersion=%s, vmVendor=%s, vmName=%s, vmArgs=%s, userId=%d, groupId=%d, path=%s}", vm.p.Pid, vm.containerId, vm.pidInContainer, vm.discoveredVia, vm.commandLine, vm.mainClass, vm.classPath, vm.vmVersion, vm.vmVendor, vm.vmName, vm.vmArgs, vm.userId, vm.groupId, vm.path) } func (vm defaultJavaVmInContainer) ToInfoString() string { - return fmt.Sprintf("JavaVm{pid=%d, containerId=%s, discoveredVia=%s, mainClass=%s}", - vm.p.Pid, vm.containerId, vm.discoveredVia, vm.mainClass) + return fmt.Sprintf("JavaVm{pid=%d (%d:%d), containerId=%s, discoveredVia=%s, mainClass=%s}", vm.p.Pid, vm.userId, vm.groupId, vm.containerId, vm.discoveredVia, vm.mainClass) }