fix(plugin): handle a missing exec permission in status (#11687) · cloudnative-pg/cloudnative-pg@69dab5d · GitHub
Skip to content

Commit 69dab5d

Browse files
gbartoliniarmrumnencia
authored
fix(plugin): handle a missing exec permission in status (#11687)
`kubectl cnpg status` runs commands in the primary Pod through exec: `du` for the cluster size and, with `-v -v`, `cat` to read `custom.conf` and `pg_hba.conf`. Without the `pods/exec` permission, it printed raw exec errors, and the configuration sections made the command fail. Check permissions first with a `SelfSubjectAccessReview` on the primary Pod, and show `not permitted: requires the pods/exec permission` in place of the size and configuration sections. If the review itself fails, run the command anyway and report the actual error. When no Pod holds the primary role, as during bootstrap or a failover, skip the check, because the review would have no namespace and be evaluated at cluster scope. Mark `pods/exec` as optional for `status` in the kubectl plugin documentation. Assisted-by: Claude References #11519 Signed-off-by: Gabriele Bartolini <gabriele.bartolini@enterprisedb.com> Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com> Signed-off-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com> Co-authored-by: Armando Ruocco <armando.ruocco@enterprisedb.com> Co-authored-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com>
1 parent 3503f78 commit 69dab5d

3 files changed

Lines changed: 125 additions & 1 deletion

File tree

‎docs/src/kubectl-plugin.md‎

Lines changed: 4 additions & 1 deletion

‎internal/cmd/plugin/status/status.go‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package status
2222
import (
2323
"context"
2424
"encoding/json"
25+
"errors"
2526
"fmt"
2627
"os"
2728
"path"
@@ -35,6 +36,7 @@ import (
3536
"github.com/cloudnative-pg/machinery/pkg/stringset"
3637
"github.com/cloudnative-pg/machinery/pkg/types"
3738
"github.com/logrusorgru/aurora/v4"
39+
authorizationv1 "k8s.io/api/authorization/v1"
3840
corev1 "k8s.io/api/core/v1"
3941
policyv1 "k8s.io/api/policy/v1"
4042
apierrs "k8s.io/apimachinery/pkg/api/errors"
@@ -218,11 +220,52 @@ func listFencedInstances(fencedInstances *stringset.Data) string {
218220
return strings.Join(fencedInstances.ToList(), ", ")
219221
}
220222

223+
// errExecNotPermitted is returned when the user lacks `pods/exec`
224+
var errExecNotPermitted = errors.New("not permitted: requires the pods/exec permission")
225+
226+
// checkExecPermission returns errExecNotPermitted when the user cannot exec
227+
// into the primary Pod. If the check itself fails, it returns nil so the
228+
// command runs and reports the actual error. Without a primary Pod the review
229+
// would have no namespace and run at cluster scope, so it is skipped.
230+
func (fullStatus *PostgresqlStatus) checkExecPermission(
231+
ctx context.Context,
232+
client kubernetes.Interface,
233+
) error {
234+
if fullStatus.PrimaryPod.Name == "" {
235+
return nil
236+
}
237+
238+
pod := &fullStatus.PrimaryPod
239+
review, err := client.AuthorizationV1().SelfSubjectAccessReviews().Create(
240+
ctx,
241+
&authorizationv1.SelfSubjectAccessReview{
242+
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
243+
ResourceAttributes: &authorizationv1.ResourceAttributes{
244+
Namespace: pod.Namespace,
245+
Verb: "create",
246+
Resource: "pods",
247+
Subresource: "exec",
248+
Name: pod.Name,
249+
},
250+
},
251+
},
252+
metav1.CreateOptions{},
253+
)
254+
if err == nil && !review.Status.Allowed {
255+
return errExecNotPermitted
256+
}
257+
return nil
258+
}
259+
221260
func (fullStatus *PostgresqlStatus) getClusterSize(
222261
ctx context.Context,
223262
client kubernetes.Interface,
224263
timeout time.Duration,
225264
) (string, error) {
265+
if err := fullStatus.checkExecPermission(ctx, client); err != nil {
266+
return "", err
267+
}
268+
226269
// Compute the disk space through `du`
227270
output, _, err := utils.ExecCommand(
228271
ctx,
@@ -322,6 +365,8 @@ func (fullStatus *PostgresqlStatus) printBasicInfo(
322365
summary.AddLine("Size:", "- (hibernated)")
323366
case isPrimaryFenced:
324367
summary.AddLine("Size:", "- (fenced)")
368+
case errors.Is(clusterSizeErr, errExecNotPermitted):
369+
summary.AddLine("Size:", "- ("+clusterSizeErr.Error()+")")
325370
default:
326371
summary.AddLine("Size:", aurora.Red(clusterSizeErr.Error()))
327372
}
@@ -491,6 +536,17 @@ func (fullStatus *PostgresqlStatus) printPostgresConfiguration(
491536
client kubernetes.Interface,
492537
timeout time.Duration,
493538
) []error {
539+
if err := fullStatus.checkExecPermission(ctx, client); err != nil {
540+
notPermitted := "- (" + err.Error() + ")"
541+
fmt.Println(aurora.Green("PostgreSQL Configuration"))
542+
fmt.Println(notPermitted)
543+
fmt.Println()
544+
fmt.Println(aurora.Green("PostgreSQL HBA Rules"))
545+
fmt.Println(notPermitted)
546+
fmt.Println()
547+
return nil
548+
}
549+
494550
var errs []error
495551

496552
// Read PostgreSQL configuration from custom.conf

‎internal/cmd/plugin/status/status_test.go‎

Lines changed: 65 additions & 0 deletions

0 commit comments

Comments
 (0)