Commit 6971fd26 authored by Hordur Freyr Yngvason's avatar Hordur Freyr Yngvason Committed by Achilleas Pipinellis

Give Knative serving permissions to service account

GitLab uses a kubernetes service account to perform deployments. For
serverless deployments to work as expected with externally created
clusters with their own knative installations (e.g. via Cloud Run), this
account requires additional permissions in the serving.knative.dev API
group.
parent cc3ef635
...@@ -9,6 +9,8 @@ module Clusters ...@@ -9,6 +9,8 @@ module Clusters
GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin' GITLAB_CLUSTER_ROLE_BINDING_NAME = 'gitlab-admin'
GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin' GITLAB_CLUSTER_ROLE_NAME = 'cluster-admin'
PROJECT_CLUSTER_ROLE_NAME = 'edit' PROJECT_CLUSTER_ROLE_NAME = 'edit'
GITLAB_KNATIVE_SERVING_ROLE_NAME = 'gitlab-knative-serving-role'
GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME = 'gitlab-knative-serving-rolebinding'
end end
end end
end end
...@@ -41,7 +41,15 @@ module Clusters ...@@ -41,7 +41,15 @@ module Clusters
kubeclient.create_or_update_service_account(service_account_resource) kubeclient.create_or_update_service_account(service_account_resource)
kubeclient.create_or_update_secret(service_account_token_resource) kubeclient.create_or_update_secret(service_account_token_resource)
create_role_or_cluster_role_binding if rbac
return unless rbac
create_role_or_cluster_role_binding
return unless namespace_creator
create_or_update_knative_serving_role
create_or_update_knative_serving_role_binding
end end
private private
...@@ -63,6 +71,14 @@ module Clusters ...@@ -63,6 +71,14 @@ module Clusters
end end
end end
def create_or_update_knative_serving_role
kubeclient.update_role(knative_serving_role_resource)
end
def create_or_update_knative_serving_role_binding
kubeclient.update_role_binding(knative_serving_role_binding_resource)
end
def service_account_resource def service_account_resource
Gitlab::Kubernetes::ServiceAccount.new( Gitlab::Kubernetes::ServiceAccount.new(
service_account_name, service_account_name,
...@@ -92,6 +108,29 @@ module Clusters ...@@ -92,6 +108,29 @@ module Clusters
Gitlab::Kubernetes::RoleBinding.new( Gitlab::Kubernetes::RoleBinding.new(
name: role_binding_name, name: role_binding_name,
role_name: Clusters::Gcp::Kubernetes::PROJECT_CLUSTER_ROLE_NAME, role_name: Clusters::Gcp::Kubernetes::PROJECT_CLUSTER_ROLE_NAME,
role_kind: :ClusterRole,
namespace: service_account_namespace,
service_account_name: service_account_name
).generate
end
def knative_serving_role_resource
Gitlab::Kubernetes::Role.new(
name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
namespace: service_account_namespace,
rules: [{
apiGroups: %w(serving.knative.dev),
resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services),
verbs: %w(get list create update delete patch watch)
}]
).generate
end
def knative_serving_role_binding_resource
Gitlab::Kubernetes::RoleBinding.new(
name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME,
role_name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
role_kind: :Role,
namespace: service_account_namespace, namespace: service_account_namespace,
service_account_name: service_account_name service_account_name: service_account_name
).generate ).generate
......
---
title: Create Knative role and binding with service account
merge_request: 30235
author:
type: changed
...@@ -102,12 +102,15 @@ You must do the following: ...@@ -102,12 +102,15 @@ You must do the following:
1. Ensure GitLab can manage Knative: 1. Ensure GitLab can manage Knative:
- For a non-GitLab managed cluster, ensure that the service account for the token - For a non-GitLab managed cluster, ensure that the service account for the token
provided can manage resources in the `serving.knative.dev` API group. provided can manage resources in the `serving.knative.dev` API group.
- For a GitLab managed cluster, - For a GitLab managed cluster, if you added the cluster in [GitLab 12.1 or later](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/30235),
GitLab uses a service account with the `edit` cluster role. This account needs then GitLab will already have the required access and you can proceed to the next step.
the ability to manage resources in the `serving.knative.dev` API group.
We suggest you do this with an [aggregated ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles) Otherwise, you need to manually grant GitLab's service account the ability to manage
adding rules to the default `edit` cluster role: resources in the `serving.knative.dev` API group. Since every GitLab service account
First, save the following YAML as `knative-serving-only-role.yaml`: has the `edit` cluster role, the simplest way to do this is with an
[aggregated ClusterRole](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#aggregated-clusterroles)
adding rules to the default `edit` cluster role: First, save the following YAML as
`knative-serving-only-role.yaml`:
```yaml ```yaml
apiVersion: rbac.authorization.k8s.io/v1 apiVersion: rbac.authorization.k8s.io/v1
...@@ -143,6 +146,9 @@ You must do the following: ...@@ -143,6 +146,9 @@ You must do the following:
kubectl apply -f knative-serving-only-role.yaml kubectl apply -f knative-serving-only-role.yaml
``` ```
If you would rather grant permissions on a per service account basis, you can do this
using a `Role` and `RoleBinding` specific to the service account and namespace.
1. Follow the steps to deploy [functions](#deploying-functions) 1. Follow the steps to deploy [functions](#deploying-functions)
or [serverless applications](#deploying-serverless-applications) onto your or [serverless applications](#deploying-serverless-applications) onto your
cluster. cluster.
......
...@@ -57,6 +57,13 @@ module Gitlab ...@@ -57,6 +57,13 @@ module Gitlab
:update_cluster_role_binding, :update_cluster_role_binding,
to: :rbac_client to: :rbac_client
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
# group client
delegate :create_role,
:get_role,
:update_role,
to: :rbac_client
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api # RBAC methods delegates to the apis/rbac.authorization.k8s.io api
# group client # group client
delegate :create_role_binding, delegate :create_role_binding,
......
# frozen_string_literal: true
module Gitlab
module Kubernetes
class Role
def initialize(name:, namespace:, rules:)
@name = name
@namespace = namespace
@rules = rules
end
def generate
::Kubeclient::Resource.new(
metadata: { name: name, namespace: namespace },
rules: rules
)
end
private
attr_reader :name, :namespace, :rules
end
end
end
...@@ -3,9 +3,10 @@ ...@@ -3,9 +3,10 @@
module Gitlab module Gitlab
module Kubernetes module Kubernetes
class RoleBinding class RoleBinding
def initialize(name:, role_name:, namespace:, service_account_name:) def initialize(name:, role_name:, role_kind:, namespace:, service_account_name:)
@name = name @name = name
@role_name = role_name @role_name = role_name
@role_kind = role_kind
@namespace = namespace @namespace = namespace
@service_account_name = service_account_name @service_account_name = service_account_name
end end
...@@ -20,7 +21,7 @@ module Gitlab ...@@ -20,7 +21,7 @@ module Gitlab
private private
attr_reader :name, :role_name, :namespace, :service_account_name attr_reader :name, :role_name, :role_kind, :namespace, :service_account_name
def metadata def metadata
{ name: name, namespace: namespace } { name: name, namespace: namespace }
...@@ -29,7 +30,7 @@ module Gitlab ...@@ -29,7 +30,7 @@ module Gitlab
def role_ref def role_ref
{ {
apiGroup: 'rbac.authorization.k8s.io', apiGroup: 'rbac.authorization.k8s.io',
kind: 'ClusterRole', kind: role_kind,
name: role_name name: role_name
} }
end end
......
...@@ -176,6 +176,9 @@ describe Gitlab::Kubernetes::KubeClient do ...@@ -176,6 +176,9 @@ describe Gitlab::Kubernetes::KubeClient do
let(:rbac_client) { client.rbac_client } let(:rbac_client) { client.rbac_client }
[ [
:create_role,
:get_role,
:update_role,
:create_cluster_role_binding, :create_cluster_role_binding,
:get_cluster_role_binding, :get_cluster_role_binding,
:update_cluster_role_binding :update_cluster_role_binding
......
...@@ -4,6 +4,7 @@ require 'spec_helper' ...@@ -4,6 +4,7 @@ require 'spec_helper'
describe Gitlab::Kubernetes::RoleBinding, '#generate' do describe Gitlab::Kubernetes::RoleBinding, '#generate' do
let(:role_name) { 'edit' } let(:role_name) { 'edit' }
let(:role_kind) { 'ClusterRole' }
let(:namespace) { 'my-namespace' } let(:namespace) { 'my-namespace' }
let(:service_account_name) { 'my-service-account' } let(:service_account_name) { 'my-service-account' }
...@@ -20,7 +21,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do ...@@ -20,7 +21,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do
let(:role_ref) do let(:role_ref) do
{ {
apiGroup: 'rbac.authorization.k8s.io', apiGroup: 'rbac.authorization.k8s.io',
kind: 'ClusterRole', kind: role_kind,
name: role_name name: role_name
} }
end end
...@@ -37,6 +38,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do ...@@ -37,6 +38,7 @@ describe Gitlab::Kubernetes::RoleBinding, '#generate' do
described_class.new( described_class.new(
name: "gitlab-#{namespace}", name: "gitlab-#{namespace}",
role_name: role_name, role_name: role_name,
role_kind: role_kind,
namespace: namespace, namespace: namespace,
service_account_name: service_account_name service_account_name: service_account_name
).generate ).generate
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Kubernetes::Role do
let(:role) { described_class.new(name: name, namespace: namespace, rules: rules) }
let(:name) { 'example-name' }
let(:namespace) { 'example-namespace' }
let(:rules) do
[{
apiGroups: %w(hello.world),
resources: %w(oil diamonds coffee),
verbs: %w(say do walk run)
}]
end
describe '#generate' do
subject { role.generate }
let(:resource) do
::Kubeclient::Resource.new(
metadata: { name: name, namespace: namespace },
rules: rules
)
end
it { is_expected.to eq(resource) }
end
end
...@@ -34,6 +34,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' d ...@@ -34,6 +34,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService, '#execute' d
stub_kubeclient_create_service_account(api_url, namespace: namespace) stub_kubeclient_create_service_account(api_url, namespace: namespace)
stub_kubeclient_create_secret(api_url, namespace: namespace) stub_kubeclient_create_secret(api_url, namespace: namespace)
stub_kubeclient_put_secret(api_url, "#{namespace}-token", namespace: namespace) stub_kubeclient_put_secret(api_url, "#{namespace}-token", namespace: namespace)
stub_kubeclient_put_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, namespace: namespace)
stub_kubeclient_put_role_binding(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
stub_kubeclient_get_secret( stub_kubeclient_get_secret(
api_url, api_url,
......
...@@ -143,6 +143,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do ...@@ -143,6 +143,8 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do
stub_kubeclient_get_role_binding_error(api_url, role_binding_name, namespace: namespace) stub_kubeclient_get_role_binding_error(api_url, role_binding_name, namespace: namespace)
stub_kubeclient_create_role_binding(api_url, namespace: namespace) stub_kubeclient_create_role_binding(api_url, namespace: namespace)
stub_kubeclient_put_role(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME, namespace: namespace)
stub_kubeclient_put_role_binding(api_url, Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_BINDING_NAME, namespace: namespace)
end end
it_behaves_like 'creates service account and token' it_behaves_like 'creates service account and token'
...@@ -169,6 +171,24 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do ...@@ -169,6 +171,24 @@ describe Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService do
) )
) )
end end
it 'creates a role and role binding granting knative serving permissions to the service account' do
subject
expect(WebMock).to have_requested(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME}").with(
body: hash_including(
metadata: {
name: Clusters::Gcp::Kubernetes::GITLAB_KNATIVE_SERVING_ROLE_NAME,
namespace: namespace
},
rules: [{
apiGroups: %w(serving.knative.dev),
resources: %w(configurations configurationgenerations routes revisions revisionuids autoscalers services),
verbs: %w(get list create update delete patch watch)
}]
)
)
end
end end
end end
end end
...@@ -199,6 +199,11 @@ module KubernetesHelpers ...@@ -199,6 +199,11 @@ module KubernetesHelpers
.to_return(kube_response({})) .to_return(kube_response({}))
end end
def stub_kubeclient_put_role(api_url, name, namespace: 'default')
WebMock.stub_request(:put, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/roles/#{name}")
.to_return(kube_response({}))
end
def kube_v1_secret_body(**options) def kube_v1_secret_body(**options)
{ {
"kind" => "SecretList", "kind" => "SecretList",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment