Skip to content
Snippets Groups Projects
Commit 6f2ad2b6 authored by Thong Kuah's avatar Thong Kuah :speech_balloon: Committed by Kamil Trzciński
Browse files

Enable Kubernetes RBAC for GitLab Managed Apps for existing clusters

parent a2ea32dd
No related branches found
No related tags found
1 merge request!10495Merge Requests - Assignee
Showing
with 232 additions and 41 deletions
Loading
Loading
@@ -157,7 +157,8 @@ class Projects::ClustersController < Projects::ApplicationController
:namespace,
:api_url,
:token,
:ca_cert
:ca_cert,
:authorization_type
]).merge(
provider_type: :user,
platform_type: :kubernetes
Loading
Loading
Loading
Loading
@@ -11,4 +11,8 @@ module ClustersHelper
render 'projects/clusters/gcp_signup_offer_banner'
end
end
def rbac_clusters_feature_enabled?
Feature.enabled?(:rbac_clusters)
end
end
Loading
Loading
@@ -32,7 +32,8 @@ module Clusters
def install_command
Gitlab::Kubernetes::Helm::InitCommand.new(
name: name,
files: files
files: files,
rbac: cluster.platform_kubernetes_rbac?
)
end
 
Loading
Loading
Loading
Loading
@@ -39,6 +39,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
Loading
Loading
Loading
Loading
@@ -40,6 +40,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files,
repository: repository
Loading
Loading
Loading
Loading
@@ -48,6 +48,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files
)
Loading
Loading
@@ -71,7 +72,7 @@ module Clusters
private
 
def kube_client
cluster&.kubeclient
cluster&.kubeclient&.core_client
end
end
end
Loading
Loading
Loading
Loading
@@ -33,6 +33,7 @@ module Clusters
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files,
repository: repository
Loading
Loading
Loading
Loading
@@ -42,6 +42,7 @@ module Clusters
delegate :on_creation?, to: :provider, allow_nil: true
 
delegate :active?, to: :platform_kubernetes, prefix: true, allow_nil: true
delegate :rbac?, to: :platform_kubernetes, prefix: true, allow_nil: true
delegate :installed?, to: :application_helm, prefix: true, allow_nil: true
delegate :installed?, to: :application_ingress, prefix: true, allow_nil: true
 
Loading
Loading
Loading
Loading
@@ -5,6 +5,7 @@ module Clusters
class Kubernetes < ActiveRecord::Base
include Gitlab::Kubernetes
include ReactiveCaching
include EnumWithNil
 
self.table_name = 'cluster_platforms_kubernetes'
self.reactive_cache_key = ->(kubernetes) { [kubernetes.class.model_name.singular, kubernetes.id] }
Loading
Loading
@@ -47,6 +48,12 @@ module Clusters
 
alias_method :active?, :enabled?
 
enum_with_nil authorization_type: {
unknown_authorization: nil,
rbac: 1,
abac: 2
}
def actual_namespace
if namespace.present?
namespace
Loading
Loading
@@ -95,7 +102,7 @@ module Clusters
end
 
def kubeclient
@kubeclient ||= build_kubeclient!
@kubeclient ||= build_kube_client!(api_groups: ['api', 'apis/rbac.authorization.k8s.io'])
end
 
private
Loading
Loading
@@ -115,15 +122,16 @@ module Clusters
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
end
 
def build_kubeclient!(api_path: 'api', api_version: 'v1')
def build_kube_client!(api_groups: ['api'], api_version: 'v1')
raise "Incomplete settings" unless api_url && actual_namespace
 
unless (username && password) || token
raise "Either username/password or token is required to access API"
end
 
::Kubeclient::Client.new(
join_api_url(api_path),
Gitlab::Kubernetes::KubeClient.new(
api_url,
api_groups,
api_version,
auth_options: kubeclient_auth_options,
ssl_options: kubeclient_ssl_options,
Loading
Loading
@@ -133,7 +141,7 @@ module Clusters
 
# Returns a hash of all pods in the namespace
def read_pods
kubeclient = build_kubeclient!
kubeclient = build_kube_client!
 
kubeclient.get_pods(namespace: actual_namespace).as_json
rescue Kubeclient::HttpError => err
Loading
Loading
@@ -157,15 +165,6 @@ module Clusters
{ bearer_token: token }
end
 
def join_api_url(api_path)
url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
url.path = [prefix, api_path].join("/")
url.to_s
end
def terminal_auth
{
token: token,
Loading
Loading
Loading
Loading
@@ -96,10 +96,10 @@ class KubernetesService < DeploymentService
 
# Check we can connect to the Kubernetes API
def test(*args)
kubeclient = build_kubeclient!
kubeclient = build_kube_client!
 
kubeclient.discover
{ success: kubeclient.discovered, result: "Checked API discovery endpoint" }
kubeclient.core_client.discover
{ success: kubeclient.core_client.discovered, result: "Checked API discovery endpoint" }
rescue => err
{ success: false, result: err }
end
Loading
Loading
@@ -144,7 +144,7 @@ class KubernetesService < DeploymentService
end
 
def kubeclient
@kubeclient ||= build_kubeclient!
@kubeclient ||= build_kube_client!(api_groups: ['api', 'apis/rbac.authorization.k8s.io'])
end
 
def deprecated?
Loading
Loading
@@ -182,11 +182,12 @@ class KubernetesService < DeploymentService
slug.gsub(/[^-a-z0-9]/, '-').gsub(/^-+/, '')
end
 
def build_kubeclient!(api_path: 'api', api_version: 'v1')
def build_kube_client!(api_groups: ['api'], api_version: 'v1')
raise "Incomplete settings" unless api_url && actual_namespace && token
 
::Kubeclient::Client.new(
join_api_url(api_path),
Gitlab::Kubernetes::KubeClient.new(
api_url,
api_groups,
api_version,
auth_options: kubeclient_auth_options,
ssl_options: kubeclient_ssl_options,
Loading
Loading
@@ -196,7 +197,7 @@ class KubernetesService < DeploymentService
 
# Returns a hash of all pods in the namespace
def read_pods
kubeclient = build_kubeclient!
kubeclient = build_kube_client!
 
kubeclient.get_pods(namespace: actual_namespace).as_json
rescue Kubeclient::HttpError => err
Loading
Loading
@@ -220,15 +221,6 @@ class KubernetesService < DeploymentService
{ bearer_token: token }
end
 
def join_api_url(api_path)
url = URI.parse(api_url)
prefix = url.path.sub(%r{/+\z}, '')
url.path = [prefix, api_path].join("/")
url.to_s
end
def terminal_auth
{
token: token,
Loading
Loading
Loading
Loading
@@ -25,5 +25,14 @@
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
 
- if rbac_clusters_feature_enabled?
.form-group
.form-check
= platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input' }, 'rbac', 'abac'
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
.form-group
= field.submit s_('ClusterIntegration|Add Kubernetes cluster'), class: 'btn btn-success'
Loading
Loading
@@ -26,5 +26,14 @@
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-bold'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
 
- if rbac_clusters_feature_enabled?
.form-group
.form-check
= platform_kubernetes_field.check_box :authorization_type, { class: 'form-check-input', disabled: true }, 'rbac', 'abac'
= platform_kubernetes_field.label :authorization_type, s_('ClusterIntegration|RBAC-enabled cluster (experimental)'), class: 'form-check-label label-bold'
.form-text.text-muted
= s_('ClusterIntegration|Enable this setting if using role-based access control (RBAC).')
= s_('ClusterIntegration|This option will allow you to install applications on RBAC clusters.')
.form-group
= field.submit s_('ClusterIntegration|Save changes'), class: 'btn btn-success'
---
title: Support Kubernetes RBAC for GitLab Managed Apps when adding a existing cluster
merge_request: 21127
author:
type: changed
# frozen_string_literal: true
class AddAuthorizationTypeToClusterPlatformsKubernetes < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :cluster_platforms_kubernetes, :authorization_type, :integer, limit: 2
end
end
Loading
Loading
@@ -588,6 +588,7 @@ ActiveRecord::Schema.define(version: 20180826111825) do
t.string "encrypted_password_iv"
t.text "encrypted_token"
t.string "encrypted_token_iv"
t.integer "authorization_type", limit: 2
end
 
add_index "cluster_platforms_kubernetes", ["cluster_id"], name: "index_cluster_platforms_kubernetes_on_cluster_id", unique: true, using: :btree
Loading
Loading
# frozen_string_literal: true
module Gitlab
module Kubernetes
class ClusterRoleBinding
attr_reader :name, :cluster_role_name, :subjects
def initialize(name, cluster_role_name, subjects)
@name = name
@cluster_role_name = cluster_role_name
@subjects = subjects
end
def generate
::Kubeclient::Resource.new.tap do |resource|
resource.metadata = metadata
resource.roleRef = role_ref
resource.subjects = subjects
end
end
private
def metadata
{ name: name }
end
def role_ref
{
apiGroup: 'rbac.authorization.k8s.io',
kind: 'ClusterRole',
name: cluster_role_name
}
end
end
end
end
Loading
Loading
@@ -3,6 +3,9 @@ module Gitlab
module Helm
HELM_VERSION = '2.7.2'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
SERVICE_ACCOUNT = 'tiller'.freeze
CLUSTER_ROLE_BINDING = 'tiller-admin'.freeze
CLUSTER_ROLE = 'cluster-admin'.freeze
end
end
end
Loading
Loading
@@ -9,7 +9,11 @@ module Gitlab
 
def install(command)
namespace.ensure_exists!
create_service_account(command)
create_cluster_role_binding(command)
create_config_map(command)
kubeclient.create_pod(command.pod_resource)
end
 
Loading
Loading
@@ -41,6 +45,50 @@ module Gitlab
kubeclient.create_config_map(config_map_resource)
end
end
def create_service_account(command)
command.service_account_resource.tap do |service_account_resource|
break unless service_account_resource
if service_account_exists?(service_account_resource)
kubeclient.update_service_account(service_account_resource)
else
kubeclient.create_service_account(service_account_resource)
end
end
end
def create_cluster_role_binding(command)
command.cluster_role_binding_resource.tap do |cluster_role_binding_resource|
break unless cluster_role_binding_resource
if cluster_role_binding_exists?(cluster_role_binding_resource)
kubeclient.update_cluster_role_binding(cluster_role_binding_resource)
else
kubeclient.create_cluster_role_binding(cluster_role_binding_resource)
end
end
end
def service_account_exists?(resource)
resource_exists? do
kubeclient.get_service_account(resource.metadata.name, resource.metadata.namespace)
end
end
def cluster_role_binding_exists?(resource)
resource_exists? do
kubeclient.get_cluster_role_binding(resource.metadata.name)
end
end
def resource_exists?
yield
rescue ::Kubeclient::HttpError => e
raise e unless e.error_code == 404
false
end
end
end
end
Loading
Loading
Loading
Loading
@@ -3,7 +3,9 @@ module Gitlab
module Helm
module BaseCommand
def pod_resource
Gitlab::Kubernetes::Helm::Pod.new(self, namespace).generate
pod_service_account_name = rbac? ? service_account_name : nil
Gitlab::Kubernetes::Helm::Pod.new(self, namespace, service_account_name: pod_service_account_name).generate
end
 
def generate_script
Loading
Loading
@@ -26,6 +28,14 @@ module Gitlab
Gitlab::Kubernetes::ConfigMap.new(name, files).generate
end
 
def service_account_resource
nil
end
def cluster_role_binding_resource
nil
end
def file_names
files.keys
end
Loading
Loading
@@ -34,6 +44,10 @@ module Gitlab
raise "Not implemented"
end
 
def rbac?
raise "Not implemented"
end
def files
raise "Not implemented"
end
Loading
Loading
@@ -47,6 +61,10 @@ module Gitlab
def namespace
Gitlab::Kubernetes::Helm::NAMESPACE
end
def service_account_name
Gitlab::Kubernetes::Helm::SERVICE_ACCOUNT
end
end
end
end
Loading
Loading
Loading
Loading
@@ -6,9 +6,10 @@ module Gitlab
 
attr_reader :name, :files
 
def initialize(name:, files:)
def initialize(name:, files:, rbac:)
@name = name
@files = files
@rbac = rbac
end
 
def generate_script
Loading
Loading
@@ -17,15 +18,62 @@ module Gitlab
].join("\n")
end
 
def rbac?
@rbac
end
def service_account_resource
return unless rbac?
Gitlab::Kubernetes::ServiceAccount.new(service_account_name, namespace).generate
end
def cluster_role_binding_resource
return unless rbac?
subjects = [{ kind: 'ServiceAccount', name: service_account_name, namespace: namespace }]
Gitlab::Kubernetes::ClusterRoleBinding.new(
cluster_role_binding_name,
cluster_role_name,
subjects
).generate
end
private
 
def init_helm_command
tls_flags = "--tiller-tls" \
" --tiller-tls-verify --tls-ca-cert #{files_dir}/ca.pem" \
" --tiller-tls-cert #{files_dir}/cert.pem" \
" --tiller-tls-key #{files_dir}/key.pem"
command = %w[helm init] + init_command_flags
command.shelljoin + " >/dev/null\n"
end
def init_command_flags
tls_flags + optional_service_account_flag
end
def tls_flags
[
'--tiller-tls',
'--tiller-tls-verify',
'--tls-ca-cert', "#{files_dir}/ca.pem",
'--tiller-tls-cert', "#{files_dir}/cert.pem",
'--tiller-tls-key', "#{files_dir}/key.pem"
]
end
def optional_service_account_flag
return [] unless rbac?
['--service-account', service_account_name]
end
def cluster_role_binding_name
Gitlab::Kubernetes::Helm::CLUSTER_ROLE_BINDING
end
 
"helm init #{tls_flags} >/dev/null"
def cluster_role_name
Gitlab::Kubernetes::Helm::CLUSTER_ROLE
end
end
end
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment