diff --git a/inventory/sample/group_vars/k8s-cluster.yml b/inventory/sample/group_vars/k8s-cluster.yml
index 2ca718598469703a58b15773130aa08a82a2dd42..ded955066aab9200d9554724d3b4d29cc18a4b73 100644
--- a/inventory/sample/group_vars/k8s-cluster.yml
+++ b/inventory/sample/group_vars/k8s-cluster.yml
@@ -172,6 +172,9 @@ k8s_image_pull_policy: IfNotPresent
 # audit log for kubernetes
 kubernetes_audit: false
 
+# pod security policy (RBAC must be enabled either by having 'RBAC' in authorization_modes or kubeadm enabled)
+podsecuritypolicy_enabled: false
+
 # Kubernetes dashboard
 # RBAC required. see docs/getting-started.md for access details.
 dashboard_enabled: true
diff --git a/roles/kubernetes-apps/ansible/defaults/main.yml b/roles/kubernetes-apps/ansible/defaults/main.yml
index 312b6aca8a5aaa100a5423563e5e81f4ac3fe87e..263700d9797b150aeb897490f95aefc6fc43539d 100644
--- a/roles/kubernetes-apps/ansible/defaults/main.yml
+++ b/roles/kubernetes-apps/ansible/defaults/main.yml
@@ -42,6 +42,12 @@ netchecker_server_memory_limit: 256M
 netchecker_server_cpu_requests: 50m
 netchecker_server_memory_requests: 64M
 
+# SecurityContext when PodSecurityPolicy is enabled
+netchecker_agent_user: 1000
+netchecker_server_user: 1000
+netchecker_agent_group: 1000
+netchecker_server_group: 1000
+
 # Dashboard
 dashboard_enabled: true
 dashboard_image_repo: gcr.io/google_containers/kubernetes-dashboard-{{ image_arch }}
diff --git a/roles/kubernetes-apps/ansible/tasks/netchecker.yml b/roles/kubernetes-apps/ansible/tasks/netchecker.yml
index bf0322a2714c05d92a2f2fb84355e6a4c47005ec..655ef744b0fd2c71b1adce9c9e71b645fed0c16e 100644
--- a/roles/kubernetes-apps/ansible/tasks/netchecker.yml
+++ b/roles/kubernetes-apps/ansible/tasks/netchecker.yml
@@ -20,18 +20,32 @@
   tags:
     - upgrade
 
+- name: Kubernetes Apps | Netchecker Templates list
+  set_fact:
+    netchecker_templates:
+      - {file: netchecker-agent-sa.yml, type: sa, name: netchecker-agent}
+      - {file: netchecker-agent-ds.yml, type: ds, name: netchecker-agent}
+      - {file: netchecker-agent-hostnet-ds.yml, type: ds, name: netchecker-agent-hostnet}
+      - {file: netchecker-server-sa.yml, type: sa, name: netchecker-server}
+      - {file: netchecker-server-clusterrole.yml, type: clusterrole, name: netchecker-server}
+      - {file: netchecker-server-clusterrolebinding.yml, type: clusterrolebinding, name: netchecker-server}
+      - {file: netchecker-server-deployment.yml, type: deployment, name: netchecker-server}
+      - {file: netchecker-server-svc.yml, type: svc, name: netchecker-service}
+    netchecker_templates_for_psp:
+      - {file: netchecker-agent-hostnet-psp.yml, type: podsecuritypolicy, name: netchecker-agent-hostnet-policy}
+      - {file: netchecker-agent-hostnet-clusterrole.yml, type: clusterrole, name: netchecker-agent}
+      - {file: netchecker-agent-hostnet-clusterrolebinding.yml, type: clusterrolebinding, name: netchecker-agent}
+
+- name: Kubernetes Apps | Append extra templates to Netchecker Templates list for PodSecurityPolicy
+  set_fact:
+    netchecker_templates: "{{ netchecker_templates_for_psp + netchecker_templates}}"
+  when: podsecuritypolicy_enabled
+
 - name: Kubernetes Apps | Lay Down Netchecker Template
   template:
     src: "{{item.file}}.j2"
     dest: "{{kube_config_dir}}/{{item.file}}"
-  with_items:
-    - {file: netchecker-agent-ds.yml, type: ds, name: netchecker-agent}
-    - {file: netchecker-agent-hostnet-ds.yml, type: ds, name: netchecker-agent-hostnet}
-    - {file: netchecker-server-sa.yml, type: sa, name: netchecker-server}
-    - {file: netchecker-server-clusterrole.yml, type: clusterrole, name: netchecker-server}
-    - {file: netchecker-server-clusterrolebinding.yml, type: clusterrolebinding, name: netchecker-server}
-    - {file: netchecker-server-deployment.yml, type: deployment, name: netchecker-server}
-    - {file: netchecker-server-svc.yml, type: svc, name: netchecker-service}
+  with_items: "{{ netchecker_templates }}"
   register: manifests
   when:
     - inventory_hostname == groups['kube-master'][0]
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-agent-ds.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-agent-ds.yml.j2
index 4f32214ebd996efcaf101707f3098caabdd72154..4314482314aaf13cd7d549b8147d82f811e9a344 100644
--- a/roles/kubernetes-apps/ansible/templates/netchecker-agent-ds.yml.j2
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-agent-ds.yml.j2
@@ -40,6 +40,10 @@ spec:
             requests:
               cpu: {{ netchecker_agent_cpu_requests }}
               memory: {{ netchecker_agent_memory_requests }}
+          securityContext:
+            runAsUser: {{ netchecker_agent_user | default('0') }}
+            runAsGroup: {{ netchecker_agent_group | default('0') }}
+      serviceAccountName: netchecker-agent
   updateStrategy:
     rollingUpdate:
       maxUnavailable: 100%
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-clusterrole.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-clusterrole.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..f364b428388b93663933d238c7dae3a7ea2e2b34
--- /dev/null
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-clusterrole.yml.j2
@@ -0,0 +1,14 @@
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: psp:netchecker-agent-hostnet
+  namespace: {{ netcheck_namespace }}
+rules:
+  - apiGroups:
+    - policy
+    resourceNames:
+    - netchecker-agent-hostnet
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-clusterrolebinding.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-clusterrolebinding.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..766faa28cb57954bc2e5b4d9ca8f298ae2183a4d
--- /dev/null
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-clusterrolebinding.yml.j2
@@ -0,0 +1,13 @@
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: psp:netchecker-agent-hostnet
+  namespace: {{ netcheck_namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: netchecker-agent-hostnet
+    namespace: {{ netcheck_namespace }}
+roleRef:
+  kind: ClusterRole
+  name: psp:netchecker-agent-hostnet
+  apiGroup: rbac.authorization.k8s.io
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-ds.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-ds.yml.j2
index 76fca4812834415039eaeafe941a0399f004c057..ad32d509a13b6a0791f51e71d363d4de9ebecbf4 100644
--- a/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-ds.yml.j2
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-ds.yml.j2
@@ -44,6 +44,10 @@ spec:
             requests:
               cpu: {{ netchecker_agent_cpu_requests }}
               memory: {{ netchecker_agent_memory_requests }}
+          securityContext:
+            runAsUser: {{ netchecker_agent_user | default('0') }}
+            runAsGroup: {{ netchecker_agent_group | default('0') }}
+      serviceAccountName: netchecker-agent
   updateStrategy:
     rollingUpdate:
       maxUnavailable: 100%
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-psp.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-psp.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..32fb0c1a0e15bf064128db6161631f733e1dde13
--- /dev/null
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-agent-hostnet-psp.yml.j2
@@ -0,0 +1,45 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: netchecker-agent-hostnet
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: false
+  allowPrivilegeEscalation: false
+  requiredDropCapabilities:
+    - ALL
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'projected'
+    - 'secret'
+    - 'downwardAPI'
+    - 'persistentVolumeClaim'
+  hostNetwork: true
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'MustRunAsNonRoot'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-agent-sa.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-agent-sa.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..d842faa6c076b0dd1b1fcfd3b340cb1cfd00df6c
--- /dev/null
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-agent-sa.yml.j2
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: netchecker-agent
+  namespace: {{ netcheck_namespace }}
+  labels:
+    kubernetes.io/cluster-service: "true"
diff --git a/roles/kubernetes-apps/ansible/templates/netchecker-server-deployment.yml.j2 b/roles/kubernetes-apps/ansible/templates/netchecker-server-deployment.yml.j2
index 30e3b590778e06719ea5db1e227cc7807d5e3d94..1a858683d2af777febbdee0d39664cae27c3b19d 100644
--- a/roles/kubernetes-apps/ansible/templates/netchecker-server-deployment.yml.j2
+++ b/roles/kubernetes-apps/ansible/templates/netchecker-server-deployment.yml.j2
@@ -23,6 +23,9 @@ spec:
             requests:
               cpu: {{ netchecker_server_cpu_requests }}
               memory: {{ netchecker_server_memory_requests }}
+          securityContext:
+            runAsUser: {{ netchecker_server_user | default('0') }}
+            runAsGroup: {{ netchecker_server_group | default('0') }}
           ports:
             - containerPort: 8081
           args:
diff --git a/roles/kubernetes-apps/cluster_roles/tasks/main.yml b/roles/kubernetes-apps/cluster_roles/tasks/main.yml
index 0511b7be52240987773c24131c5c03c691a7cccd..56ab951b623167b7903282ea3e268846a75bbef8 100644
--- a/roles/kubernetes-apps/cluster_roles/tasks/main.yml
+++ b/roles/kubernetes-apps/cluster_roles/tasks/main.yml
@@ -11,6 +11,46 @@
   delay: 6
   when: inventory_hostname == groups['kube-master'][0]
 
+- name: Kubernetes Apps | Check AppArmor status
+  command: which apparmor_parser
+  register: apparmor_status
+  when:
+    - podsecuritypolicy_enabled
+    - inventory_hostname == groups['kube-master'][0]
+  failed_when: false
+
+- name: Kubernetes Apps | Set apparmor_enabled
+  set_fact:
+    apparmor_enabled: "{{ apparmor_status.rc == 0 }}"
+  when:
+    - podsecuritypolicy_enabled
+    - inventory_hostname == groups['kube-master'][0]
+
+- name: Kubernetes Apps | Render templates for PodSecurityPolicy
+  template:
+    src: "{{ item.file }}.j2"
+    dest: "{{ kube_config_dir }}/{{ item.file }}"
+  register: psp_manifests
+  with_items:
+    - {file: psp.yml, type: psp, name: psp}
+    - {file: psp-cr.yml, type: clusterrole, name: psp-cr}
+    - {file: psp-crb.yml, type: rolebinding, name: psp-crb}
+  when:
+    - podsecuritypolicy_enabled
+    - inventory_hostname == groups['kube-master'][0]
+
+- name: Kubernetes Apps | Add policies, roles, bindings for PodSecurityPolicy
+  kube:
+    name: "{{item.item.name}}"
+    kubectl: "{{bin_dir}}/kubectl"
+    resource: "{{item.item.type}}"
+    filename: "{{kube_config_dir}}/{{item.item.file}}"
+    state: "latest"
+  with_items: "{{ psp_manifests.results }}"
+  when:
+    - inventory_hostname == groups['kube-master'][0]
+    - not item|skipped
+
 - name: Kubernetes Apps | Add ClusterRoleBinding to admit nodes
   template:
     src: "node-crb.yml.j2"
diff --git a/roles/kubernetes-apps/cluster_roles/templates/psp-cr.yml.j2 b/roles/kubernetes-apps/cluster_roles/templates/psp-cr.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..9fa333ddc62294d8f9f7db880a15d980a3430f19
--- /dev/null
+++ b/roles/kubernetes-apps/cluster_roles/templates/psp-cr.yml.j2
@@ -0,0 +1,35 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: psp:privileged
+  labels:
+    kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+  - policy
+  resourceNames:
+  - privileged
+  resources:
+  - podsecuritypolicies
+  verbs:
+  - use
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: psp:restricted
+  namespace: kube-system
+  labels:
+    kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
+rules:
+- apiGroups:
+  - policy
+  resourceNames:
+  - restricted
+  resources:
+  - podsecuritypolicies
+  verbs:
+  - use
diff --git a/roles/kubernetes-apps/cluster_roles/templates/psp-crb.yml.j2 b/roles/kubernetes-apps/cluster_roles/templates/psp-crb.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..6cade2883b772f73b3d32092cf2198ddc879c0e8
--- /dev/null
+++ b/roles/kubernetes-apps/cluster_roles/templates/psp-crb.yml.j2
@@ -0,0 +1,55 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+  name: psp:any:restricted
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: psp:restricted
+subjects:
+- kind: Group
+  name: system:authenticated
+  apiGroup: rbac.authorization.k8s.io
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: psp:kube-system:privileged
+  namespace: kube-system
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: psp:privileged
+subjects:
+- kind: Group
+  name: system:masters
+  apiGroup: rbac.authorization.k8s.io
+- kind: Group
+  name: system:serviceaccounts:kube-system
+  apiGroup: rbac.authorization.k8s.io
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: psp:nodes:privileged
+  namespace: kube-system
+  annotations:
+    kubernetes.io/description: 'Allow nodes to create privileged pods. Should
+      be used in combination with the NodeRestriction admission plugin to limit
+      nodes to mirror pods bound to themselves.'
+  labels:
+    addonmanager.kubernetes.io/mode: Reconcile
+    kubernetes.io/cluster-service: 'true'
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: ClusterRole
+  name: psp:privileged
+subjects:
+  - kind: Group
+    apiGroup: rbac.authorization.k8s.io
+    name: system:nodes
+  - kind: User
+    apiGroup: rbac.authorization.k8s.io
+    # Legacy node ID
+    name: kubelet
diff --git a/roles/kubernetes-apps/cluster_roles/templates/psp.yml.j2 b/roles/kubernetes-apps/cluster_roles/templates/psp.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..a9d32a6e6bb8d3b8813bf139bd816e454470b742
--- /dev/null
+++ b/roles/kubernetes-apps/cluster_roles/templates/psp.yml.j2
@@ -0,0 +1,77 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: restricted
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: false
+  allowPrivilegeEscalation: false
+  requiredDropCapabilities:
+    - ALL
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'projected'
+    - 'secret'
+    - 'downwardAPI'
+    - 'persistentVolumeClaim'
+  hostNetwork: false
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'MustRunAsNonRoot'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  readOnlyRootFilesystem: false
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: privileged
+  annotations:
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
+  labels:
+    kubernetes.io/cluster-service: "true"
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: true
+  allowPrivilegeEscalation: true
+  allowedCapabilities:
+  - '*'
+  volumes:
+  - '*'
+  hostNetwork: true
+  hostPorts:
+  - min: 0
+    max: 65535
+  hostIPC: true
+  hostPID: true
+  runAsUser:
+    rule: 'RunAsAny'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'RunAsAny'
+  fsGroup:
+    rule: 'RunAsAny'
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/tasks/main.yml b/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/tasks/main.yml
index 7b78080c350f3d07a70ccd0c5897dd4c9a27058d..c93ecfde79b35bf8e7e4016dd59cd67f3f442e2b 100644
--- a/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/tasks/main.yml
+++ b/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/tasks/main.yml
@@ -37,20 +37,33 @@
   when:
     - inventory_hostname == groups['kube-master'][0]
 
+- name: CephFS Provisioner | Templates list
+  set_fact:
+    cephfs_provisioner_templates:
+      - { name: 00-namespace, file: 00-namespace.yml, type: ns }
+      - { name: secret-cephfs-provisioner, file: secret-cephfs-provisioner.yml, type: secret }
+      - { name: sa-cephfs-provisioner, file: sa-cephfs-provisioner.yml, type: sa }
+      - { name: clusterrole-cephfs-provisioner, file: clusterrole-cephfs-provisioner.yml, type: clusterrole }
+      - { name: clusterrolebinding-cephfs-provisioner, file: clusterrolebinding-cephfs-provisioner.yml, type: clusterrolebinding }
+      - { name: role-cephfs-provisioner, file: role-cephfs-provisioner.yml, type: role }
+      - { name: rolebinding-cephfs-provisioner, file: rolebinding-cephfs-provisioner.yml, type: rolebinding }
+      - { name: deploy-cephfs-provisioner, file: deploy-cephfs-provisioner.yml, type: deploy }
+      - { name: sc-cephfs-provisioner, file: sc-cephfs-provisioner.yml, type: sc }
+    cephfs_provisioner_templates_for_psp:
+      - { name: psp-cephfs-provisioner, file: psp-cephfs-provisioner.yml, type: psp }
+
+- name: CephFS Provisioner | Append extra templates to CephFS Provisioner Templates list for PodSecurityPolicy
+  set_fact:
+    cephfs_provisioner_templates: "{{ cephfs_provisioner_templates_for_psp + cephfs_provisioner_templates }}"
+  when:
+    - podsecuritypolicy_enabled
+    - cephfs_provisioner_namespace != "kube-system"
+
 - name: CephFS Provisioner | Create manifests
   template:
     src: "{{ item.file }}.j2"
     dest: "{{ kube_config_dir }}/addons/cephfs_provisioner/{{ item.file }}"
-  with_items:
-    - { name: 00-namespace, file: 00-namespace.yml, type: ns }
-    - { name: secret-cephfs-provisioner, file: secret-cephfs-provisioner.yml, type: secret }
-    - { name: sa-cephfs-provisioner, file: sa-cephfs-provisioner.yml, type: sa }
-    - { name: clusterrole-cephfs-provisioner, file: clusterrole-cephfs-provisioner.yml, type: clusterrole }
-    - { name: clusterrolebinding-cephfs-provisioner, file: clusterrolebinding-cephfs-provisioner.yml, type: clusterrolebinding }
-    - { name: role-cephfs-provisioner, file: role-cephfs-provisioner.yml, type: role }
-    - { name: rolebinding-cephfs-provisioner, file: rolebinding-cephfs-provisioner.yml, type: rolebinding }
-    - { name: deploy-cephfs-provisioner, file: deploy-cephfs-provisioner.yml, type: deploy }
-    - { name: sc-cephfs-provisioner, file: sc-cephfs-provisioner.yml, type: sc }
+  with_items: "{{ cephfs_provisioner_templates }}"
   register: cephfs_provisioner_manifests
   when: inventory_hostname == groups['kube-master'][0]
 
diff --git a/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/clusterrole-cephfs-provisioner.yml.j2 b/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/clusterrole-cephfs-provisioner.yml.j2
index 398956b68afde21d576498c3b247f0262e12cc9f..359d61a40993eaba5cf87f71b5a788c7a7a3859b 100644
--- a/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/clusterrole-cephfs-provisioner.yml.j2
+++ b/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/clusterrole-cephfs-provisioner.yml.j2
@@ -23,3 +23,11 @@ rules:
   - apiGroups: [""]
     resources: ["secrets"]
     verbs: ["get", "create", "delete"]
+  - apiGroups:
+    - policy
+    resourceNames:
+    - cephfs-provisioner
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/psp-cephfs-provisioner.yml.j2 b/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/psp-cephfs-provisioner.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..b1e9b0ac117f9b4e7096bacc80fb3232a92d8073
--- /dev/null
+++ b/roles/kubernetes-apps/external_provisioner/cephfs_provisioner/templates/psp-cephfs-provisioner.yml.j2
@@ -0,0 +1,45 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: cephfs-provisioner
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: false
+  allowPrivilegeEscalation: false
+  requiredDropCapabilities:
+    - ALL
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'projected'
+    - 'secret'
+    - 'downwardAPI'
+    - 'persistentVolumeClaim'
+  hostNetwork: false
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'RunAsAny'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/tasks/main.yml b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/tasks/main.yml
index b83e45a2023273f014333f45d7591ea2f714573f..4485639228449788e657186a8fa813a940b1aa78 100644
--- a/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/tasks/main.yml
+++ b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/tasks/main.yml
@@ -19,17 +19,32 @@
     group: root
     mode: 0755
 
+- name: Local Volume Provisioner | Templates list
+  set_fact:
+    local_volume_provisioner_templates:
+      - { name: local-volume-provisioner-ns, file: local-volume-provisioner-ns.yml, type: ns }
+      - { name: local-volume-provisioner-sa, file: local-volume-provisioner-sa.yml, type: sa }
+      - { name: local-volume-provisioner-clusterrolebinding, file: local-volume-provisioner-clusterrolebinding.yml, type: clusterrolebinding }
+      - { name: local-volume-provisioner-cm, file: local-volume-provisioner-cm.yml, type: cm }
+      - { name: local-volume-provisioner-ds, file: local-volume-provisioner-ds.yml, type: ds }
+      - { name: local-volume-provisioner-sc, file: local-volume-provisioner-sc.yml, type: sc }
+    local_volume_provisioner_templates_for_psp_not_system_ns:
+      - { name: local-volume-provisioner-psp, file: local-volume-provisioner-psp.yml, type: psp }
+      - { name: local-volume-provisioner-psp-role, file: local-volume-provisioner-psp-role.yml, type: role }
+      - { name: local-volume-provisioner-psp-rb, file: local-volume-provisioner-psp-rb.yml, type: rolebinding }
+
+- name: Local Volume Provisioner | Insert extra templates to Local Volume Provisioner templates list for PodSecurityPolicy
+  set_fact:
+    local_volume_provisioner_templates: "{{ local_volume_provisioner_templates[:2] + local_volume_provisioner_templates_for_psp_not_system_ns + local_volume_provisioner_templates[3:] }}"
+  when:
+    - podsecuritypolicy_enabled
+    - local_volume_provisioner_namespace != "kube-system"
+
 - name: Local Volume Provisioner | Create manifests
   template:
     src: "{{ item.file }}.j2"
     dest: "{{ kube_config_dir }}/addons/local_volume_provisioner/{{ item.file }}"
-  with_items:
-    - { name: local-volume-provisioner-ns, file: local-volume-provisioner-ns.yml, type: ns }
-    - { name: local-volume-provisioner-sa, file: local-volume-provisioner-sa.yml, type: sa }
-    - { name: local-volume-provisioner-clusterrolebinding, file: local-volume-provisioner-clusterrolebinding.yml, type, clusterrolebinding }
-    - { name: local-volume-provisioner-cm, file: local-volume-provisioner-cm.yml, type, cm }
-    - { name: local-volume-provisioner-ds, file: local-volume-provisioner-ds.yml, type, ds }
-    - { name: local-volume-provisioner-sc, file: local-volume-provisioner-sc.yml, type, sc }
+  with_items: "{{ local_volume_provisioner_templates }}"
   register: local_volume_provisioner_manifests
   when: inventory_hostname == groups['kube-master'][0]
 
diff --git a/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp-cr.yml.j2 b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp-cr.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..a9dcc23ae5811f7cddee26d1015cd014224ce734
--- /dev/null
+++ b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp-cr.yml.j2
@@ -0,0 +1,14 @@
+kind: ClusterRole
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: psp:local-volume-provisioner
+  namespace: {{ local_volume_provisioner_namespace }}
+rules:
+  - apiGroups:
+    - policy
+    resourceNames:
+    - local-volume-provisioner
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp-rb.yml.j2 b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp-rb.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..45ca822a3be37bb44c5dcda383f0a7e500f1aab9
--- /dev/null
+++ b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp-rb.yml.j2
@@ -0,0 +1,13 @@
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: psp:local-volume-provisioner
+  namespace: {{ local_volume_provisioner_namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: local-volume-provisioner
+    namespace: {{ local_volume_provisioner_namespace }}
+roleRef:
+  kind: ClusterRole
+  name: psp:local-volume-provisioner
+  apiGroup: rbac.authorization.k8s.io
diff --git a/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp.yml.j2 b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..9daf694fa369e85536cdb8823d2b9b83f1d34c78
--- /dev/null
+++ b/roles/kubernetes-apps/external_provisioner/local_volume_provisioner/templates/local-volume-provisioner-psp.yml.j2
@@ -0,0 +1,44 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: local-volume-provisioner
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: true
+  allowPrivilegeEscalation: true
+  requiredDropCapabilities:
+    - ALL
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'secret'
+    - 'downwardAPI'
+    - 'hostPath'
+  allowedHostPaths:
+    - pathPrefix: "{{ local_volume_provisioner_base_dir }}"
+      readOnly: false
+  hostNetwork: false
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'RunAsAny'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'RunAsAny'
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/ingress_controller/cert_manager/defaults/main.yml b/roles/kubernetes-apps/ingress_controller/cert_manager/defaults/main.yml
index 5136cad53dacbc4cc2ebb518ffab099fb3b321e5..58c09e6a9d54d156a7c96fa869cb7267a69ec35f 100644
--- a/roles/kubernetes-apps/ingress_controller/cert_manager/defaults/main.yml
+++ b/roles/kubernetes-apps/ingress_controller/cert_manager/defaults/main.yml
@@ -1,2 +1,3 @@
 ---
 cert_manager_namespace: "cert-manager"
+cert_manager_user: 1001
diff --git a/roles/kubernetes-apps/ingress_controller/cert_manager/templates/deploy-cert-manager.yml.j2 b/roles/kubernetes-apps/ingress_controller/cert_manager/templates/deploy-cert-manager.yml.j2
index 0221be5627c2616a7f9f70250b99cad42a402cc1..2bcf5c701e9586d448189c22d2a02954eba9c5c3 100644
--- a/roles/kubernetes-apps/ingress_controller/cert_manager/templates/deploy-cert-manager.yml.j2
+++ b/roles/kubernetes-apps/ingress_controller/cert_manager/templates/deploy-cert-manager.yml.j2
@@ -39,3 +39,5 @@ spec:
             requests:
               cpu: 10m
               memory: 32Mi
+          securityContext:
+            runAsUser: {{ cert_manager_user }}
diff --git a/roles/kubernetes-apps/ingress_controller/ingress_nginx/tasks/main.yml b/roles/kubernetes-apps/ingress_controller/ingress_nginx/tasks/main.yml
index eff3c7ed8ad2acc3f196ec112aef6259e18a6ec8..8db7d2972151893372629844a585778c8b1e3f8d 100644
--- a/roles/kubernetes-apps/ingress_controller/ingress_nginx/tasks/main.yml
+++ b/roles/kubernetes-apps/ingress_controller/ingress_nginx/tasks/main.yml
@@ -28,23 +28,34 @@
   when:
     - inventory_hostname == groups['kube-master'][0]
 
+- name: NGINX Ingress Controller | Templates list
+  set_fact:
+    ingress_nginx_templates:
+      - { name: 00-namespace, file: 00-namespace.yml, type: ns }
+      - { name: deploy-default-backend, file: deploy-default-backend.yml, type: deploy }
+      - { name: svc-default-backend, file: svc-default-backend.yml, type: svc }
+      - { name: cm-ingress-nginx, file: cm-ingress-nginx.yml, type: cm }
+      - { name: cm-tcp-services, file: cm-tcp-services.yml, type: cm }
+      - { name: cm-udp-services, file: cm-udp-services.yml, type: cm }
+      - { name: sa-ingress-nginx, file: sa-ingress-nginx.yml, type: sa }
+      - { name: clusterrole-ingress-nginx, file: clusterrole-ingress-nginx.yml, type: clusterrole }
+      - { name: clusterrolebinding-ingress-nginx, file: clusterrolebinding-ingress-nginx.yml, type: clusterrolebinding }
+      - { name: role-ingress-nginx, file: role-ingress-nginx.yml, type: role }
+      - { name: rolebinding-ingress-nginx, file: rolebinding-ingress-nginx.yml, type: rolebinding }
+      - { name: ds-ingress-nginx-controller, file: ds-ingress-nginx-controller.yml, type: ds }
+    ingress_nginx_templates_for_psp:
+      - { name: psp-ingress-nginx, file: psp-ingress-nginx.yml, type: podsecuritypolicy }
+
+- name: NGINX Ingress Controller | Append extra templates to NGINX Ingress Templates list for PodSecurityPolicy
+  set_fact:
+    ingress_nginx_templates: "{{ ingress_nginx_templates_for_psp + ingress_nginx_templates }}"
+  when: podsecuritypolicy_enabled
+
 - name: NGINX Ingress Controller | Create manifests
   template:
     src: "{{ item.file }}.j2"
     dest: "{{ kube_config_dir }}/addons/ingress_nginx/{{ item.file }}"
-  with_items:
-    - { name: 00-namespace, file: 00-namespace.yml, type: ns }
-    - { name: deploy-default-backend, file: deploy-default-backend.yml, type: deploy }
-    - { name: svc-default-backend, file: svc-default-backend.yml, type: svc }
-    - { name: cm-ingress-nginx, file: cm-ingress-nginx.yml, type: cm }
-    - { name: cm-tcp-services, file: cm-tcp-services.yml, type: cm }
-    - { name: cm-udp-services, file: cm-udp-services.yml, type: cm }
-    - { name: sa-ingress-nginx, file: sa-ingress-nginx.yml, type: sa }
-    - { name: clusterrole-ingress-nginx, file: clusterrole-ingress-nginx.yml, type: clusterrole }
-    - { name: clusterrolebinding-ingress-nginx, file: clusterrolebinding-ingress-nginx.yml, type: clusterrolebinding }
-    - { name: role-ingress-nginx, file: role-ingress-nginx.yml, type: role }
-    - { name: rolebinding-ingress-nginx, file: rolebinding-ingress-nginx.yml, type: rolebinding }
-    - { name: ds-ingress-nginx-controller, file: ds-ingress-nginx-controller.yml, type: ds }
+  with_items: "{{ ingress_nginx_templates }}"
   register: ingress_nginx_manifests
   when:
     - inventory_hostname == groups['kube-master'][0]
diff --git a/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/psp-ingress-nginx.yml.j2 b/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/psp-ingress-nginx.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..0eac6aa2c9d835ea0ecfed2ae8b446cbb99c8066
--- /dev/null
+++ b/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/psp-ingress-nginx.yml.j2
@@ -0,0 +1,48 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: ingress-nginx
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: false
+  allowPrivilegeEscalation: true
+  allowedCapabilities:
+    - NET_BIND_SERVICE
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'projected'
+    - 'secret'
+    - 'downwardAPI'
+    - 'persistentVolumeClaim'
+  hostNetwork: {{ ingress_nginx_host_network|bool }}
+  hostPorts:
+  - min: 0
+    max: 65535
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'MustRunAsNonRoot'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/role-ingress-nginx.yml.j2 b/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/role-ingress-nginx.yml.j2
index 9254e035a26c681a9e0f652a7fa32fe9bfe02d79..1f436ba7d28a7cc14d70ee4d7528a00f41b345a8 100644
--- a/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/role-ingress-nginx.yml.j2
+++ b/roles/kubernetes-apps/ingress_controller/ingress_nginx/templates/role-ingress-nginx.yml.j2
@@ -22,3 +22,11 @@ rules:
   - apiGroups: [""]
     resources: ["endpoints"]
     verbs: ["get"]
+  - apiGroups:
+    - policy
+    resourceNames:
+    - ingress-nginx
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/kubernetes-apps/registry/tasks/main.yml b/roles/kubernetes-apps/registry/tasks/main.yml
index a175064ee3475daa5acbb9d351904563e862c7f5..6272ef5fba3ac9b95d64f34624309e97ef240805 100644
--- a/roles/kubernetes-apps/registry/tasks/main.yml
+++ b/roles/kubernetes-apps/registry/tasks/main.yml
@@ -8,15 +8,35 @@
     group: root
     mode: 0755
 
+- name: Registry | Templates list
+  set_fact:
+    registry_templates:
+      - { name: registry-ns, file: registry-ns.yml, type: ns }
+      - { name: registry-sa, file: registry-sa.yml, type: sa }
+      - { name: registry-proxy-sa, file: registry-proxy-sa.yml, type: sa }
+      - { name: registry-svc, file: registry-svc.yml, type: svc }
+      - { name: registry-rs, file: registry-rs.yml, type: rs }
+      - { name: registry-proxy-ds, file: registry-proxy-ds.yml, type: ds }
+    registry_templates_for_psp:
+      - { name: registry-psp, file: registry-psp.yml, type: psp }
+      - { name: registry-cr, file: registry-cr.yml, type: clusterrole }
+      - { name: registry-crb, file: registry-crb.yml, type: rolebinding }
+      - { name: registry-proxy-psp, file: registry-proxy-psp.yml, type: psp }
+      - { name: registry-proxy-cr, file: registry-proxy-cr.yml, type: clusterrole }
+      - { name: registry-proxy-crb, file: registry-proxy-crb.yml, type: rolebinding }
+
+- name: Registry | Append extra templates to Registry Templates list for PodSecurityPolicy
+  set_fact:
+    registry_templates: "{{ registry_templates[:3] + registry_templates_for_psp + registry_templates[4:] }}"
+  when:
+    - podsecuritypolicy_enabled
+    - registry_namespace != "kube-system"
+
 - name: Registry | Create manifests
   template:
     src: "{{ item.file }}.j2"
     dest: "{{ kube_config_dir }}/addons/registry/{{ item.file }}"
-  with_items:
-    - { name: registry-ns, file: registry-ns.yml, type: ns }
-    - { name: registry-svc, file: registry-svc.yml, type: svc }
-    - { name: registry-rs, file: registry-rs.yml, type: rs }
-    - { name: registry-proxy-ds, file: registry-proxy-ds.yml, type: ds }
+  with_items: "{{ registry_templates }}"
   register: registry_manifests
   when: inventory_hostname == groups['kube-master'][0]
 
diff --git a/roles/kubernetes-apps/registry/templates/registry-cr.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-cr.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..27b2f316edef906484cce7c435accb2b71c10dc3
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-cr.yml.j2
@@ -0,0 +1,15 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+  name: psp:registry
+  namespace: {{ registry_namespace }}
+rules:
+  - apiGroups:
+    - policy
+    resourceNames:
+    - registry
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/kubernetes-apps/registry/templates/registry-crb.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-crb.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..c9d14813f631ca8240ea740bfbf9f4f9b232e985
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-crb.yml.j2
@@ -0,0 +1,13 @@
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: psp:registry
+  namespace: {{ registry_namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: registry
+    namespace: {{ registry_namespace }}
+roleRef:
+  kind: ClusterRole
+  name: psp:registry
+  apiGroup: rbac.authorization.k8s.io
diff --git a/roles/kubernetes-apps/registry/templates/registry-proxy-cr.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-proxy-cr.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..5a28b076dd525b0b2a828b6f1a2ced7964169683
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-proxy-cr.yml.j2
@@ -0,0 +1,15 @@
+---
+apiVersion: rbac.authorization.k8s.io/v1beta1
+kind: ClusterRole
+metadata:
+  name: psp:registry-proxy
+  namespace: {{ registry_namespace }}
+rules:
+  - apiGroups:
+    - policy
+    resourceNames:
+    - registry-proxy
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/kubernetes-apps/registry/templates/registry-proxy-crb.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-proxy-crb.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..db6ce0c95b5d872ff0183c51d869e0f80c7bb7e8
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-proxy-crb.yml.j2
@@ -0,0 +1,13 @@
+kind: RoleBinding
+apiVersion: rbac.authorization.k8s.io/v1beta1
+metadata:
+  name: psp:registry-proxy
+  namespace: {{ registry_namespace }}
+subjects:
+  - kind: ServiceAccount
+    name: registry-proxy
+    namespace: {{ registry_namespace }}
+roleRef:
+  kind: ClusterRole
+  name: psp:registry-proxy
+  apiGroup: rbac.authorization.k8s.io
diff --git a/roles/kubernetes-apps/registry/templates/registry-proxy-ds.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-proxy-ds.yml.j2
index 84bf1cf5a98f59ae9a8abe9e0d7ff493b5b47a2c..0a04c40d13449e3d22e62f8ef68e7914176e8348 100644
--- a/roles/kubernetes-apps/registry/templates/registry-proxy-ds.yml.j2
+++ b/roles/kubernetes-apps/registry/templates/registry-proxy-ds.yml.j2
@@ -21,6 +21,7 @@ spec:
         kubernetes.io/cluster-service: "true"
         version: v{{ registry_proxy_image_tag }}
     spec:
+      serviceAccountName: registry-proxy
       containers:
         - name: registry-proxy
           image: {{ registry_proxy_image_repo }}:{{ registry_proxy_image_tag }}
diff --git a/roles/kubernetes-apps/registry/templates/registry-proxy-psp.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-proxy-psp.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..e73711a95783bb447ef2c03324780222247bf212
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-proxy-psp.yml.j2
@@ -0,0 +1,48 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: registry-proxy
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: false
+  allowPrivilegeEscalation: false
+  requiredDropCapabilities:
+    - ALL
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'projected'
+    - 'secret'
+    - 'downwardAPI'
+    - 'persistentVolumeClaim'
+  hostNetwork: true
+  hostPorts:
+  - min: 5000
+    max: 5000
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'RunAsAny'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/registry/templates/registry-proxy-sa.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-proxy-sa.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..0c18fa22769321d3a53518aada7bfa8ab6106422
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-proxy-sa.yml.j2
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: registry-proxy
+  namespace: {{ registry_namespace }}
+  labels:
+    kubernetes.io/cluster-service: "true"
diff --git a/roles/kubernetes-apps/registry/templates/registry-psp.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-psp.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..512f8a4e7be89320938a708d4e7c872f29ce2c5e
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-psp.yml.j2
@@ -0,0 +1,45 @@
+---
+apiVersion: policy/v1beta1
+kind: PodSecurityPolicy
+metadata:
+  name: registry
+  annotations:
+    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
+    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
+{% if apparmor_enabled %}
+    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
+    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
+{% endif %}
+  labels:
+    kubernetes.io/cluster-service: 'true'
+    addonmanager.kubernetes.io/mode: Reconcile
+spec:
+  privileged: false
+  allowPrivilegeEscalation: false
+  requiredDropCapabilities:
+    - ALL
+  volumes:
+    - 'configMap'
+    - 'emptyDir'
+    - 'projected'
+    - 'secret'
+    - 'downwardAPI'
+    - 'persistentVolumeClaim'
+  hostNetwork: false
+  hostIPC: false
+  hostPID: false
+  runAsUser:
+    rule: 'RunAsAny'
+  seLinux:
+    rule: 'RunAsAny'
+  supplementalGroups:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  fsGroup:
+    rule: 'MustRunAs'
+    ranges:
+      - min: 1
+        max: 65535
+  readOnlyRootFilesystem: false
diff --git a/roles/kubernetes-apps/registry/templates/registry-rs.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-rs.yml.j2
index 730ce272bf235b5a6111b2cd83d71489bf02f978..3ca8e217d3d1f3b4b00e4e78625a0c00ea054a6f 100644
--- a/roles/kubernetes-apps/registry/templates/registry-rs.yml.j2
+++ b/roles/kubernetes-apps/registry/templates/registry-rs.yml.j2
@@ -22,6 +22,7 @@ spec:
         version: v{{ registry_image_tag }}
         kubernetes.io/cluster-service: "true"
     spec:
+      serviceAccountName: registry
       containers:
         - name: registry
           image: {{ registry_image_repo }}:{{ registry_image_tag }}
diff --git a/roles/kubernetes-apps/registry/templates/registry-sa.yml.j2 b/roles/kubernetes-apps/registry/templates/registry-sa.yml.j2
new file mode 100644
index 0000000000000000000000000000000000000000..b9e48b8e13969370804abd820b2ab1b57410e300
--- /dev/null
+++ b/roles/kubernetes-apps/registry/templates/registry-sa.yml.j2
@@ -0,0 +1,7 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: registry
+  namespace: {{ registry_namespace }}
+  labels:
+    kubernetes.io/cluster-service: "true"
diff --git a/roles/kubernetes/master/defaults/main.yml b/roles/kubernetes/master/defaults/main.yml
index bcf780d7e3e032447b8f6679c2f905874fd7ca7b..a32f805cb640739fec511f7053096d4823279ce0 100644
--- a/roles/kubernetes/master/defaults/main.yml
+++ b/roles/kubernetes/master/defaults/main.yml
@@ -32,7 +32,7 @@ audit_log_path: /var/log/audit/kube-apiserver-audit.log
 audit_log_maxage: 30
 # the num of audit logs to retain
 audit_log_maxbackups: 1
- # the max size in MB to retain
+# the max size in MB to retain
 audit_log_maxsize: 100
 # policy file
 audit_policy_file: "{{ kube_config_dir }}/audit-policy/apiserver-audit-policy.yaml"
diff --git a/roles/kubernetes/master/tasks/main.yml b/roles/kubernetes/master/tasks/main.yml
index 66bf261e53213e33ac7381ac2b2a6f9782ef4dd4..be2044e312009b35eb1e8d50282a1066ef123c1d 100644
--- a/roles/kubernetes/master/tasks/main.yml
+++ b/roles/kubernetes/master/tasks/main.yml
@@ -52,6 +52,12 @@
     - kubectl
     - upgrade
 
+- name: Disable SecurityContextDeny admission-controller and enable PodSecurityPolicy
+  set_fact:
+    kube_apiserver_admission_control: "{{ kube_apiserver_admission_control | default([]) | difference(['SecurityContextDeny']) | union(['PodSecurityPolicy']) | unique }}"
+    kube_apiserver_enable_admission_plugins: "{{ kube_apiserver_enable_admission_plugins | difference(['SecurityContextDeny']) | union(['PodSecurityPolicy']) | unique }}"
+  when: podsecuritypolicy_enabled
+
 - name: Include kubeadm setup if enabled
   import_tasks: kubeadm-setup.yml
   when: kubeadm_enabled|bool|default(false)
diff --git a/roles/kubespray-defaults/defaults/main.yaml b/roles/kubespray-defaults/defaults/main.yaml
index 9f7cf5e11eb04316c9cca738521d199d9f3aca93..888cbc3c8c768e557b5ffacdfd23108f85f12202 100644
--- a/roles/kubespray-defaults/defaults/main.yaml
+++ b/roles/kubespray-defaults/defaults/main.yaml
@@ -363,3 +363,5 @@ etcd_events_peer_addresses: |-
   {% for item in groups['etcd'] -%}
     {{ "etcd"+loop.index|string }}-events=https://{{ hostvars[item].access_ip | default(hostvars[item].ip | default(hostvars[item].ansible_default_ipv4['address'])) }}:2382{% if not loop.last %},{% endif %}
   {%- endfor %}
+
+podsecuritypolicy_enabled: false
diff --git a/roles/network_plugin/calico/templates/calico-cr.yml.j2 b/roles/network_plugin/calico/templates/calico-cr.yml.j2
index cef8331f39dbf6fd2e2d4a5794a63f64bc0d0108..41d4f2b0610d993365d28e111c12cfcbc27e7c20 100644
--- a/roles/network_plugin/calico/templates/calico-cr.yml.j2
+++ b/roles/network_plugin/calico/templates/calico-cr.yml.j2
@@ -11,3 +11,11 @@ rules:
       - nodes
     verbs:
       - get
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/network_plugin/canal/templates/canal-cr-calico.yml.j2 b/roles/network_plugin/canal/templates/canal-cr-calico.yml.j2
index 2e92b7b2b9cb2db3e5137acea8fa7f0e0ae2b100..0b5ebf094a0ccdaee95388009d782060ad5376fc 100644
--- a/roles/network_plugin/canal/templates/canal-cr-calico.yml.j2
+++ b/roles/network_plugin/canal/templates/canal-cr-calico.yml.j2
@@ -78,3 +78,11 @@ rules:
     verbs:
       - get
       - list
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/network_plugin/canal/templates/canal-cr-flannel.yml.j2 b/roles/network_plugin/canal/templates/canal-cr-flannel.yml.j2
index 0be8e938cb91018acd78d067f4147708149c825b..45f6fcb502421d12b5d3fd7951bb42946aa4fa31 100644
--- a/roles/network_plugin/canal/templates/canal-cr-flannel.yml.j2
+++ b/roles/network_plugin/canal/templates/canal-cr-flannel.yml.j2
@@ -24,3 +24,11 @@ rules:
       - nodes/status
     verbs:
       - patch
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/network_plugin/cilium/templates/cilium-cr.yml.j2 b/roles/network_plugin/cilium/templates/cilium-cr.yml.j2
index 2e5efff867ec2e54233661dae3f408d78138843a..60cdb5b6e4cf871eb1200d8bf8f7e123547c0aa0 100755
--- a/roles/network_plugin/cilium/templates/cilium-cr.yml.j2
+++ b/roles/network_plugin/cilium/templates/cilium-cr.yml.j2
@@ -64,3 +64,11 @@ rules:
       - ciliumendpoints/status
     verbs:
       - "*"
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/network_plugin/contiv/templates/contiv-netmaster-clusterrole.yml.j2 b/roles/network_plugin/contiv/templates/contiv-netmaster-clusterrole.yml.j2
index 6ccd4f9b49479985c31796d731aff975a8ee7ab9..92b4f588d4de04ff00da773b984421beb4137be9 100644
--- a/roles/network_plugin/contiv/templates/contiv-netmaster-clusterrole.yml.j2
+++ b/roles/network_plugin/contiv/templates/contiv-netmaster-clusterrole.yml.j2
@@ -16,3 +16,11 @@ rules:
       - watch
       - list
       - update
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/network_plugin/contiv/templates/contiv-netplugin-clusterrole.yml.j2 b/roles/network_plugin/contiv/templates/contiv-netplugin-clusterrole.yml.j2
index af4c6e584829459402a44f3e44b71855d5568380..e01fbef5d50ccdd4ebf9c34388b0c6b94651b27a 100644
--- a/roles/network_plugin/contiv/templates/contiv-netplugin-clusterrole.yml.j2
+++ b/roles/network_plugin/contiv/templates/contiv-netplugin-clusterrole.yml.j2
@@ -19,3 +19,11 @@ rules:
       - list
       - update
       - get
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
diff --git a/roles/network_plugin/flannel/templates/cni-flannel-rbac.yml.j2 b/roles/network_plugin/flannel/templates/cni-flannel-rbac.yml.j2
index 6f5c9a2114c76c116ad074ac7718ef02241973b9..873d0e3f5a8a94dc2a25cd11592b34fcbcf4a215 100644
--- a/roles/network_plugin/flannel/templates/cni-flannel-rbac.yml.j2
+++ b/roles/network_plugin/flannel/templates/cni-flannel-rbac.yml.j2
@@ -29,6 +29,14 @@ rules:
       - nodes/status
     verbs:
       - patch
+  - apiGroups:
+    - policy
+    resourceNames:
+    - privileged
+    resources:
+    - podsecuritypolicies
+    verbs:
+    - use
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1beta1
diff --git a/roles/network_plugin/weave/templates/weave-net.yml.j2 b/roles/network_plugin/weave/templates/weave-net.yml.j2
index 1995b6677af56e9b0c5fb5986d928bf72b0a9e38..09e5fbb7c7c05c936dccdc4a8faaba51f2d33801 100644
--- a/roles/network_plugin/weave/templates/weave-net.yml.j2
+++ b/roles/network_plugin/weave/templates/weave-net.yml.j2
@@ -41,6 +41,14 @@ items:
         verbs:
           - patch
           - update
+      - apiGroups:
+        - policy
+        resourceNames:
+        - privileged
+        resources:
+        - podsecuritypolicies
+        verbs:
+        - use
   - apiVersion: rbac.authorization.k8s.io/v1beta1
     kind: ClusterRoleBinding
     metadata: