e5e1c907c0
This MR adds new application setting to network section `allow_local_requests_from_system_hooks`. Prior to this change system hooks were allowed to do local network requests by default and we are adding an ability for admins to control it.
178 lines
5.1 KiB
Ruby
178 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'uri'
|
|
|
|
module Gitlab
|
|
module Kubernetes
|
|
# Wrapper around Kubeclient::Client to dispatch
|
|
# the right message to the client that can respond to the message.
|
|
# We must have a kubeclient for each ApiGroup as there is no
|
|
# other way to use the Kubeclient gem.
|
|
#
|
|
# See https://github.com/abonas/kubeclient/issues/348.
|
|
class KubeClient
|
|
include Gitlab::Utils::StrongMemoize
|
|
|
|
SUPPORTED_API_GROUPS = {
|
|
core: { group: 'api', version: 'v1' },
|
|
rbac: { group: 'apis/rbac.authorization.k8s.io', version: 'v1' },
|
|
extensions: { group: 'apis/extensions', version: 'v1beta1' },
|
|
knative: { group: 'apis/serving.knative.dev', version: 'v1alpha1' }
|
|
}.freeze
|
|
|
|
SUPPORTED_API_GROUPS.each do |name, params|
|
|
client_method_name = "#{name}_client".to_sym
|
|
|
|
define_method(client_method_name) do
|
|
strong_memoize(client_method_name) do
|
|
build_kubeclient(params[:group], params[:version])
|
|
end
|
|
end
|
|
end
|
|
|
|
# Core API methods delegates to the core api group client
|
|
delegate :get_pods,
|
|
:get_secrets,
|
|
:get_config_map,
|
|
:get_namespace,
|
|
:get_pod,
|
|
:get_secret,
|
|
:get_service,
|
|
:get_service_account,
|
|
:delete_pod,
|
|
:create_config_map,
|
|
:create_namespace,
|
|
:create_pod,
|
|
:create_secret,
|
|
:create_service_account,
|
|
:update_config_map,
|
|
:update_secret,
|
|
:update_service_account,
|
|
to: :core_client
|
|
|
|
# RBAC methods delegates to the apis/rbac.authorization.k8s.io api
|
|
# group client
|
|
delegate :create_cluster_role_binding,
|
|
:get_cluster_role_binding,
|
|
:update_cluster_role_binding,
|
|
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
|
|
# group client
|
|
delegate :create_role_binding,
|
|
:get_role_binding,
|
|
:update_role_binding,
|
|
to: :rbac_client
|
|
|
|
# Deployments resource is currently on the apis/extensions api group
|
|
delegate :get_deployments,
|
|
to: :extensions_client
|
|
|
|
# non-entity methods that can only work with the core client
|
|
# as it uses the pods/log resource
|
|
delegate :get_pod_log,
|
|
:watch_pod_log,
|
|
to: :core_client
|
|
|
|
attr_reader :api_prefix, :kubeclient_options
|
|
|
|
# We disable redirects through 'http_max_redirects: 0',
|
|
# so that KubeClient does not follow redirects and
|
|
# expose internal services.
|
|
def initialize(api_prefix, **kubeclient_options)
|
|
@api_prefix = api_prefix
|
|
@kubeclient_options = kubeclient_options.merge(http_max_redirects: 0)
|
|
|
|
validate_url!
|
|
end
|
|
|
|
def create_or_update_cluster_role_binding(resource)
|
|
if cluster_role_binding_exists?(resource)
|
|
update_cluster_role_binding(resource)
|
|
else
|
|
create_cluster_role_binding(resource)
|
|
end
|
|
end
|
|
|
|
def create_or_update_role_binding(resource)
|
|
if role_binding_exists?(resource)
|
|
update_role_binding(resource)
|
|
else
|
|
create_role_binding(resource)
|
|
end
|
|
end
|
|
|
|
def create_or_update_service_account(resource)
|
|
if service_account_exists?(resource)
|
|
update_service_account(resource)
|
|
else
|
|
create_service_account(resource)
|
|
end
|
|
end
|
|
|
|
def create_or_update_secret(resource)
|
|
if secret_exists?(resource)
|
|
update_secret(resource)
|
|
else
|
|
create_secret(resource)
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def validate_url!
|
|
return if Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
|
|
|
|
Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false)
|
|
end
|
|
|
|
def cluster_role_binding_exists?(resource)
|
|
get_cluster_role_binding(resource.metadata.name)
|
|
rescue ::Kubeclient::ResourceNotFoundError
|
|
false
|
|
end
|
|
|
|
def role_binding_exists?(resource)
|
|
get_role_binding(resource.metadata.name, resource.metadata.namespace)
|
|
rescue ::Kubeclient::ResourceNotFoundError
|
|
false
|
|
end
|
|
|
|
def service_account_exists?(resource)
|
|
get_service_account(resource.metadata.name, resource.metadata.namespace)
|
|
rescue ::Kubeclient::ResourceNotFoundError
|
|
false
|
|
end
|
|
|
|
def secret_exists?(resource)
|
|
get_secret(resource.metadata.name, resource.metadata.namespace)
|
|
rescue ::Kubeclient::ResourceNotFoundError
|
|
false
|
|
end
|
|
|
|
def build_kubeclient(api_group, api_version)
|
|
::Kubeclient::Client.new(
|
|
join_api_url(api_prefix, api_group),
|
|
api_version,
|
|
**kubeclient_options
|
|
)
|
|
end
|
|
|
|
def join_api_url(api_prefix, api_path)
|
|
url = URI.parse(api_prefix)
|
|
prefix = url.path.sub(%r{/+\z}, '')
|
|
|
|
url.path = [prefix, api_path].join("/")
|
|
|
|
url.to_s
|
|
end
|
|
end
|
|
end
|
|
end
|