Skip to content

Commit

Permalink
Remove NET_ADMIN check, introduce PSP check
Browse files Browse the repository at this point in the history
The change in #2920 introduced a PodSecurityPolicy, providing
`NET_ADMIN` capability to Linkerd. This eliminated the need for a
`NET_ADMIN` capability check in `linkerd check`, as the default
`linkerd install` now guarantees `NET_ADMIN` capability. At the same
time, this added a requirement on that `linkerd install` create a
PodSecurityPolicy.

Remove the `NET_ADMIN` capability check from `linkerd check`. Introduce
a check to validate the user can create a PodSecurityPolicy.

Fixes #2884, #2849.

Signed-off-by: Andrew Seigner <[email protected]>
  • Loading branch information
siggy committed Jun 18, 2019
1 parent 928d4cb commit 536be20
Show file tree
Hide file tree
Showing 4 changed files with 8 additions and 114 deletions.
6 changes: 0 additions & 6 deletions cli/cmd/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ type checkOptions struct {
dataPlaneOnly bool
wait time.Duration
namespace string
cniEnabled bool
output string
}

Expand All @@ -33,7 +32,6 @@ func newCheckOptions() *checkOptions {
dataPlaneOnly: false,
wait: 300 * time.Second,
namespace: "",
cniEnabled: false,
output: tableOutput,
}
}
Expand All @@ -42,7 +40,6 @@ func newCheckOptions() *checkOptions {
func (options *checkOptions) nonConfigFlagSet() *pflag.FlagSet {
flags := pflag.NewFlagSet("non-config-check", pflag.ExitOnError)

flags.BoolVar(&options.cniEnabled, "linkerd-cni-enabled", options.cniEnabled, "When running pre-installation checks (--pre), assume the linkerd-cni plugin is already installed, and a NET_ADMIN check is not needed")
flags.StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace to use for --proxy checks (default: all namespaces)")
flags.BoolVar(&options.preInstallOnly, "pre", options.preInstallOnly, "Only run pre-installation checks, to determine if the control plane can be installed")
flags.BoolVar(&options.dataPlaneOnly, "proxy", options.dataPlaneOnly, "Only run data-plane checks, to determine if the data plane is healthy")
Expand Down Expand Up @@ -146,9 +143,6 @@ func configureAndRunChecks(wout io.Writer, werr io.Writer, stage string, options

if options.preInstallOnly {
checks = append(checks, healthcheck.LinkerdPreInstallChecks)
if !options.cniEnabled {
checks = append(checks, healthcheck.LinkerdPreInstallCapabilityChecks)
}
} else {
checks = append(checks, healthcheck.LinkerdConfigChecks)

Expand Down
67 changes: 7 additions & 60 deletions pkg/healthcheck/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@ const (
// checks must be added first.
LinkerdPreInstallChecks CategoryID = "pre-kubernetes-setup"

// LinkerdPreInstallCapabilityChecks adds a check to validate the user has the
// capabilities necessary to deploy Linkerd. For example, the NET_ADMIN
// capability is required by the `linkerd-init` container to modify IP tables.
// These checks are no run when the `--linkerd-cni-enabled` flag is set.
LinkerdPreInstallCapabilityChecks CategoryID = "pre-kubernetes-capability"

// LinkerdConfigChecks enabled by `linkerd check config`

// LinkerdConfigChecks adds a series of checks to validate that the Linkerd
Expand Down Expand Up @@ -309,6 +303,13 @@ func (hc *HealthChecker) allCategories() []category {
return hc.checkCanCreate("", "apiextensions.k8s.io", "v1beta1", "customresourcedefinitions")
},
},
{
description: "can create PodSecurityPolicies",
hintAnchor: "pre-k8s",
check: func(context.Context) error {
return hc.checkCanCreate(hc.ControlPlaneNamespace, "policy", "v1beta1", "podsecuritypolicies")
},
},
{
description: "can create ServiceAccounts",
hintAnchor: "pre-k8s",
Expand Down Expand Up @@ -346,18 +347,6 @@ func (hc *HealthChecker) allCategories() []category {
},
},
},
{
id: LinkerdPreInstallCapabilityChecks,
checkers: []checker{
{
description: "has NET_ADMIN capability",
hintAnchor: "pre-k8s-cluster-net-admin",
check: func(context.Context) error {
return hc.checkNetAdmin()
},
},
},
},
{
id: LinkerdConfigChecks,
checkers: []checker{
Expand Down Expand Up @@ -966,48 +955,6 @@ func (hc *HealthChecker) checkCanCreate(namespace, group, version, resource stri
)
}

func (hc *HealthChecker) checkNetAdmin() error {
if hc.kubeAPI == nil {
// we should never get here
return fmt.Errorf("unexpected error: Kubernetes ClientSet not initialized")
}

pspList, err := hc.kubeAPI.PolicyV1beta1().PodSecurityPolicies().List(metav1.ListOptions{})
if err != nil {
return err
}

if len(pspList.Items) == 0 {
// no PodSecurityPolicies found, assume PodSecurityPolicy admission controller is disabled
return nil
}

// if PodSecurityPolicies are found, validate one exists that:
// 1) permits usage
// AND
// 2) provides NET_ADMIN
for _, psp := range pspList.Items {
err := k8s.ResourceAuthz(
hc.kubeAPI,
"",
"use",
"policy",
"v1beta1",
"podsecuritypolicies",
psp.GetName(),
)
if err == nil {
for _, capability := range psp.Spec.AllowedCapabilities {
if capability == "*" || capability == "NET_ADMIN" {
return nil
}
}
}
}

return fmt.Errorf("found %d PodSecurityPolicies, but none provide NET_ADMIN", len(pspList.Items))
}

func (hc *HealthChecker) checkClockSkew() error {
if hc.kubeAPI == nil {
// we should never get here
Expand Down
48 changes: 0 additions & 48 deletions pkg/healthcheck/healthcheck_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -386,54 +386,6 @@ status:

}

func TestCheckNetAdmin(t *testing.T) {
tests := []struct {
k8sConfigs []string
err error
}{
{
[]string{},
nil,
},
{
[]string{`apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted
spec:
requiredDropCapabilities:
- ALL`,
},
fmt.Errorf("found 1 PodSecurityPolicies, but none provide NET_ADMIN"),
},
}

for i, test := range tests {
test := test // pin
t.Run(fmt.Sprintf("%d: returns expected NET_ADMIN result", i), func(t *testing.T) {
hc := NewHealthChecker(
[]CategoryID{},
&Options{},
)

var err error
hc.kubeAPI, err = k8s.NewFakeAPI(test.k8sConfigs...)
if err != nil {
t.Fatalf("Unexpected error: %s", err)
}

err = hc.checkNetAdmin()
if err != nil || test.err != nil {
if (err == nil && test.err != nil) ||
(err != nil && test.err == nil) ||
(err.Error() != test.err.Error()) {
t.Fatalf("Unexpected error (Expected: %s, Got: %s)", test.err, err)
}
}
})
}
}

func TestConfigExists(t *testing.T) {
testCases := []struct {
k8sConfigs []string
Expand Down
1 change: 1 addition & 0 deletions test/testdata/check.pre.golden
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pre-kubernetes-setup
√ can create ClusterRoles
√ can create ClusterRoleBindings
√ can create CustomResourceDefinitions
√ can create PodSecurityPolicies
√ can create ServiceAccounts
√ can create Services
√ can create Deployments
Expand Down

0 comments on commit 536be20

Please sign in to comment.