Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions HACKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ To run the operator binary in the cluster from your local machine (as opposed to
$ make run-local
```

Set `ENABLE_CANARY=true` in your environment (or inline with the `run-local` command) to enable the ingress canary.


Note, to rescale the operator on the cluster after local testing is complete, scale the CVO back up with:

```
Expand Down
23 changes: 23 additions & 0 deletions assets/canary/daemonset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Hello Openshift Ingress Canary daemonset
# Specific values are set at runtime
kind: DaemonSet
apiVersion: apps/v1
# name and namespace are set at runtime.
spec:
progressDeadlineSeconds: 600
template:
spec:
containers:
- name: hello-openshift-canary
# Image is set at runtime
imagePullPolicy: IfNotPresent
terminationMessagePolicy: FallbackToLogsOnError
ports:
- containerPort: 8080
protocol: TCP
- containerPort: 8888
protocol: TCP
resources:
requests:
cpu: 10m
memory: 20Mi
4 changes: 4 additions & 0 deletions assets/canary/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
kind: Namespace
apiVersion: v1
metadata:
name: openshift-ingress-canary
16 changes: 16 additions & 0 deletions assets/canary/route.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Hello Openshift Ingress Canary route
# Specific values are applied at runtime
kind: Route
apiVersion: route.openshift.io/v1
# name and namespace are set at runtime.
metadata:
annotations:
haproxy.router.openshift.io/balance: "roundrobin"
spec:
port:
targetPort: 8080-tcp
to:
kind: Service
# Service name set at run time
weight: 100
wildcardPolicy: None
16 changes: 16 additions & 0 deletions assets/canary/service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Hello Openshift Ingress Canary service
# Specific values are applied at runtime
kind: Service
apiVersion: v1
# name and namespace are set at runtime.
spec:
type: ClusterIP
ports:
- name: 8080-tcp
port: 8080
protocol: TCP
targetPort: 8080
- name: 8888-tcp
port: 8888
protocol: TCP
targetPort: 8888
5 changes: 5 additions & 0 deletions cmd/ingress-operator/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type StartOptions struct {
// IngressControllerImage is the pullspec of the ingress controller image to
// be managed.
IngressControllerImage string
// CanaryImage is the pullspec of the canary tester server image to
// be managed.
CanaryImage string
// ReleaseVersion is the cluster version which the operator will converge to.
ReleaseVersion string
}
Expand All @@ -58,6 +61,7 @@ func NewStartCommand() *cobra.Command {

cmd.Flags().StringVarP(&options.OperatorNamespace, "namespace", "n", manifests.DefaultOperatorNamespace, "namespace the operator is deployed to (required)")
cmd.Flags().StringVarP(&options.IngressControllerImage, "image", "i", "", "image of the ingress controller the operator will manage (required)")
cmd.Flags().StringVarP(&options.CanaryImage, "canary-image", "c", "", "image of the canary container that the operator will manage (optional)")
cmd.Flags().StringVarP(&options.ReleaseVersion, "release-version", "", statuscontroller.UnknownVersionValue, "the release version the operator should converge to (required)")
cmd.Flags().StringVarP(&options.MetricsListenAddr, "metrics-listen-addr", "", ":60000", "metrics endpoint listen address (required)")
cmd.Flags().StringVarP(&options.ShutdownFile, "shutdown-file", "s", defaultTrustedCABundle, "if provided, shut down the operator when this file changes")
Expand Down Expand Up @@ -90,6 +94,7 @@ func start(opts *StartOptions) error {
OperatorReleaseVersion: opts.ReleaseVersion,
Namespace: opts.OperatorNamespace,
IngressControllerImage: opts.IngressControllerImage,
CanaryImage: opts.CanaryImage,
}

// Set up the channels for the watcher and operator.
Expand Down
7 changes: 6 additions & 1 deletion hack/run-local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ echo "Image: ${IMAGE}"
echo "Release version: ${RELEASE_VERSION}"
echo "Namespace: ${NAMESPACE}"

${DELVE:-} ./ingress-operator start --image "${IMAGE}" --release-version "${RELEASE_VERSION}" \
if [[ ! -z ${ENABLE_CANARY:-} ]]; then
CANARY_IMAGE=$(oc get -n openshift-ingress-operator deployments/ingress-operator -o json | jq -r '.spec.template.spec.containers[0].env[] | select(.name=="CANARY_IMAGE").value')
echo "Canary Image: ${CANARY_IMAGE}"
fi

${DELVE:-} ./ingress-operator start --image "${IMAGE}" --canary-image=${CANARY_IMAGE:-} --release-version "${RELEASE_VERSION}" \
--namespace "${NAMESPACE}" --shutdown-file "${SHUTDOWN_FILE}" "$@"
1 change: 1 addition & 0 deletions hack/uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ else
fi

oc delete namespaces/openshift-ingress
oc delete namespaces/openshift-ingress-canary

if [ "$WHAT" == "all" ]; then
oc delete clusterroles/openshift-ingress-operator
Expand Down
5 changes: 3 additions & 2 deletions manifests/00-cluster-role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ rules:
- apps
resources:
- deployments
- daemonsets
verbs:
- "*"

Expand Down Expand Up @@ -136,13 +137,13 @@ rules:
- create

# Mirrored from assets/router/cluster-role.yaml
# and expanded for the canary-controller.
- apiGroups:
- route.openshift.io
resources:
- routes
verbs:
- list
- watch
- '*'
Copy link
Contributor

Choose a reason for hiding this comment

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

We could define a new role and rolebinding in the openshift-ingress-canary namespace to avoid granting such broad permissions.

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 might be missing something, but how would the operator be able to take advantage of a new role + role binding in the openshift-ingress-canary namespace? The canary controller (which resides within the ingress operator) needs to be able to perform all (well, most at least, so maybe the "*" isn't necessary) actions on routes.

Copy link
Contributor

@Miciah Miciah Oct 29, 2020

Choose a reason for hiding this comment

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

The role would be in the openshift-ingress-canary namespace and allow full control over routes, and the role binding would bind that role to the operator's service account. Because it would be a role (not a clusterrole) in the openshift-ingress-canary namespace, the role binding should only grant access to routes within that namespace, if I understand correctly. Something like the following should work (but I have not verified that it does):

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ingress-operator-canary-controller
  namespace: openshift-ingress-canary
  annotations:
    include.release.openshift.io/self-managed-high-availability: "true"
rules:
- apiGroups:
  - route.openshift.io
  resources:
  - routes
  verbs:
  - *
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ingress-operator-canary-controller
  namespace: openshift-ingress-canary
  annotations:
    include.release.openshift.io/self-managed-high-availability: "true"
subjects:
- kind: ServiceAccount
  name: ingress-operator
  namespace: openshift-ingress-operator
roleRef:
  kind: Role
  apiGroup: rbac.authorization.k8s.io
  name: ingress-operator-canary-controller

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ill include these resources in the manifests in my next push and verify that they work. Thanks!

Copy link
Contributor Author

@sgreene570 sgreene570 Oct 29, 2020

Choose a reason for hiding this comment

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

@Miciah the CVO does not (and in my opinion, probably should not) create the openshift-ingress-canary namespace, so the CVO cannot create a Role in the openshift-ingress-canary namespace. Additionally, the operator would need cluster-wide route RBAC permissions to be able to create a role in the openshift-ingress-canary namespace for routes.

So I see 2 solutions:

  1. The CVO creates the openshift-ingress-canary namespace, and then creates the ingress canary controller role.
    (This seems like an awkward thing to do especially if the canary controller isnt a mandatory component of the operator)
  2. We fall back to just giving the operator '*' permissions on routes in it's cluster-role.

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note, ive reverted this PR to use * permissions for routes in the operator's cluster role

Copy link
Contributor Author

@sgreene570 sgreene570 Nov 9, 2020

Choose a reason for hiding this comment

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

~ @Miciah https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping is the solution we are looking for! I amended this PR so that the ingress operator is granted bind and escalate permissions on roles and rolebindings in it's cluster role. I also added the role.yaml and role-binding.yaml assets for the ingress canary. With these new changes, the ingress operator's route permissions are extended to * for the openshift-ingress-canary namespace only. PTAL when you can. ~

Copy link
Contributor Author

@sgreene570 sgreene570 Nov 12, 2020

Choose a reason for hiding this comment

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

Going with route '*' permissions after a discussion with miciah. While these permissions are broad, this is the "least bad" approach. The above mentioned escalate/bind approach introduces far more concerning privilege escalation issues to the operator (despite work around efforts to mitigate this).


# Mirrored from assets/router/cluster-role.yaml
- apiGroups:
Expand Down
4 changes: 4 additions & 0 deletions manifests/02-deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ spec:
- "$(WATCH_NAMESPACE)"
- --image
- "$(IMAGE)"
- --canary-image
- "$(CANARY_IMAGE)"
- --release-version
- "$(RELEASE_VERSION)"
env:
Expand All @@ -58,6 +60,8 @@ spec:
fieldPath: metadata.namespace
- name: IMAGE
value: openshift/origin-haproxy-router:v4.0
- name: CANARY_IMAGE
value: openshift/hello-openshift:latest
resources:
requests:
cpu: 10m
Expand Down
Loading