Skip to content

Add --single-namespace install flag for restricted permissions#1721

Merged
klingerf merged 5 commits intomasterfrom
kl/single-namespace
Oct 11, 2018
Merged

Add --single-namespace install flag for restricted permissions#1721
klingerf merged 5 commits intomasterfrom
kl/single-namespace

Conversation

@klingerf
Copy link
Contributor

This branch updates the linkerd install and linkerd check commands to accept a --single-namespace boolean flag, which if provided will modify the linkerd install YAML to use Roles instead of ClusterRoles, and it will adjust the pre-install check accordingly.

The bulk of the code changes required for conditionally switching to Roles happens in controller/k8s/api.go, where we configure the shared informers used by all of our controller components for reading from the Kubernetes API. If the --single-namespace flag is specified, all watches established in this file are scoped to an individual namespace.

Fixes #1705.

@klingerf klingerf self-assigned this Sep 27, 2018
@klingerf klingerf requested a review from siggy September 27, 2018 18:43
@klingerf
Copy link
Contributor Author

@olix0r I updated the install command to prefix all of the deployments and configmaps with "linkerd-" when the --single-namespace flag is present. Note that I haven't renamed the services as part of this change, since all of our other CLI subcommands use hardcoded service names for the control plane, and I didn't want to require users to add the --single-namespace flag to every command that they run. I think a better fix would be to rename all of the services to always include the "linkerd-" prefix, even in cluster-wide mode, but that would be a breaking change for upgrading the control plane, so I've punted on it for now.

cli/cmd/check.go Outdated
cmd.PersistentFlags().BoolVar(&options.dataPlaneOnly, "proxy", options.dataPlaneOnly, "Only run data-plane checks, to determine if the data plane is healthy")
cmd.PersistentFlags().DurationVar(&options.wait, "wait", options.wait, "Retry and wait for some checks to succeed if they don't pass the first time")
cmd.PersistentFlags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace to use for --proxy checks (default: all namespaces)")
cmd.PersistentFlags().BoolVar(&options.singleNamespace, "single-namespace", options.singleNamespace, "Only check the permissions required to operate the control plane in a single namespace")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about mentioning that this flag only applies when --pre is also set? Is that going to change soon?


var resourcePrefix string
if options.singleNamespace {
resourcePrefix = "linkerd-"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be part of a larger conversation, but I'd prefer to see as few naming differences between single-namespace and default as possible. A few reasons:

  • debugging (asking a user to do kubectl logs -f deploy/[linkerd-]web
  • docs / screenshot consistency
  • control plane code / testing complexity

if namespace == "" {
sharedInformers = informers.NewSharedInformerFactory(k8sClient, 10*time.Minute)
} else {
sharedInformers = informers.NewFilteredSharedInformerFactory(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL !

cm coreinformers.ConfigMapInformer
deploy appinformers.DeploymentInformer
endpoint coreinformers.EndpointsInformer
ns coreinformers.NamespaceInformer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

var namespaces []*apiv1.Namespace
namespaces := make([]*apiv1.Namespace, 0)

if name == "" && api.namespace != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if name != "" && name != api.namespace, should we log and/or return an error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we return an error here, that would break the CLI and web frontend, which omit the namespace when requesting stat/tap data for all namespaces. Instead, in single namespace mode we want to pretend that there's only one namespace in the cluster.

if pod.Status.Phase == v1.PodRunning {
name := strings.Split(pod.Name, "-")[0]
// strip the single-namespace "linkerd-" prefix if it exists
name := strings.TrimPrefix(pod.Name, "linkerd-")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider making linkerd- a constant and re-using in cli/cmd/install.go?

Related to previous comment, I'd really prefer to do away with this special-casing, as it will increase our support burden.

@klingerf
Copy link
Contributor Author

klingerf commented Oct 4, 2018

@siggy Thanks for the feedback on this PR. I agree that renaming things as part of a single namespace install will be more of a burden going forward. I spoke to @olix0r about this, and I'm going to back out that change. Instead, we're going to signal that this feature "experimental", likely by changing the flag to indicate that explicitly.

But all of that said, I'm also going to wait to update and merge this branch until #1714 is ready, since that change has implications for this one as well.

Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
@klingerf klingerf force-pushed the kl/single-namespace branch from c9f7bd1 to 961f9ec Compare October 10, 2018 21:51
Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
@klingerf
Copy link
Contributor Author

Ok, have rebased the branch against latest master, backed out the naming changes, and made the --single-namespace flag mutually exclusive from the --proxy-auto-inject flag. As part of this change, I've updated the help text for both flags to indicated that they are experimental. @siggy, it should be ready for another re-review if you don't mind.

Copy link
Member

@siggy siggy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, this looks much simpler! One TIOLI comment, then 🚢 !

return hc.checkCanCreate("", "rbac.authorization.k8s.io", "v1beta1", "ClusterRole")
},
})
if hc.SingleNamespace {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIOLI: consider refactoring this block with something like...

roleType := "ClusterRole"
roleBindingType := "ClusterRoleBinding"
if hc.SingleNamespace {
  roleType = "Role"
  roleBindingType = "RoleBinding"
}

hc.checkers = append(hc.checkers, &checker{
	category:    LinkerdPreInstallCategory,
	description: fmt.Sprintf("can create %ss", roleType),
	fatal:       true,
	check: func() error {
		return hc.checkCanCreate("", "rbac.authorization.k8s.io", "v1beta1", roleType)
	},
})

hc.checkers = append(hc.checkers, &checker{
	category:    LinkerdPreInstallCategory,
	description: fmt.Sprintf("can create %ss", roleBindingType),
	fatal:       true,
	check: func() error {
		return hc.checkCanCreate("", "rbac.authorization.k8s.io", "v1beta1", roleBindingType)
	},
})

Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
Copy link
Member

@siggy siggy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 :shipit:

Copy link
Member

@adleong adleong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Should we also restrict the Prometheus scrape config to only scrape from the controller namespace?

Are there plans in the future for making this more flexible? I'm imagining the ability to specify a list of namespaces that Linkerd will manage. We would then install Roles and RoleBindings into each of those namespaces. This also allows the control plane and data plane to be in separate namespaces without requiring cluster wide access.

@klingerf
Copy link
Contributor Author

@adleong This branch includes a modification to the prometheus scrape config so that it will only scrape proxies in the controller namespace when the --single-namespace flag is specified, here:

- job_name: 'linkerd-proxy'
kubernetes_sd_configs:
- role: pod
{{- if .SingleNamespace}}
namespaces:
names: ['{{.Namespace}}']
{{- end}}

Interesting idea about allowing Linkerd to monitor a specified list of namespaces via separate role bindings. That might take a bit of coding effort, since it would require establishing multiple watches per resource, and all of our code assumes a single watch per resource. But yeah, it seems feasible that we could upgrade to that setup.

@klingerf klingerf merged commit 46c887c into master Oct 11, 2018
@klingerf klingerf deleted the kl/single-namespace branch October 11, 2018 17:56
@adleong
Copy link
Member

adleong commented Oct 11, 2018

Arg, I was looking for that prometheus config change but somehow skipped right past it! Thanks, @klingerf!

siggy added a commit that referenced this pull request Mar 8, 2019
#1721 introduced a `--single-namespace` install flag,
enabling the control-plane to function within a single namespace. With
the introduction of ServiceProfiles, and upcoming identity changes, this
single namespace mode of operation is becoming less viable.

This change removes the `--single-namespace` install flag, and all
underlying support. The control-plane must have cluster-wide access to
operate.

A few related changes:
- Remove `--single-namespace` from `linkerd check`, this motivates
  combining some check categories, as we can always assume cluster-wide
  requirements.
- Simplify the `k8s.ResourceAuthz` API, as callers no longer need to
  make a decision based on cluster-wide vs. namespace-wide access.
  Components either have access, or they error out.
- Modify the web dashboard to always assume ServiceProfiles are enabled.

Reverts #1721
Part of #2337

Signed-off-by: Andrew Seigner <siggy@buoyant.io>
siggy added a commit that referenced this pull request Mar 8, 2019
#1721 introduced a `--single-namespace` install flag,
enabling the control-plane to function within a single namespace. With
the introduction of ServiceProfiles, and upcoming identity changes, this
single namespace mode of operation is becoming less viable.

This change removes the `--single-namespace` install flag, and all
underlying support. The control-plane must have cluster-wide access to
operate.

A few related changes:
- Remove `--single-namespace` from `linkerd check`, this motivates
  combining some check categories, as we can always assume cluster-wide
  requirements.
- Simplify the `k8s.ResourceAuthz` API, as callers no longer need to
  make a decision based on cluster-wide vs. namespace-wide access.
  Components either have access, or they error out.
- Modify the web dashboard to always assume ServiceProfiles are enabled.

Reverts #1721
Part of #2337

Signed-off-by: Andrew Seigner <siggy@buoyant.io>
siggy added a commit that referenced this pull request Mar 12, 2019
#1721 introduced a `--single-namespace` install flag,
enabling the control-plane to function within a single namespace. With
the introduction of ServiceProfiles, and upcoming identity changes, this
single namespace mode of operation is becoming less viable.

This change removes the `--single-namespace` install flag, and all
underlying support. The control-plane must have cluster-wide access to
operate.

A few related changes:
- Remove `--single-namespace` from `linkerd check`, this motivates
  combining some check categories, as we can always assume cluster-wide
  requirements.
- Simplify the `k8s.ResourceAuthz` API, as callers no longer need to
  make a decision based on cluster-wide vs. namespace-wide access.
  Components either have access, or they error out.
- Modify the web dashboard to always assume ServiceProfiles are enabled.

Reverts #1721
Part of #2337

Signed-off-by: Andrew Seigner <siggy@buoyant.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants