diff --git a/charts/library/common-test/ci/networkpolicy-values.yaml b/charts/library/common-test/ci/networkpolicy-values.yaml new file mode 100644 index 00000000000..78483b892f3 --- /dev/null +++ b/charts/library/common-test/ci/networkpolicy-values.yaml @@ -0,0 +1,127 @@ +image: + repository: ghcr.io/truecharts/whoami + pullPolicy: IfNotPresent + tag: v1.7.1@sha256:9140a27e94fdfa538f4c7f95e4c2c6910341e21aa0e2f2703718fcfdf0cc825a + +service: + main: + ports: + main: + port: 8080 + +args: + - --port + - '8080' + +ingress: + main: + enabled: true + +probes: + liveness: + enabled: true + readiness: + enabled: true + startup: + enabled: true + + +"ixCertificateAuthorities": {} +"ixCertificates": + "1": + "CA_type_existing": false + "CA_type_intermediate": false + "CA_type_internal": false + "CSR": "" + "DN": "/C=US/O=iXsystems/CN=localhost/emailAddress=info@ixsystems.com/ST=Tennessee/L=Maryville/subjectAltName=DNS:localhost" + "cert_type": "CERTIFICATE" + "cert_type_CSR": false + "cert_type_existing": true + "cert_type_internal": false + "certificate": "-----BEGIN CERTIFICATE-----\nMIIDqjCCApKgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgDELMAkGA1UEBhMCVVMx\nEjAQBgNVBAoMCWlYc3lzdGVtczESMBAGA1UEAwwJbG9jYWxob3N0MSEwHwYJKoZI\nhvcNAQkBFhJpbmZvQGl4c3lzdGVtcy5jb20xEjAQBgNVBAgMCVRlbm5lc3NlZTES\nMBAGA1UEBwwJTWFyeXZpbGxlMB4XDTIwMDkyNTE0MDUzOFoXDTIyMTIyOTE0MDUz\nOFowgYAxCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlpWHN5c3RlbXMxEjAQBgNVBAMM\nCWxvY2FsaG9zdDEhMB8GCSqGSIb3DQEJARYSaW5mb0BpeHN5c3RlbXMuY29tMRIw\nEAYDVQQIDAlUZW5uZXNzZWUxEjAQBgNVBAcMCU1hcnl2aWxsZTCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBALpoGliii6X8DeoFdLcR7jjsfJIn3nC8f1pT\nLQ3RURHUOEyhPT3Z6TkhaHeHoj8D6kiXROhyJJq3kw5OeqGZisfpGQhkxjpxkfh9\nfAhlvhuLwCWHaMvSh1TaT+h9+eHfcx3un5CIaH8b1KYRBMH+jmKFpr7jkPNkBXLS\nMA7jKIIa8pD9R6lF4gAsbqJafCbT3R7bqkd9xp3n3j2YhqQzETU2lmu4fra3BPio\nofK47kSkguUC6mtk6VrDf2+QtCKlY0dtbF3e2ZBNWo1aj86sjCtoEmqOCMsPRLc/\nXwQcfEqHY4XfafXwqk0G0UxV2ce18xKoR/pN3MpLBZ65NzPnpn0CAwEAAaMtMCsw\nFAYDVR0RBA0wC4IJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqG\nSIb3DQEBCwUAA4IBAQBFW1R037y7wllg/gRk9p2T1stiG8iIXosblmL4Ak1YToTQ\n/0to5GY2ZYW29+rbA4SDTS5eeu2YqZ0A/fF3wey7ggzMS7KyNBOvx5QBJRw3PJGn\n+THfhXvdfkOyeUC6KWRGLgl+/zBFvgh6vFDq3jmv0NI4ehVBTBMCJn7r6577S16T\nwtgKMCooizII0Odu5HIF10gTieFIH3PQYm9JBji9iyemb9Ht3wn7fXQptfGadz/l\nWz/Dv9+a6IOr7JVJMHnqAIvPzpkav4efuVPOX1zbhjg4K5g+nRYfjr5F5upOd0Y3\nznWTUBUyI7CXRkpHtSDXfEqKgnk/8uv7GWw+hyKr\n-----END CERTIFICATE-----\n" + "certificate_path": "/etc/certificates/freenas_default.crt" + "chain": false + "chain_list": [ + "-----BEGIN CERTIFICATE-----\nMIIDqjCCApKgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgDELMAkGA1UEBhMCVVMx\nEjAQBgNVBAoMCWlYc3lzdGVtczESMBAGA1UEAwwJbG9jYWxob3N0MSEwHwYJKoZI\nhvcNAQkBFhJpbmZvQGl4c3lzdGVtcy5jb20xEjAQBgNVBAgMCVRlbm5lc3NlZTES\nMBAGA1UEBwwJTWFyeXZpbGxlMB4XDTIwMDkyNTE0MDUzOFoXDTIyMTIyOTE0MDUz\nOFowgYAxCzAJBgNVBAYTAlVTMRIwEAYDVQQKDAlpWHN5c3RlbXMxEjAQBgNVBAMM\nCWxvY2FsaG9zdDEhMB8GCSqGSIb3DQEJARYSaW5mb0BpeHN5c3RlbXMuY29tMRIw\nEAYDVQQIDAlUZW5uZXNzZWUxEjAQBgNVBAcMCU1hcnl2aWxsZTCCASIwDQYJKoZI\nhvcNAQEBBQADggEPADCCAQoCggEBALpoGliii6X8DeoFdLcR7jjsfJIn3nC8f1pT\nLQ3RURHUOEyhPT3Z6TkhaHeHoj8D6kiXROhyJJq3kw5OeqGZisfpGQhkxjpxkfh9\nfAhlvhuLwCWHaMvSh1TaT+h9+eHfcx3un5CIaH8b1KYRBMH+jmKFpr7jkPNkBXLS\nMA7jKIIa8pD9R6lF4gAsbqJafCbT3R7bqkd9xp3n3j2YhqQzETU2lmu4fra3BPio\nofK47kSkguUC6mtk6VrDf2+QtCKlY0dtbF3e2ZBNWo1aj86sjCtoEmqOCMsPRLc/\nXwQcfEqHY4XfafXwqk0G0UxV2ce18xKoR/pN3MpLBZ65NzPnpn0CAwEAAaMtMCsw\nFAYDVR0RBA0wC4IJbG9jYWxob3N0MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqG\nSIb3DQEBCwUAA4IBAQBFW1R037y7wllg/gRk9p2T1stiG8iIXosblmL4Ak1YToTQ\n/0to5GY2ZYW29+rbA4SDTS5eeu2YqZ0A/fF3wey7ggzMS7KyNBOvx5QBJRw3PJGn\n+THfhXvdfkOyeUC6KWRGLgl+/zBFvgh6vFDq3jmv0NI4ehVBTBMCJn7r6577S16T\nwtgKMCooizII0Odu5HIF10gTieFIH3PQYm9JBji9iyemb9Ht3wn7fXQptfGadz/l\nWz/Dv9+a6IOr7JVJMHnqAIvPzpkav4efuVPOX1zbhjg4K5g+nRYfjr5F5upOd0Y3\nznWTUBUyI7CXRkpHtSDXfEqKgnk/8uv7GWw+hyKr\n-----END CERTIFICATE-----\n" + ] + "city": "Maryville" + "common": "localhost" + "country": "US" + "csr_path": "/etc/certificates/freenas_default.csr" + "digest_algorithm": "SHA256" + "email": "info@ixsystems.com" + "extensions": + "ExtendedKeyUsage": "TLS Web Server Authentication" + "SubjectAltName": "DNS:localhost" + "fingerprint": "9C:5A:1D:1B:E7:9E:0B:89:2B:37:F4:19:83:ED:3C:6B:D8:14:0D:9B" + "from": "Fri Sep 25 16:05:38 2020" + "id": 1 + "internal": "NO" + "issuer": "external" + "key_length": 2048 + "key_type": "RSA" + "lifetime": 825 + "name": "freenas_default" + "organization": "iXsystems" + "organizational_unit": "" + "parsed": true + "privatekey": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC6aBpYooul/A3q\nBXS3Ee447HySJ95wvH9aUy0N0VER1DhMoT092ek5IWh3h6I/A+pIl0TociSat5MO\nTnqhmYrH6RkIZMY6cZH4fXwIZb4bi8Alh2jL0odU2k/offnh33Md7p+QiGh/G9Sm\nEQTB/o5ihaa+45DzZAVy0jAO4yiCGvKQ/UepReIALG6iWnwm090e26pHfcad5949\nmIakMxE1NpZruH62twT4qKHyuO5EpILlAuprZOlaw39vkLQipWNHbWxd3tmQTVqN\nWo/OrIwraBJqjgjLD0S3P18EHHxKh2OF32n18KpNBtFMVdnHtfMSqEf6TdzKSwWe\nuTcz56Z9AgMBAAECggEARwcb4uIs7BZbBu0FSCyg5TfXT6m5bKOmszg2VqmHho+i\n1DAsMcEyyP4d3E3mWLSZNQfOzfOQVxPUCQOGXsUuyHXdgAFGN0bHJDRMara59a0O\njj5GhEO4JXD6OdCmwpZuOt2OF3iiuKxWHuElOvZQMuJSYzI7LULTgKjufv23lbsf\nxMO/v9yi57c5EGgnQ8siLKOy/FQZapn4Z9qKn+lVyk5gfaKP0pDsvV4d7nGYMDD2\nYijfkSyNecApFdtWiLE5zLUlvF6oNj8o66z3YrVNKrCPzhA/5Rkkwwk32SNxvKU3\nVZFSNPeOZ60BicxYcWO+b2aAa0WF+uazJAZ4q52gUQKBgQDu88R+0wm76secYkzE\nQglteLNZKFcvth0kI5xH42Hmk9IXkGimFoDJCIrLAuopyGnfNmqmh2is3QUMUPdR\n/wDLnKc4MCezEidNoD2RBC+bzM1hB9oye/b5sOZUDFXSa0k4XSLu1UEuy1yWhkuS\n6JjY1KQfc4FN0K0Fjqqo7UCTCwKBgQDHtKQh/NvMJ2ok4YW+/QAsus4mEK9eCyUy\nOuyDszQYrGvjkS7STKJVNxGLhWb0XKSIAxMZ66b1MwOt+71h7xNn6pcancfVdK7F\n1Xl5J+76SwbXSgQwTZuoMDxPIvZn7v/2ep5Ni/BcOhMcPIcobWb/OmXrFN1brBvo\nlFNQyWWhlwKBgFDAyPMjVvLO0U6kWdUpjA4W8GV9IJnbLdX8wt/4lClcY2/bOcKH\ncFaAMIeTIJemR0FMHpbQxCtHNmGHK03mo9orwsdWXtRBmk69jJDpnT1F5VKZWMAe\n7MRNaEmXMZm+8CvALgIQx8qMp2mnUPsA6Ea+9gg6/MPTdeWe5UXZiC0pAoGAGtSt\nPJfBXBNrklruYjORo3DRo5GYThVHQRFjl2orNKltsVxfIwgCw1ortEgPBgOwY0mu\ndkwP2V+qPeTVk+PQAqUk+gF6yLXtiUzeDiYMWHpeB+y81VSH9jfM0oELA/m7T/03\naYnEmE+BI8kKC6dvMBlDeisKdneQJFZRP0hfrC8CgYEAgYIyCGwcydKpe2Nkj0Fz\nKTtCMC/k4DvJfd5Kb9AbmrPUfKgA9Xj4GT6yPG6uBMi8r5etvLCKJ2x2NtN024a8\nQJLATYPrSsaZkE+9zM0j5nYAgbKpxBhlDzDAzn//3ByVzfgJ25S80XhTI2lfbLH/\nU07ssxdZaQCo+WuD82OvNcg=\n-----END PRIVATE KEY-----\n" + "privatekey_path": "/etc/certificates/freenas_default.key" + "revoked": false + "revoked_date": "" + "root_path": "/etc/certificates" + "san": [ + "DNS:localhost" + ] + "serial": 1 + "signedby": "" + "state": "Tennessee" + "subject_name_hash": 3193428416 + "type": 8 + "until": "Thu Dec 29 15:05:38 2022" + +networkPolicy: + enabled: true + + ingress: + - from: + - ipBlock: + cidr: 172.17.0.0/16 + except: + - 172.17.1.0/24 + - namespaceSelector: + matchLabels: + project: myproject + - podSelector: + matchLabels: + role: frontend + - namespaceSelector: + matchLabels: + project: myproject2 + podSelector: + matchLabels: + role: frontend2 + ports: + - protocol: TCP + port: 6379 + egress: + - to: + - ipBlock: + cidr: 172.17.2.0/16 + except: + - 172.17.2.0/24 + - namespaceSelector: + matchLabels: + project: myproject3 + - podSelector: + matchLabels: + role: frontend3 + - namespaceSelector: + matchLabels: + project: myproject4 + podSelector: + matchLabels: + role: frontend4 + ports: + - protocol: TCP + port: 5978 diff --git a/charts/library/common/Chart.yaml b/charts/library/common/Chart.yaml index 4875f126f92..10cf4ec108f 100644 --- a/charts/library/common/Chart.yaml +++ b/charts/library/common/Chart.yaml @@ -15,4 +15,4 @@ maintainers: name: common sources: null type: library -version: 8.12.2 +version: 8.13.0 diff --git a/charts/library/common/templates/_networkPolicy.tpl b/charts/library/common/templates/_networkPolicy.tpl index 4ac6e2c95a5..441469858b6 100644 --- a/charts/library/common/templates/_networkPolicy.tpl +++ b/charts/library/common/templates/_networkPolicy.tpl @@ -21,20 +21,142 @@ spec: {{- include "common.labels.selectorLabels" . | nindent 6 }} {{- end }} - {{- with .Values.networkPolicy.policyTypes }} - policyTypes: - {{- . | toYaml | nindent 4 }} + {{- if .Values.networkPolicy.policyType }} + {{- if eq .Values.networkPolicy.policyType "ingress" }} + policyTypes: ["Ingress"] + {{- else if eq .Values.networkPolicy.policyType "egress" }} + policyTypes: ["Egress"] + + {{- else if eq .Values.networkPolicy.policyType "ingress-egress" }} + policyTypes: ["Ingress", "Egress"] + {{- end -}} {{- end -}} - {{- with .Values.networkPolicy.egress }} + {{- if .Values.networkPolicy.egress }} egress: - {{- . | toYaml | nindent 4 }} + {{- range .Values.networkPolicy.egress }} + - to: + {{- range .to }} + {{- $nss := false }} + {{- $ipb := false }} + {{- if .ipBlock }} + {{- if .ipBlock.cidr }} + {{- $ipb = true }} + - ipBlock: + cidr: {{ .ipBlock.cidr }} + {{- if .ipBlock.except }} + except: + {{- range .ipBlock.except }} + - {{ . }} + {{- end }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if and ( .namespaceSelector ) ( not $ipb ) }} + {{- if or ( .namespaceSelector.matchLabels ) ( .namespaceSelector.matchExpressions ) -}} + {{- $nss = true }} + - namespaceSelector: + {{- if .namespaceSelector.matchLabels }} + matchLabels: + {{- .namespaceSelector.matchLabels | toYaml | nindent 12 }} + {{- end -}} + {{- if .namespaceSelector.matchExpressions }} + matchExpressions: + {{- .namespaceSelector.matchExpressions | toYaml | nindent 12 }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if and ( .podSelector ) ( not $ipb ) }} + {{- if or ( .podSelector.matchLabels ) ( .podSelector.matchExpressions ) }} + {{- if $nss }} + podSelector: + {{- else }} + - podSelector: + {{- end }} + {{- if .podSelector.matchLabels }} + matchLabels: + {{- .podSelector.matchLabels | toYaml | nindent 12 }} + {{- end -}} + {{- if .podSelector.matchExpressions }} + matchExpressions: + {{- .podSelector.matchExpressions | toYaml | nindent 12 }} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + + {{- with .ports }} + ports: + {{- . | toYaml | nindent 6 }} + {{- end -}} + {{- end -}} {{- end -}} - {{- with .Values.networkPolicy.ingress }} + {{- if .Values.networkPolicy.ingress }} ingress: - {{- . | toYaml | nindent 4 }} + {{- range .Values.networkPolicy.ingress }} + - from: + {{- range .from }} + {{- $nss := false }} + {{- $ipb := false }} + {{- if .ipBlock }} + {{- if .ipBlock.cidr }} + {{- $ipb = true }} + - ipBlock: + cidr: {{ .ipBlock.cidr }} + {{- if .ipBlock.except }} + except: + {{- range .ipBlock.except }} + - {{ . }} + {{- end }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if and ( .namespaceSelector ) ( not $ipb ) }} + {{- if or ( .namespaceSelector.matchLabels ) ( .namespaceSelector.matchExpressions ) -}} + {{- $nss = true }} + - namespaceSelector: + {{- if .namespaceSelector.matchLabels }} + matchLabels: + {{- .namespaceSelector.matchLabels | toYaml | nindent 12 }} + {{- end -}} + {{- if .namespaceSelector.matchExpressions }} + matchExpressions: + {{- .namespaceSelector.matchExpressions | toYaml | nindent 12 }} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- if and ( .podSelector ) ( not $ipb ) }} + {{- if or ( .podSelector.matchLabels ) ( .podSelector.matchExpressions ) }} + {{- if $nss }} + podSelector: + {{- else }} + - podSelector: + {{- end }} + {{- if .podSelector.matchLabels }} + matchLabels: + {{- .podSelector.matchLabels | toYaml | nindent 12 }} + {{- end -}} + {{- if .podSelector.matchExpressions }} + matchExpressions: + {{- .podSelector.matchExpressions | toYaml | nindent 12 }} + {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{- with .ports }} + ports: + {{- . | toYaml | nindent 6 }} {{- end -}} + {{- end -}} + {{- end -}} + {{- end -}} {{- end -}} diff --git a/charts/library/common/values.yaml b/charts/library/common/values.yaml index a4905ae63ed..d94b253e41c 100644 --- a/charts/library/common/values.yaml +++ b/charts/library/common/values.yaml @@ -225,8 +225,8 @@ networkPolicy: # customizes the podSelector (defaults to the helm-chart selector-labels # podSelector: - # -- add or remove Policy types - policyTypes: [] + # -- add or remove Policy types. Options: ingress, egress, ingress-egress + policyType: "" # -- add or remove egress policies egress: [] diff --git a/templates/questions/advanced.yaml b/templates/questions/advanced.yaml index 3ca266b6d83..764239720d1 100644 --- a/templates/questions/advanced.yaml +++ b/templates/questions/advanced.yaml @@ -37,3 +37,352 @@ schema: type: int default: 80 + - variable: networkPolicy + group: "Advanced" + label: "(Advanced) Network Policy" + schema: + type: dict + attrs: + - variable: enabled + label: "enabled" + schema: + type: boolean + default: false + show_subquestions_if: true + subquestions: + - variable: policyType + label: "Policy Type" + schema: + type: string + default: "" + enum: + - value: "" + description: "Default" + - value: "ingress" + description: "Ingress" + - value: "egress" + description: "Egress" + - value: "ingress-egress" + description: "Ingress and Egress" + - variable: egress + label: "Egress" + schema: + type: list + default: [] + items: + - variable: egressEntry + label: "" + schema: + type: dict + attrs: + - variable: to + label: "To" + schema: + type: list + default: [] + items: + - variable: toEntry + label: "" + schema: + type: dict + attrs: + - variable: ipBlock + label: "ipBlock" + schema: + type: dict + attrs: + - variable: cidr + label: "cidr" + schema: + type: string + default: "" + - variable: except + label: "except" + schema: + type: list + default: [] + items: + - variable: exceptint + label: "" + schema: + type: int + - variable: namespaceSelector + label: "namespaceSelector" + schema: + type: dict + attrs: + - variable: matchExpressions + label: "matchExpressions" + schema: + type: list + default: [] + items: + - variable: expressionEntry + label: "" + schema: + type: dict + attrs: + - variable: key + label: "Key" + schema: + type: string + - variable: operator + label: "operator" + schema: + type: string + default: "TCP" + enum: + - value: "In" + description: "In" + - value: "NotIn" + description: "NotIn" + - value: "Exists " + description: "Exists " + - value: "DoesNotExist " + description: "DoesNotExist " + - variable: values + label: "values" + schema: + type: list + default: [] + items: + - variable: value + label: "" + schema: + type: string + - variable: podSelector + label: "" + schema: + type: dict + attrs: + - variable: matchExpressions + label: "matchExpressions" + schema: + type: list + default: [] + items: + - variable: expressionEntry + label: "" + schema: + type: dict + attrs: + - variable: key + label: "Key" + schema: + type: string + - variable: operator + label: "operator" + schema: + type: string + default: "TCP" + enum: + - value: "In" + description: "In" + - value: "NotIn" + description: "NotIn" + - value: "Exists " + description: "Exists " + - value: "DoesNotExist " + description: "DoesNotExist " + - variable: values + label: "values" + schema: + type: list + default: [] + items: + - variable: value + label: "" + schema: + type: string + - variable: ports + label: "Ports" + schema: + type: list + default: [] + items: + - variable: portsEntry + label: "" + schema: + type: dict + attrs: + - variable: port + label: "port" + schema: + type: int + - variable: endPort + label: "port" + schema: + type: int + - variable: protocol + label: "Protocol" + schema: + type: string + default: "TCP" + enum: + - value: "TCP" + description: "TCP" + - value: "UDP" + description: "UDP" + - value: "SCTP" + description: "SCTP" + - variable: ingress + label: "Ingress" + schema: + type: list + default: [] + items: + - variable: ingressEntry + label: "" + schema: + type: dict + attrs: + - variable: from + label: "From" + schema: + type: list + default: [] + items: + - variable: fromEntry + label: "" + schema: + type: dict + attrs: + - variable: ipBlock + label: "ipBlock" + schema: + type: dict + attrs: + - variable: cidr + label: "cidr" + schema: + type: string + default: "" + - variable: except + label: "except" + schema: + type: list + default: [] + items: + - variable: exceptint + label: "" + schema: + type: int + - variable: namespaceSelector + label: "namespaceSelector" + schema: + type: dict + attrs: + - variable: matchExpressions + label: "matchExpressions" + schema: + type: list + default: [] + items: + - variable: expressionEntry + label: "" + schema: + type: dict + attrs: + - variable: key + label: "Key" + schema: + type: string + - variable: operator + label: "operator" + schema: + type: string + default: "TCP" + enum: + - value: "In" + description: "In" + - value: "NotIn" + description: "NotIn" + - value: "Exists " + description: "Exists " + - value: "DoesNotExist " + description: "DoesNotExist " + - variable: values + label: "values" + schema: + type: list + default: [] + items: + - variable: value + label: "" + schema: + type: string + - variable: podSelector + label: "" + schema: + type: dict + attrs: + - variable: matchExpressions + label: "matchExpressions" + schema: + type: list + default: [] + items: + - variable: expressionEntry + label: "" + schema: + type: dict + attrs: + - variable: key + label: "Key" + schema: + type: string + - variable: operator + label: "operator" + schema: + type: string + default: "TCP" + enum: + - value: "In" + description: "In" + - value: "NotIn" + description: "NotIn" + - value: "Exists " + description: "Exists " + - value: "DoesNotExist " + description: "DoesNotExist " + - variable: values + label: "values" + schema: + type: list + default: [] + items: + - variable: value + label: "" + schema: + type: string + - variable: ports + label: "Ports" + schema: + type: list + default: [] + items: + - variable: portsEntry + label: "" + schema: + type: dict + attrs: + - variable: port + label: "port" + schema: + type: int + - variable: endPort + label: "port" + schema: + type: int + - variable: protocol + label: "Protocol" + schema: + type: string + default: "TCP" + enum: + - value: "TCP" + description: "TCP" + - value: "UDP" + description: "UDP" + - value: "SCTP" + description: "SCTP"