diff --git a/charts/kyverno/README.md b/charts/kyverno/README.md index 94746e99fb..6bdb29b261 100644 --- a/charts/kyverno/README.md +++ b/charts/kyverno/README.md @@ -99,7 +99,7 @@ Parameter | Description | Default `service.type` | type of service | `ClusterIP` `tolerations` | list of node taints to tolerate | `[]` `securityContext` | security context configuration | `{}` - +`podSecurityStandard` | set desired pod security level `privileged`, `default`, `restricted`. Set to `restricted` for maximum security for your cluster. See: https://kyverno.io/policies/pod-security/ | `default` Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, diff --git a/charts/kyverno/templates/policies/default/disallow-adding-capabilities.yaml b/charts/kyverno/templates/policies/default/disallow-adding-capabilities.yaml new file mode 100644 index 0000000000..0127a8e0a9 --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-adding-capabilities.yaml @@ -0,0 +1,35 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-add-capabilities + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + Capabilities permit privileged actions without giving full root access. + Adding capabilities beyond the default set must not be allowed. +spec: + validationFailureAction: audit + background: true + rules: + - name: capabilities + match: + resources: + kinds: + - Pod + validate: + message: >- + Adding of additional capabilities beyond the default set is not allowed. + The fields spec.containers[*].securityContext.capabilities.add and + spec.initContainers[*].securityContext.capabilities.add must be empty. + pattern: + spec: + containers: + - =(securityContext): + =(capabilities): + X(add): null + =(initContainers): + - =(securityContext): + =(capabilities): + X(add): null +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/disallow-host-namespaces.yaml b/charts/kyverno/templates/policies/default/disallow-host-namespaces.yaml new file mode 100644 index 0000000000..79b0861917 --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-host-namespaces.yaml @@ -0,0 +1,30 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-host-namespaces + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + Host namespaces (Process ID namespace, Inter-Process Communication namespace, and + network namespace) allow access to shared information and can be used to elevate + privileges. Pods should not be allowed access to host namespaces. +spec: + validationFailureAction: audit + background: true + rules: + - name: host-namespaces + match: + resources: + kinds: + - Pod + validate: + message: >- + Sharing the host namespaces is disallowed. The fields spec.hostNetwork, + spec.hostIPC, and spec.hostPID must not be set to true. + pattern: + spec: + =(hostPID): "false" + =(hostIPC): "false" + =(hostNetwork): "false" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/disallow-host-path.yaml b/charts/kyverno/templates/policies/default/disallow-host-path.yaml new file mode 100644 index 0000000000..92cec7443f --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-host-path.yaml @@ -0,0 +1,28 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-host-path + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + HostPath volumes let pods use host directories and volumes in containers. + Using host resources can be used to access shared data or escalate privileges + and should not be allowed. +spec: + validationFailureAction: audit + background: true + rules: + - name: host-path + match: + resources: + kinds: + - Pod + validate: + message: >- + HostPath volumes are forbidden. The fields spec.volumes[*].hostPath must not be set. + pattern: + spec: + =(volumes): + - X(hostPath): "null" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/disallow-host-ports.yaml b/charts/kyverno/templates/policies/default/disallow-host-ports.yaml new file mode 100644 index 0000000000..dabcb2730e --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-host-ports.yaml @@ -0,0 +1,32 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-host-ports + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + Access to host ports allows potential snooping of network traffic and should not be + allowed, or at minimum restricted to a known list. +spec: + validationFailureAction: audit + background: true + rules: + - name: host-ports + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of host ports is disallowed. The fields spec.containers[*].ports[*].hostPort + and spec.initContainers[*].ports[*].hostPort must be empty. + pattern: + spec: + =(initContainers): + - =(ports): + - X(hostPort): 0 + containers: + - =(ports): + - X(hostPort): 0 +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/disallow-privileged-containers.yaml b/charts/kyverno/templates/policies/default/disallow-privileged-containers.yaml new file mode 100644 index 0000000000..96545eacd6 --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-privileged-containers.yaml @@ -0,0 +1,31 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-privileged-containers + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + Privileged mode disables most security mechanisms and must not be allowed. +spec: + validationFailureAction: audit + background: true + rules: + - name: priviledged-containers + match: + resources: + kinds: + - Pod + validate: + message: >- + Privileged mode is disallowed. The fields spec.containers[*].securityContext.privileged + and spec.initContainers[*].securityContext.privileged must not be set to true. + pattern: + spec: + =(initContainers): + - =(securityContext): + =(privileged): "false" + containers: + - =(securityContext): + =(privileged): "false" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/disallow-proc-mount.yaml b/charts/kyverno/templates/policies/default/disallow-proc-mount.yaml new file mode 100644 index 0000000000..142c328405 --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-proc-mount.yaml @@ -0,0 +1,33 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-default-proc-mount + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + The default /proc masks are set up to reduce attack surface and should be required. +spec: + validationFailureAction: audit + background: true + rules: + - name: check-proc-mount + match: + resources: + kinds: + - Pod + validate: + message: >- + Changing the proc mount from the default is not allowed. The fields + spec.containers[*].securityContext.procMount and + spec.initContainers[*].securityContext.procMount must not be changed + from `Default`. + pattern: + spec: + =(initContainers): + - =(securityContext): + =(procMount): "Default" + containers: + - =(securityContext): + =(procMount): "Default" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/disallow-selinux.yaml b/charts/kyverno/templates/policies/default/disallow-selinux.yaml new file mode 100644 index 0000000000..d288481ce8 --- /dev/null +++ b/charts/kyverno/templates/policies/default/disallow-selinux.yaml @@ -0,0 +1,35 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: disallow-selinux + annotations: + policies.kyverno.io/title: Disallow SELinux + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + SELinux options can be used to escalate privileges and should not be allowed. +spec: + validationFailureAction: audit + background: true + rules: + - name: seLinux + match: + resources: + kinds: + - Pod + validate: + message: >- + Setting custom SELinux options is disallowed. The fields + spec.securityContext.seLinuxOptions, spec.containers[*].securityContext.seLinuxOptions, + and spec.initContainers[*].securityContext.seLinuxOptions must be empty. + pattern: + spec: + =(securityContext): + X(seLinuxOptions): "null" + =(initContainers): + - =(securityContext): + X(seLinuxOptions): "null" + containers: + - =(securityContext): + X(seLinuxOptions): "null" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/restrict-apparmor-profiles.yaml b/charts/kyverno/templates/policies/default/restrict-apparmor-profiles.yaml new file mode 100644 index 0000000000..f8c6389702 --- /dev/null +++ b/charts/kyverno/templates/policies/default/restrict-apparmor-profiles.yaml @@ -0,0 +1,31 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: restrict-apparmor-profiles + annotations: + policies.kyverno.io/title: Restrict AppArmor + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + On supported hosts, the 'runtime/default' AppArmor profile is applied by default. + The default policy should prevent overriding or disabling the policy, or restrict + overrides to an allowed set of profiles. +spec: + validationFailureAction: audit + background: true + rules: + - name: app-armor + match: + resources: + kinds: + - Pod + validate: + message: >- + Specifying other AppArmor profiles is disallowed. The annotation + container.apparmor.security.beta.kubernetes.io must not be defined, + or must not be set to anything other than `runtime/default`. + pattern: + metadata: + =(annotations): + =(container.apparmor.security.beta.kubernetes.io/*): "runtime/default" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/default/restrict-sysctls.yaml b/charts/kyverno/templates/policies/default/restrict-sysctls.yaml new file mode 100644 index 0000000000..2d5589a399 --- /dev/null +++ b/charts/kyverno/templates/policies/default/restrict-sysctls.yaml @@ -0,0 +1,34 @@ +{{- if eq .Values.podSecurityStandard "default" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: restrict-sysctls + annotations: + policies.kyverno.io/category: Pod Security Standards (Default) + policies.kyverno.io/description: >- + Sysctls can disable security mechanisms or affect all containers on a + host, and should be disallowed except for an allowed "safe" subset. A + sysctl is considered safe if it is namespaced in the container or the + Pod, and it is isolated from other Pods or processes on the same Node. +spec: + validationFailureAction: audit + background: true + rules: + - name: sysctls + match: + resources: + kinds: + - Pod + validate: + message: >- + Setting additional sysctls above the allowed type is disallowed. + The field spec.securityContext.sysctls must not use any other names + than 'kernel.shm_rmid_forced', 'net.ipv4.ip_local_port_range', + 'net.ipv4.tcp_syncookies' and 'net.ipv4.ping_group_range'. + pattern: + spec: + =(securityContext): + =(sysctls): + - name: "kernel.shm_rmid_forced | net.ipv4.ip_local_port_range | net.ipv4.tcp_syncookies | net.ipv4.ping_group_range" + value: "?*" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/restricted/deny-privilege-escalation.yaml b/charts/kyverno/templates/policies/restricted/deny-privilege-escalation.yaml new file mode 100644 index 0000000000..99266c44e7 --- /dev/null +++ b/charts/kyverno/templates/policies/restricted/deny-privilege-escalation.yaml @@ -0,0 +1,33 @@ +{{- if eq .Values.podSecurityStandard "restricted" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: deny-privilege-escalation + annotations: + policies.kyverno.io/category: Pod Security Standards (Restricted) + policies.kyverno.io/description: >- + Privilege escalation, such as via set-user-ID or set-group-ID file mode, should not be allowed. +spec: + background: true + validationFailureAction: audit + rules: + - name: deny-privilege-escalation + match: + resources: + kinds: + - Pod + validate: + message: >- + Privilege escalation is disallowed. The fields + spec.containers[*].securityContext.allowPrivilegeEscalation, and + spec.initContainers[*].securityContext.allowPrivilegeEscalation must + be undefined or set to `false`. + pattern: + spec: + =(initContainers): + - =(securityContext): + =(allowPrivilegeEscalation): "false" + containers: + - =(securityContext): + =(allowPrivilegeEscalation): "false" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/restricted/require-non-root-groups.yaml b/charts/kyverno/templates/policies/restricted/require-non-root-groups.yaml new file mode 100644 index 0000000000..f7c53bf607 --- /dev/null +++ b/charts/kyverno/templates/policies/restricted/require-non-root-groups.yaml @@ -0,0 +1,61 @@ +{{- if eq .Values.podSecurityStandard "restricted" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-non-root-groups + annotations: + policies.kyverno.io/category: Pod Security Standards (Restricted) + policies.kyverno.io/description: >- + Containers should be forbidden from running with a root primary or supplementary GID. +spec: + background: true + validationFailureAction: audit + rules: + - name: check-runasgroup + match: + resources: + kinds: + - Pod + validate: + message: >- + Running with root group IDs is disallowed. The fields + spec.securityContext.runAsGroup, spec.containers[*].securityContext.runAsGroup, + and spec.initContainers[*].securityContext.runAsGroup must be empty + or greater than zero. + pattern: + spec: + =(securityContext): + =(runAsGroup): ">0" + =(initContainers): + - =(securityContext): + =(runAsGroup): ">0" + containers: + - =(securityContext): + =(runAsGroup): ">0" + - name: check-supplementalGroups + match: + resources: + kinds: + - Pod + validate: + message: >- + Adding of supplemental group IDs is not allowed. The field + spec.securityContext.supplementalGroups must not be defined. + pattern: + spec: + =(securityContext): + =(supplementalGroups): ["null"] + - name: check-fsGroup + match: + resources: + kinds: + - Pod + validate: + message: >- + Changing of file system groups is not allowed. The field + spec.securityContext.fsGroup must not be defined. + pattern: + spec: + =(securityContext): + X(fsGroup): "*" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/restricted/require-run-as-nonroot.yaml b/charts/kyverno/templates/policies/restricted/require-run-as-nonroot.yaml new file mode 100644 index 0000000000..21b870cdec --- /dev/null +++ b/charts/kyverno/templates/policies/restricted/require-run-as-nonroot.yaml @@ -0,0 +1,40 @@ +{{- if eq .Values.podSecurityStandard "restricted" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: require-run-as-non-root + annotations: + policies.kyverno.io/category: Pod Security Standards (Restricted) + policies.kyverno.io/description: Containers must be required to run as non-root users. +spec: + background: true + validationFailureAction: audit + rules: + - name: check-containers + match: + resources: + kinds: + - Pod + validate: + message: >- + Running as root is not allowed. The fields spec.securityContext.runAsNonRoot, + spec.containers[*].securityContext.runAsNonRoot, and + spec.initContainers[*].securityContext.runAsNonRoot must be `true`. + anyPattern: + - spec: + securityContext: + runAsNonRoot: true + containers: + - =(securityContext): + =(runAsNonRoot): true + =(initContainers): + - =(securityContext): + =(runAsNonRoot): true + - spec: + containers: + - securityContext: + runAsNonRoot: true + =(initContainers): + - securityContext: + runAsNonRoot: true +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/restricted/restrict-seccomp.yaml b/charts/kyverno/templates/policies/restricted/restrict-seccomp.yaml new file mode 100644 index 0000000000..9af9849bd9 --- /dev/null +++ b/charts/kyverno/templates/policies/restricted/restrict-seccomp.yaml @@ -0,0 +1,41 @@ +{{- if eq .Values.podSecurityStandard "restricted" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: restrict-seccomp + annotations: + policies.kyverno.io/title: Restrict Seccomp + policies.kyverno.io/category: Pod Security Standards (Restricted) + policies.kyverno.io/description: >- + The runtime default seccomp profile must be required, or only specific + additional profiles should be allowed. +spec: + background: true + validationFailureAction: audit + rules: + - name: seccomp + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of custom Seccomp profiles is disallowed. The fields + spec.securityContext.seccompProfile.type, + spec.containers[*].securityContext.seccompProfile.type, and + spec.initContainers[*].securityContext.seccompProfile.type + must be unset or set to `runtime/default`. + pattern: + spec: + =(securityContext): + =(seccompProfile): + =(type): "runtime/default" + =(initContainers): + - =(securityContext): + =(seccompProfile): + =(type): "runtime/default" + containers: + - =(securityContext): + =(seccompProfile): + =(type): "runtime/default" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/templates/policies/restricted/restrict-volume-types.yaml b/charts/kyverno/templates/policies/restricted/restrict-volume-types.yaml new file mode 100644 index 0000000000..2c6b80d74e --- /dev/null +++ b/charts/kyverno/templates/policies/restricted/restrict-volume-types.yaml @@ -0,0 +1,275 @@ +{{- if eq .Values.podSecurityStandard "restricted" }} +apiVersion: kyverno.io/v1 +kind: ClusterPolicy +metadata: + name: restrict-volume-types + annotations: + policies.kyverno.io/category: Pod Security Standards (Restricted) + policies.kyverno.io/description: >- + In addition to restricting HostPath volumes, the restricted pod security profile + limits usage of non-core volume types to those defined through PersistentVolumes. +spec: + background: true + validationFailureAction: audit + rules: + - name: restricted-vol-gcePersistentDisk + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the gcePersistentDisk type volume is disallowed. + The fields spec.volumes[*].gcePersistentDisk must not be set. + pattern: + spec: + =(volumes): + - X(gcePersistentDisk): "null" + - name: restricted-vol-awsElasticBlockStore + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the awsElasticBlockStore type volume is disallowed. + The fields spec.volumes[*].awsElasticBlockStore must not be set. + pattern: + spec: + =(volumes): + - X(awsElasticBlockStore): "null" + - name: restricted-vol-gitRepo + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the gitRepo type volume is disallowed. + The fields spec.volumes[*].gitRepo must not be set. + pattern: + spec: + =(volumes): + - X(gitRepo): "null" + - name: restricted-vol-nfs + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the nfs type volume is disallowed. + The fields spec.volumes[*].nfs must not be set. + pattern: + spec: + =(volumes): + - X(nfs): "null" + - name: restricted-vol-iscsi + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the iscsi type volume is disallowed. + The fields spec.volumes[*].iscsi must not be set. + pattern: + spec: + =(volumes): + - X(iscsi): "null" + - name: restricted-vol-glusterfs + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the glusterfs type volume is disallowed. + The fields spec.volumes[*].glusterfs must not be set. + pattern: + spec: + =(volumes): + - X(glusterfs): "null" + - name: restricted-vol-rbd + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the rbd type volume is disallowed. + The fields spec.volumes[*].rbd must not be set. + pattern: + spec: + =(volumes): + - X(rbd): "null" + - name: restricted-vol-flexVolume + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the flexVolume type volume is disallowed. + The fields spec.volumes[*].flexVolume must not be set. + pattern: + spec: + =(volumes): + - X(flexVolume): "null" + - name: restricted-vol-cinder + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the cinder type volume is disallowed. + The fields spec.volumes[*].cinder must not be set. + pattern: + spec: + =(volumes): + - X(cinder): "null" + - name: restricted-vol-cephfs + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the cephfs type volume is disallowed. + The fields spec.volumes[*].cephfs must not be set. + pattern: + spec: + =(volumes): + - X(cephfs): "null" + - name: restricted-vol-flocker + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the flocker type volume is disallowed. + The fields spec.volumes[*].flocker must not be set. + pattern: + spec: + =(volumes): + - X(flocker): "null" + - name: restricted-vol-fc + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the fc type volume is disallowed. + The fields spec.volumes[*].fc must not be set. + pattern: + spec: + =(volumes): + - X(fc): "null" + - name: restricted-vol-azureFile + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the azureFile type volume is disallowed. + The fields spec.volumes[*].azureFile must not be set. + pattern: + spec: + =(volumes): + - X(azureFile): "null" + - name: restricted-vol-vsphereVolume + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the vsphereVolume type volume is disallowed. + The fields spec.volumes[*].vsphereVolume must not be set. + pattern: + spec: + =(volumes): + - X(vsphereVolume): "null" + - name: restricted-vol-quobyte + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the quobyte type volume is disallowed. + The fields spec.volumes[*].quobyte must not be set. + pattern: + spec: + =(volumes): + - X(quobyte): "null" + - name: restricted-vol-azureDisk + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the azureDisk type volume is disallowed. + The fields spec.volumes[*].azureDisk must not be set. + pattern: + spec: + =(volumes): + - X(azureDisk): "null" + - name: restricted-vol-portworxVolume + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the portworxVolume type volume is disallowed. + The fields spec.volumes[*].portworxVolume must not be set. + pattern: + spec: + =(volumes): + - X(portworxVolume): "null" + - name: restricted-vol-scaleIO + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the scaleIO type volume is disallowed. + The fields spec.volumes[*].scaleIO must not be set. + pattern: + spec: + =(volumes): + - X(scaleIO): "null" + - name: restricted-vol-storageos + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the storageos type volume is disallowed. + The fields spec.volumes[*].storageos must not be set. + pattern: + spec: + =(volumes): + - X(storageos): "null" + - name: restricted-vol-csi + match: + resources: + kinds: + - Pod + validate: + message: >- + Use of the csi type volume is disallowed. + The fields spec.volumes[*].csi must not be set. + pattern: + spec: + =(volumes): + - X(csi): "null" +{{- end -}} \ No newline at end of file diff --git a/charts/kyverno/values.yaml b/charts/kyverno/values.yaml index 5d9a72efe7..cf0d45fcb8 100644 --- a/charts/kyverno/values.yaml +++ b/charts/kyverno/values.yaml @@ -1,6 +1,9 @@ nameOverride: fullnameOverride: namespace: +# Supported- default/restricted/privileged +# For more info- https://kyverno.io/policies/pod-security +podSecurityStandard: default rbac: create: true