Merge "Fix coredns endpoint"
diff --git a/README.rst b/README.rst
index b0b8e7d..7f4dedc 100644
--- a/README.rst
+++ b/README.rst
@@ -1073,6 +1073,79 @@
value: one
image_pull_secretes: password
+Role-based access control
+=========================
+
+To enable RBAC, you need to set following option on your apiserver:
+
+.. code-block:: yaml
+
+ kubernetes:
+ master:
+ auth:
+ mode: RBAC
+
+Then you can use ``kubernetes.control.role`` state to orchestrate role and
+rolebindings. Following example shows how to create brand new role and binding
+for service account:
+
+.. code-block:: yaml
+
+ control:
+ role:
+ etcd-operator:
+ kind: ClusterRole
+ rules:
+ - apiGroups:
+ - etcd.coreos.com
+ resources:
+ - clusters
+ verbs:
+ - "*"
+ - apiGroups:
+ - extensions
+ resources:
+ - thirdpartyresources
+ verbs:
+ - create
+ - apiGroups:
+ - storage.k8s.io
+ resources:
+ - storageclasses
+ verbs:
+ - create
+ - apiGroups:
+ - ""
+ resources:
+ - replicasets
+ verbs:
+ - "*"
+ binding:
+ etcd-operator:
+ kind: ClusterRoleBinding
+ namespace: test # <-- if no namespace, then it's clusterrolebinding
+ subject:
+ etcd-operator:
+ kind: ServiceAccount
+
+Simplest possible use-case, add user test edit permissions on it's test
+namespace:
+
+.. code-block:: yaml
+
+ kubernetes:
+ control:
+ role:
+ edit:
+ kind: ClusterRole
+ # No rules defined, so only binding will be created assuming role
+ # already exists
+ binding:
+ test:
+ namespace: test
+ subject:
+ test:
+ kind: User
More Information
================
diff --git a/kubernetes/control/cluster.sls b/kubernetes/control/cluster.sls
index f597c40..4f894ba 100644
--- a/kubernetes/control/cluster.sls
+++ b/kubernetes/control/cluster.sls
@@ -1,153 +1,6 @@
-{% from "kubernetes/map.jinja" import control with context %}
-{%- if control.enabled %}
-
-/srv/kubernetes:
- file.directory:
- - makedirs: true
-
-{%- if control.job is defined %}
-
-{%- for job_name, job in control.job.iteritems() %}
-
-/srv/kubernetes/jobs/{{ job_name }}-job.yml:
- file.managed:
- - source: salt://kubernetes/files/job.yml
- - user: root
- - group: root
- - template: jinja
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- job: {{ job|yaml }}
-
-{%- endfor %}
-
-{%- endif %}
-
-{%- for service_name, service in control.service.iteritems() %}
-
-{%- if service.enabled %}
-
-/srv/kubernetes/services/{{ service.cluster }}/{{ service_name }}-svc.yml:
- file.managed:
- - source: salt://kubernetes/files/svc.yml
- - user: root
- - group: root
- - template: jinja
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- service: {{ service|yaml }}
-
-{%- endif %}
-
-/srv/kubernetes/{{ service.kind|lower }}/{{ service_name }}-{{ service.kind }}.yml:
- file.managed:
- - source: salt://kubernetes/files/rc.yml
- - user: root
- - group: root
- - template: jinja
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- service: {{ service|yaml }}
-
-{%- endfor %}
-
-{%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').iteritems() %}
-
-{%- if node_grains.get('kubernetes', {}).service is defined %}
-
-{%- set service = node_grains.get('kubernetes', {}).get('service', {}) %}
-
-{%- if service.enabled %}
-
-/srv/kubernetes/services/{{ node_name }}-svc.yml:
- file.managed:
- - source: salt://kubernetes/files/svc.yml
- - user: root
- - group: root
- - template: jinja
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- service: {{ service|yaml }}
-
-{%- endif %}
-/srv/kubernetes/{{ service.kind|lower }}/{{ node_name }}-{{ service.kind }}.yml:
- file.managed:
- - source: salt://kubernetes/files/rc.yml
- - user: root
- - group: root
- - template: jinja
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- service: {{ service|yaml }}
-
-{%- endif %}
-
-{%- endfor %}
-
-{%- for configmap_name, configmap in control.get('configmap', {}).iteritems() %}
-{%- if configmap.enabled|default(True) %}
-
-{%- if configmap.pillar is defined %}
-{%- if control.config_type == "default" %}
- {%- for service_name in configmap.pillar.keys() %}
- {%- if pillar.get(service_name, {}).get('_support', {}).get('config', {}).get('enabled', False) %}
-
- {%- set support_fragment_file = service_name+'/meta/config.yml' %}
- {% macro load_support_file(pillar, grains) %}{% include support_fragment_file %}{% endmacro %}
-
- {%- set service_config_files = load_support_file(configmap.pillar, configmap.get('grains', {}))|load_yaml %}
- {%- for service_config_name, service_config in service_config_files.config.iteritems() %}
-
-/srv/kubernetes/configmap/{{ configmap_name }}/{{ service_config_name }}:
- file.managed:
- - source: {{ service_config.source }}
- - user: root
- - group: root
- - template: {{ service_config.template }}
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- pillar: {{ configmap.pillar|yaml }}
- grains: {{ configmap.get('grains', {}) }}
-
- {%- endfor %}
- {%- endif %}
- {%- endfor %}
-
-{%- else %}
-
-/srv/kubernetes/configmap/{{ configmap_name }}.yml:
- file.managed:
- - source: salt://kubernetes/files/configmap.yml
- - user: root
- - group: root
- - template: jinja
- - makedirs: true
- - require:
- - file: /srv/kubernetes
- - defaults:
- configmap_name: {{ configmap_name }}
- configmap: {{ configmap|yaml }}
- grains: {{ configmap.get('grains', {}) }}
-
-{%- endif %}
-
-{%- else %}
-{# TODO: configmap not using support between formulas #}
-{%- endif %}
-
-{%- endif %}
-{%- endfor %}
-
-{%- endif %}
+{#-
+ kubernetes.control.cluster is only for backward compatibility and simply
+ includes kubernetes.control and it's child states
+-#}
+include:
+ - kubernetes.control
diff --git a/kubernetes/control/configmap.sls b/kubernetes/control/configmap.sls
new file mode 100644
index 0000000..07b8189
--- /dev/null
+++ b/kubernetes/control/configmap.sls
@@ -0,0 +1,59 @@
+{% from "kubernetes/map.jinja" import control with context %}
+include:
+ - kubernetes.control
+
+{%- for configmap_name, configmap in control.get('configmap', {}).iteritems() %}
+{%- if configmap.enabled|default(True) %}
+
+{%- if configmap.pillar is defined %}
+{%- if control.config_type == "default" %}
+ {%- for service_name in configmap.pillar.keys() %}
+ {%- if pillar.get(service_name, {}).get('_support', {}).get('config', {}).get('enabled', False) %}
+
+ {%- set support_fragment_file = service_name+'/meta/config.yml' %}
+ {% macro load_support_file(pillar, grains) %}{% include support_fragment_file %}{% endmacro %}
+
+ {%- set service_config_files = load_support_file(configmap.pillar, configmap.get('grains', {}))|load_yaml %}
+ {%- for service_config_name, service_config in service_config_files.config.iteritems() %}
+
+/srv/kubernetes/configmap/{{ configmap_name }}/{{ service_config_name }}:
+ file.managed:
+ - source: {{ service_config.source }}
+ - user: root
+ - group: root
+ - template: {{ service_config.template }}
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ pillar: {{ configmap.pillar|yaml }}
+ grains: {{ configmap.get('grains', {}) }}
+
+ {%- endfor %}
+ {%- endif %}
+ {%- endfor %}
+
+{%- else %}
+
+/srv/kubernetes/configmap/{{ configmap_name }}.yml:
+ file.managed:
+ - source: salt://kubernetes/files/configmap.yml
+ - user: root
+ - group: root
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ configmap_name: {{ configmap_name }}
+ configmap: {{ configmap|yaml }}
+ grains: {{ configmap.get('grains', {}) }}
+
+{%- endif %}
+
+{%- else %}
+{# TODO: configmap not using support between formulas #}
+{%- endif %}
+
+{%- endif %}
+{%- endfor %}
diff --git a/kubernetes/control/init.sls b/kubernetes/control/init.sls
index 35c5745..be31c21 100644
--- a/kubernetes/control/init.sls
+++ b/kubernetes/control/init.sls
@@ -1,3 +1,18 @@
-
+{% from "kubernetes/map.jinja" import control with context %}
include:
-- kubernetes.control.cluster
+ {%- if control.job is defined %}
+ - kubernetes.control.job
+ {%- endif %}
+ {%- if control.service is defined %}
+ - kubernetes.control.service
+ {%- endif %}
+ {%- if control.configmap is defined %}
+ - kubernetes.control.configmap
+ {%- endif %}
+ {%- if control.role is defined %}
+ - kubernetes.control.role
+ {%- endif %}
+
+/srv/kubernetes:
+ file.directory:
+ - makedirs: true
diff --git a/kubernetes/control/job.sls b/kubernetes/control/job.sls
new file mode 100644
index 0000000..b3a0696
--- /dev/null
+++ b/kubernetes/control/job.sls
@@ -0,0 +1,19 @@
+{% from "kubernetes/map.jinja" import control with context %}
+include:
+ - kubernetes.control
+
+{%- for job_name, job in control.job.iteritems() %}
+
+/srv/kubernetes/jobs/{{ job_name }}-job.yml:
+ file.managed:
+ - source: salt://kubernetes/files/job.yml
+ - user: root
+ - group: root
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ job: {{ job|yaml }}
+
+{%- endfor %}
diff --git a/kubernetes/control/role.sls b/kubernetes/control/role.sls
new file mode 100644
index 0000000..ce7248d
--- /dev/null
+++ b/kubernetes/control/role.sls
@@ -0,0 +1,56 @@
+{% from "kubernetes/map.jinja" import control with context %}
+include:
+ - kubernetes.control
+
+{%- for role_name, role in control.role.iteritems() %}
+ {%- set role_name = role.name|default(role_name) %}
+
+ {%- if role.get('namespace') or role.get('kind') == 'Role' %}
+ {%- set role_kind = 'Role' %}
+ {%- else %}
+ {%- set role_kind = 'ClusterRole' %}
+ {%- endif %}
+
+ {%- if role.enabled|default(True) %}
+
+ {%- if role.get('rules') %}
+/srv/kubernetes/roles/{{ role_name }}/{{ role_name }}-role.yml:
+ file.managed:
+ - source: salt://kubernetes/files/role.yml
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ role_name: {{ role_name }}
+ role_kind: {{ role_kind }}
+ role: {{ role|yaml }}
+ {%- endif %}
+
+ {%- for binding_name, binding in role.get('binding', {}).iteritems() %}
+ {%- set binding_name = binding.name|default(binding_name) %}
+ {%- if binding.get('namespace') or binding.get('kind') == 'RoleBinding' %}
+ {%- set binding_kind = 'RoleBinding' %}
+ {%- else %}
+ {%- set binding_kind = 'ClusterRoleBinding' %}
+ {%- endif %}
+
+/srv/kubernetes/roles/{{ role_name }}/{{ binding_name }}-rolebinding.yml:
+ file.managed:
+ - source: salt://kubernetes/files/rolebinding.yml
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ role_name: {{ role_name }}
+ role_kind: {{ role_kind }}
+ role: {{ role|yaml }}
+ binding_name: {{ binding_name }}
+ binding_kind: {{ binding_kind }}
+ binding: {{ binding|yaml }}
+
+ {%- endfor %}
+
+ {%- endif %}
+{%- endfor %}
diff --git a/kubernetes/control/service.sls b/kubernetes/control/service.sls
new file mode 100644
index 0000000..b88dd85
--- /dev/null
+++ b/kubernetes/control/service.sls
@@ -0,0 +1,69 @@
+{% from "kubernetes/map.jinja" import control with context %}
+include:
+ - kubernetes.control
+
+{%- for service_name, service in control.service.iteritems() %}
+ {%- if service.enabled %}
+
+/srv/kubernetes/services/{{ service.cluster }}/{{ service_name }}-svc.yml:
+ file.managed:
+ - source: salt://kubernetes/files/svc.yml
+ - user: root
+ - group: root
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ service: {{ service|yaml }}
+
+ {%- endif %}
+
+/srv/kubernetes/{{ service.kind|lower }}/{{ service_name }}-{{ service.kind }}.yml:
+ file.managed:
+ - source: salt://kubernetes/files/rc.yml
+ - user: root
+ - group: root
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ service: {{ service|yaml }}
+
+{%- endfor %}
+
+{%- for node_name, node_grains in salt['mine.get']('*', 'grains.items').iteritems() %}
+
+ {%- if node_grains.get('kubernetes', {}).service is defined %}
+ {%- set service = node_grains.get('kubernetes', {}).get('service', {}) %}
+ {%- if service.enabled %}
+
+/srv/kubernetes/services/{{ node_name }}-svc.yml:
+ file.managed:
+ - source: salt://kubernetes/files/svc.yml
+ - user: root
+ - group: root
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ service: {{ service|yaml }}
+
+ {%- endif %}
+/srv/kubernetes/{{ service.kind|lower }}/{{ node_name }}-{{ service.kind }}.yml:
+ file.managed:
+ - source: salt://kubernetes/files/rc.yml
+ - user: root
+ - group: root
+ - template: jinja
+ - makedirs: true
+ - require:
+ - file: /srv/kubernetes
+ - defaults:
+ service: {{ service|yaml }}
+
+ {%- endif %}
+
+{%- endfor %}
diff --git a/kubernetes/files/manifest/kube-apiserver.manifest b/kubernetes/files/manifest/kube-apiserver.manifest
index 3cf76c8..58afb82 100644
--- a/kubernetes/files/manifest/kube-apiserver.manifest
+++ b/kubernetes/files/manifest/kube-apiserver.manifest
@@ -46,6 +46,9 @@
--etcd-quorum-read=true
--v={{ master.get('verbosity', 2) }}
--allow-privileged=True
+ {%- if master.auth.get('mode') %}
+ --authorization-mode={{ master.auth.mode }}
+ {%- endif %}
{%- if master.apiserver.node_port_range is defined %}
--service-node-port-range {{ master.apiserver.node_port_range }}
{%- endif %}
diff --git a/kubernetes/files/role.yml b/kubernetes/files/role.yml
new file mode 100644
index 0000000..335a5e5
--- /dev/null
+++ b/kubernetes/files/role.yml
@@ -0,0 +1,17 @@
+kind: {{ role_kind }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ {%- if role.get('namespace') %}
+ namespace: {{ role.namespace }}
+ {%- endif %}
+ name: {{ role_name }}
+{%- if role.get('rules') %}
+rules:
+ {%- for rule in role.rules %}
+ - {{ rule|yaml|indent(2) }}
+ {%- endfor %}
+{%- endif %}
+
+{#-
+vim: syntax=jinja
+-#}
diff --git a/kubernetes/files/rolebinding.yml b/kubernetes/files/rolebinding.yml
new file mode 100644
index 0000000..1befc8d
--- /dev/null
+++ b/kubernetes/files/rolebinding.yml
@@ -0,0 +1,21 @@
+kind: {{ binding_kind }}
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+ name: {{ binding_name }}
+ {%- if binding.get('namespace') %}
+ namespace: {{ binding.namespace }}
+ {%- endif %}
+subjects:
+ {%- for subject_name, subject in binding.subject.iteritems() %}
+ - kind: {{ subject.kind }}
+ name: {{ subject.name|default(subject_name) }}
+ apiGroup: rbac.authorization.k8s.io
+ {%- endfor %}
+roleRef:
+ kind: {{ role_kind }}
+ name: {{ role_name }}
+ apiGroup: rbac.authorization.k8s.io
+
+{#-
+vim: syntax=jinja
+-#}
diff --git a/kubernetes/master/controller.sls b/kubernetes/master/controller.sls
index 108bb07..282fd36 100644
--- a/kubernetes/master/controller.sls
+++ b/kubernetes/master/controller.sls
@@ -95,6 +95,9 @@
DAEMON_ARGS="
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,DefaultStorageClass
--allow-privileged=True
+ {%- if master.auth.get('mode') %}
+ --authorization-mode={{ master.auth.mode }}
+ {%- endif %}
{%- if master.auth.get('basic', {}).enabled|default(True) %}
--basic-auth-file={{ master.auth.basic.file|default("/srv/kubernetes/basic_auth.csv") }}
{%- endif %}