Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow bootstrap configuration to be configured and reentrant #16571

Merged

Conversation

smarterclayton
Copy link
Contributor

@smarterclayton smarterclayton commented Sep 26, 2017

Make bootstrapping a real production node possible.

  1. Simplify and streamline the process whereby the bootstrap config is looked up.
    1. --bootstrap-config-name can be used to customize which config is looked up (one per node group)
    2. Any failure in fetching node config results in termination of the pass - no client side defaulting
    3. Handle 0.0.0.0 in dnsIP as a special case which results in the local node IP lookup
  2. Backport the one remaining cert rotation patch and make client and server node side cert rotation mandatory when bootstrapping
    1. Fix a number of small issues upstream where node bootstrapping is not reentrant
    2. pass cert-dir to the kubelet instead of passing individual key and cert, which was preventing rotation from working
  3. Make openshift start network work podified
    1. Allow kubeconfig to be specified on the CLI and override the node-config
    2. The proxy healthz was not starting due to missing config - this has been corrected
    3. Provide a daemonset example that correctly starts OpenShift SDN in a pod
    4. Fix a few minor bugs in openshift-sdn

There is still one bug outstanding upstream that can be fixed separately - the kubelet client rotation can fail due to the cert expiring and be unable to get new certs, so it never exits.

Tested the following scenario extensively (requires a new openshift/node image tagged as v3.7.0-alpha.1):

  1. Update master-config with cert signing on on 10m rotation
  2. create a node-config oc create configmap -n openshift-node node-config --from-file=node-config.yaml=contrib/kubernetes/default-node-config.yaml
  3. start node in bootstrapping mode openshift start node --bootstrap-config-name=node-config --config=/etc/origin/node/node-config.yaml --enable=kubelet --loglevel=3 (which has it run only the kubelet)
  4. run a background oc observe csr -- oc adm certificate approve to approve both csr
  5. run oc create -f contrib/kubernetes/static/network-policy.yaml
  6. run oc create -f contrib/kubernetes/static/network-daemonset.yaml
  7. verify the daemonset starts correctly and that it passes health checks
  8. launch a new pod and verify it has dns oc run --restart=Never --attach -it --image=centos:7 -- /bin/bash and then yum install bind-utils -y && dig +search kubernetes.default.svc
  9. launch two pods in two namespaces and verify multi tenant SDN works

Follow up for the daemonset - openshift-sdn expects to have access to the dockershim.sock which this doesn't bind mount in.

@openshift-merge-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: smarterclayton

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@openshift-merge-robot openshift-merge-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Sep 26, 2017
@openshift-ci-robot openshift-ci-robot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Sep 26, 2017
@smarterclayton
Copy link
Contributor Author

@deads2k bootstrapping

@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 27, 2017
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 27, 2017
// BootstrapConfigName is the name of a config map to read node-config.yaml from.
BootstrapConfigName string
// BootstrapConfigNamespace is the namespace the config map for bootstrap config is expected to load from.
BootstrapConfigNamespace string
Copy link
Contributor

Choose a reason for hiding this comment

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

Why another configurable namespace? Sounds like another thing we will end up removing in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is configurable from the node, not from the master. Nodes are allowed to point to different namespaces.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is configurable from the node, not from the master. Nodes are allowed to point to different namespaces.

I think the "why" question still stands? Why not be prescriptive, at least until someone has a compelling subdivision use-case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we don't as a general rule lock clients into namespaces. I don't see a trend away from clients owning their destiny, and I don't believe in being overly restrictive here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we don't as a general rule lock clients into namespaces. I don't see a trend away from clients owning their destiny, and I don't believe in being overly restrictive here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Because we don't as a general rule lock clients into namespaces. I don't see a trend away from clients owning their destiny, and I don't believe in being overly restrictive here.

summarizing in-person discussion: not configurable in the server, configurable in the client if someone really wants to wire it.

@@ -120,6 +122,7 @@ const (
NodeProxierRoleBindingName = NodeProxierRoleName + "s"
NodeAdminRoleBindingName = NodeAdminRoleName + "s"
NodeReaderRoleBindingName = NodeReaderRoleName + "s"
NodeConfigReaderRoleBindingName = NodeConfigReaderRoleName + "s"
Copy link
Contributor

Choose a reason for hiding this comment

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

No more + "s" please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why is it still there for the others?

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it still there for the others?

Legacy. We stopped doing it upstream.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Where's the comment in the code saying that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We should probably separate into section with header and footer to that effect


func GetBootstrapNodeConfigProvisioningRoleBindings(namespace string) []rbac.RoleBinding {
return []rbac.RoleBinding{
newOriginRoleBindingForClusterRole(NodeConfigReaderRoleBindingName, NodeConfigReaderRoleName, namespace).
Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing new should use newOriginRoleBindingForClusterRole

@@ -1083,3 +1092,11 @@ var rolesToShow = sets.NewString(
"system:image-pusher",
"view",
)

func GetBootstrapNodeConfigProvisioningRoleBindings(namespace string) []rbac.RoleBinding {
Copy link
Contributor

Choose a reason for hiding this comment

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

No namespace configuration please.

@openshift-merge-robot openshift-merge-robot removed the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Sep 28, 2017
@@ -1099,3 +1108,11 @@ func GetBootstrapNamespaceRoleBindings() map[string][]rbac.RoleBinding {
}
return ret
}

func GetBootstrapNodeConfigProvisioningRoleBindings(namespace string) []rbac.RoleBinding {
Copy link
Contributor

Choose a reason for hiding this comment

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

This namespace should be pinnned. @enj did you pull merge yet?

@@ -666,6 +666,15 @@ func GetOpenshiftBootstrapClusterRoles() []rbac.ClusterRole {
},
{
ObjectMeta: metav1.ObjectMeta{
Name: NodeConfigReaderRoleName,
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like it should be namespace scoped. #16517 made it easily possible

Copy link
Contributor

Choose a reason for hiding this comment

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

Lets make this a role since we remove the namespace configuration.

@@ -251,6 +251,7 @@ func (c *MasterConfig) Run(kubeAPIServerConfig *kubeapiserver.Config, controller
}

// add post-start hooks
aggregatedAPIServer.GenericAPIServer.AddPostStartHookOrDie("node.openshift.io-sharednamespace", c.ensureOpenShiftNodeNamespace)
Copy link
Contributor

Choose a reason for hiding this comment

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

after #16517 this won't be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because we auto init namespaces in the role bindings?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switched to the shared mode.

Why is shared resource namespaces still its own hook?

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is shared resource namespaces still its own hook?

cruft

@@ -212,12 +212,20 @@ func (o NodeOptions) loadBootstrap(hostnames []string, nodeConfigDir string) err

// try to refresh the latest node-config.yaml
o.ConfigFile = filepath.Join(nodeConfigDir, "node-config.yaml")
config, err := c.Core().ConfigMaps("kube-system").Get("node-config", metav1.GetOptions{})
config, err := c.Core().ConfigMaps(o.NodeArgs.BootstrapConfigNamespace).Get(o.NodeArgs.BootstrapConfigName, metav1.GetOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

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

doesn't seem like the namespace should be configurable. Name yes, but I don't see why we'd let people change the namespace.

@@ -212,12 +212,20 @@ func (o NodeOptions) loadBootstrap(hostnames []string, nodeConfigDir string) err

// try to refresh the latest node-config.yaml
o.ConfigFile = filepath.Join(nodeConfigDir, "node-config.yaml")
config, err := c.Core().ConfigMaps("kube-system").Get("node-config", metav1.GetOptions{})
config, err := c.Core().ConfigMaps(o.NodeArgs.BootstrapConfigNamespace).Get(o.NodeArgs.BootstrapConfigName, metav1.GetOptions{})
Copy link
Contributor

Choose a reason for hiding this comment

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

you're re-using this error down below. Please give it a special name.

Copy link
Contributor

Choose a reason for hiding this comment

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

you're re-using this error down below. Please give it a special name.

error name still outstanding

}

return err
// if there is no node-config.yaml and no server config map, generate one
glog.V(2).Infof("Generating a local configuration since no server config available")
Copy link
Contributor

Choose a reason for hiding this comment

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

"since no server or local config available"

@@ -100,7 +100,7 @@ func newKubeControllerManager(kubeconfigFile, saPrivateKeyFile, saRootCAFile, po
cmdLineArgs["cluster-signing-key-file"] = []string{""}
}
if _, ok := cmdLineArgs["experimental-cluster-signing-duration"]; !ok {
cmdLineArgs["experimental-cluster-signing-duration"] = []string{"0s"}
cmdLineArgs["experimental-cluster-signing-duration"] = []string{"720h"}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer much shorter to flush out refreshing errors. o(12 hours)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't work in 3.7 for client certs so we're going to do it on the node level or backport the required changes. Also, the goal is to get to bootstrapping first, then tighten rotation, so I don't want to block on that.

Copy link
Contributor

Choose a reason for hiding this comment

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

It doesn't work in 3.7 for client certs so we're going to do it on the node level or backport the required changes. Also, the goal is to get to bootstrapping first, then tighten rotation, so I don't want to block on that.

This looks like a time bomb. Are you saying you're going to be manually refereshing certificates or something?

Copy link
Contributor

Choose a reason for hiding this comment

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

Uh so what happens after a month?

return err
}
return nil
switch {
Copy link
Contributor

Choose a reason for hiding this comment

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

if you are suppose to bootstrap and you can't, I think you should fail, not use a local config instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair. Although should probably be based on whether you asked for a config or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm torn on this. An empty config map should result in a default. A missing config map or typod map seems like an error. But at the same time a 403 or other error (especially for the default config) seems borderline arbitrary. A node having a config you didn't expect is bad, a node being down because of a correlated auth mixup also seems bad.

@simo5
Copy link
Contributor

simo5 commented Sep 28, 2017

This PR does a lot more than "correcting small issues", for example it permits to configure stuff that some people believe should be hard coded (eg namespaces).

Can you please give a better explanation of what is the aim of this PR @smarterclayton ?

@tiran
Copy link
Contributor

tiran commented Sep 28, 2017

@smarterclayton Could you please explain why you are proposing --bootstrap-config-name and --bootstrap-config-namespace? The PR message doesn't contain an explanation. @openshift/sig-security doesn't understand why the new options are necessary and which value are added by them.

@enj is also not keen on configurable config namespace.

@smarterclayton
Copy link
Contributor Author

smarterclayton commented Sep 28, 2017 via email

@smarterclayton
Copy link
Contributor Author

Seeing multiple rebinds using the new way for namespace mappings: #16611

@smarterclayton
Copy link
Contributor Author

smarterclayton commented Sep 29, 2017 via email

@openshift-ci-robot openshift-ci-robot removed the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Sep 29, 2017
@openshift-merge-robot openshift-merge-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 9, 2017
@openshift-merge-robot openshift-merge-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Oct 9, 2017
- RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true
masterClientConnectionOverrides:
acceptContentTypes: application/vnd.kubernetes.protobuf,application/json
burst: 40
Copy link
Contributor

Choose a reason for hiding this comment

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

qps looks ok, but burst looks small

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually our default.

hostPID: true
containers:
- name: network
image: openshift/node:v3.7.0-alpha.1
Copy link
Contributor

Choose a reason for hiding this comment

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

not a parameterized version/tag?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just an example, wanted to have the larger discussion about how static config flows from openshift/origin -> ansible first. Wanted to have something checked in that should be reproducible for a bit, then will be made formal and moved out of here.

// Start up a metrics server if requested
if len(c.ProxyConfig.MetricsBindAddress) > 0 {
mux := http.NewServeMux()
mux.HandleFunc("/proxyMode", func(w http.ResponseWriter, r *http.Request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

name it alpha or something? this is like config-lite.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is consistent with upstream, so we can't rename it.


MasterCertDir string
ConfigDir flag.StringFlag

AllowDisabledDocker bool
// VolumeDir is the volume storage directory.
VolumeDir string
// VolumeDirProvided is set to true if the user has specified this flag.
VolumeDirProvided bool
Copy link
Contributor

Choose a reason for hiding this comment

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

blech

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hold your nose a month or so longer and most of this code dies and goes away and we're all happy.

@deads2k
Copy link
Contributor

deads2k commented Oct 11, 2017

I still think you should remove --bootstrap. I don't think it's adding value. Making them choose a particular config seems like the right thing to do.

lgtm otherwise.

@smarterclayton
Copy link
Contributor Author

/test all

Updated to remove the --bootstrap flag.

When we mount /var/run/openshift-sdn into the container, we need to be
able to clear its contents but the directory itself cannot be removed as
it is a mount point. Also clarify one error.
This makes running in a separate process for networks able to have a
health check and for metrics to be reported.
CFSSL throws an opaque error, and bootstrapping requires user
intervention to configure anyway.
This allows the kubelet to be configured to load default configuration
out of a known namespace. By default it is openshift-node/node-config.

Correct an error in bootstrapping where errors weren't logged, and
properly ignore forbidden errors when trying to load the config map.

Add a better description of bootstrapping to openshift start node.

Ensure the volume directory is correctly loaded from node-config during
bootstrapping instead of being overwritten into the config directory.

Enable client and server rotation on the node automatically when
bootstrapping, and only do a client certificate creation (server
bootstrapping done by kubelet only). This unfortunately requires setting
a fake value in the node config that will be cleared later - as we are
moving towards a future where node-config does not exist this entire
section will likely go away.

Relax validation on node-config to allow cert-dir to be provided instead
of explicit certificates.

bootstrap
Other config variants will be stored in this location. The new namespace
ensures clean security isolation.
Need to be able to take node-config from bootstrap node. For
openshift start network the --kubeconfig flag from the CLI overrides the
value of masterKubeConfig in the provided node config. If the value is
empty (like it is by default) the in-cluster-config is used.

Reorganize the node startup slightly so there is even less overlap
between kubelet and network. A future change will completely separate
these two initialization paths.
@openshift-ci-robot
Copy link

openshift-ci-robot commented Oct 12, 2017

@smarterclayton: The following tests failed, say /retest to rerun them all:

Test name Commit Details Rerun command
ci/openshift-jenkins/origin/verify 4e4e5ce link /test origin-verify
ci/openshift-jenkins/origin/unit 4e4e5ce link /test origin-ut
ci/openshift-jenkins/experimental/integration d6a7486 link /test origin-it

Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.

@smarterclayton
Copy link
Contributor Author

/retest

@smarterclayton smarterclayton added the lgtm Indicates that a PR is ready to be merged. label Oct 12, 2017
@smarterclayton
Copy link
Contributor Author

Applying label

@openshift-merge-robot
Copy link
Contributor

Automatic merge from submit-queue.

@openshift-merge-robot openshift-merge-robot merged commit 259bd33 into openshift:master Oct 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.