diff --git a/Cargo.lock b/Cargo.lock index 72b224715..917db5c17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1831,7 +1831,7 @@ dependencies = [ [[package]] name = "chronicle-domain-lint" -version = "0.7.5" +version = "0.7.6" dependencies = [ "chronicle", "clap 3.2.25", diff --git a/charts/chronicle/Chart.lock b/charts/chronicle/Chart.lock index 7db551ef5..c5cf6ffc8 100644 --- a/charts/chronicle/Chart.lock +++ b/charts/chronicle/Chart.lock @@ -4,9 +4,9 @@ dependencies: version: 0.1.3 - name: node repository: https://paritytech.github.io/helm-charts/ - version: 5.6.1 + version: 5.7.1 - name: vault repository: https://helm.releases.hashicorp.com version: 0.27.0 -digest: sha256:19c6ade52b5c53daab6fd0a8a2c42a81e5371b99eb9de8a5f102b215930dce40 -generated: "2024-03-05T14:03:42.428673+03:00" +digest: sha256:6073af2c490fa86b821ac5188b14cd4f9bd9f2c8e61a778d2acbde9861470a0c +generated: "2024-05-01T20:31:33.345331+01:00" diff --git a/charts/chronicle/Chart.yaml b/charts/chronicle/Chart.yaml index 71e556676..96feb98c4 100644 --- a/charts/chronicle/Chart.yaml +++ b/charts/chronicle/Chart.yaml @@ -27,9 +27,4 @@ dependencies: - name: standard-defs version: ~0.1.0 repository: https://btp-charts-stable.s3.amazonaws.com/charts/ - - name: node - version: ~5.6.1 - repository: https://paritytech.github.io/helm-charts/ - - name: vault - version: ~0.27 - repository: https://helm.releases.hashicorp.com + diff --git a/charts/chronicle/charts/node-5.6.1.tgz b/charts/chronicle/charts/node-5.6.1.tgz deleted file mode 100644 index 67cc59a76..000000000 Binary files a/charts/chronicle/charts/node-5.6.1.tgz and /dev/null differ diff --git a/charts/chronicle/charts/standard-defs-0.1.3.tgz b/charts/chronicle/charts/standard-defs-0.1.3.tgz index 2f63806a9..d5ac17a14 100644 Binary files a/charts/chronicle/charts/standard-defs-0.1.3.tgz and b/charts/chronicle/charts/standard-defs-0.1.3.tgz differ diff --git a/charts/chronicle/templates/_chronicle.tpl b/charts/chronicle/templates/_chronicle.tpl index 0b6d04868..c1bda86c9 100644 --- a/charts/chronicle/templates/_chronicle.tpl +++ b/charts/chronicle/templates/_chronicle.tpl @@ -3,7 +3,7 @@ {{- end -}} {{- define "tp.replicas" -}} -{{ include "lib.call-nested" (list . "sawtooth" "sawtooth.replicas") | int }} +{{ include "lib.call-nested" (list . "node" "node.replicas") | int }} {{- end -}} {{- define "chronicle.service.name" -}} @@ -26,16 +26,12 @@ chronicle: {{ include "common.names.fullname" . }} {{ include "chronicle.labels.appLabels" . }} {{- end -}} -{{- define "chronicle.sawtooth.sawcomp" -}} -{{ include "lib.call-nested" (list . "sawtooth" "sawtooth.ports.sawcomp") | int }} +{{- define "chronicle.substrate.rpc" -}} +9982 {{- end -}} -{{- define "chronicle.sawtooth.rest" -}} -{{ include "lib.call-nested" (list . "sawtooth" "sawtooth.ports.rest") | int }} -{{- end -}} - -{{- define "chronicle.sawtooth.service" -}} -{{- $svc := include "lib.call-nested" (list . "sawtooth" "common.names.fullname") -}} +{{- define "chronicle.substrate.service" -}} +{{- $svc := include "lib.call-nested" (list . "node" "common.names.fullname") -}} {{- $ns := .Release.Namespace -}} {{- $domain := "svc.cluster.local" -}} {{ printf "%s.%s.%s" $svc $ns $domain }} diff --git a/charts/chronicle/templates/chronicle-config.yaml b/charts/chronicle/templates/chronicle-config.yaml index f0f114b48..640f5f71d 100644 --- a/charts/chronicle/templates/chronicle-config.yaml +++ b/charts/chronicle/templates/chronicle-config.yaml @@ -1,17 +1,10 @@ --- -{{$stlServiceName := include "lib.call-nested" (list . "sawtooth" "common.names.fullname")}} +{{$stlServiceName := include "lib.call-nested" (list . "node" "common.names.fullname")}} apiVersion: v1 kind: ConfigMap metadata: name: {{.Release.Name}}-chronicle-config data: config.toml: | - [secrets] - path = "/var/lib/chronicle/secrets/" - [store] - path = "/var/lib/chronicle/store/" - address = "postgresql://{{ .Values.postgres.user }}@{{ .Values.postgres.host }}:5432/{{ .Values.postgres.database }}" - [validator] - address = "tcp://{{ include "chronicle.sawtooth.service" . }}:{{ include "chronicle.sawtooth.sawcomp" . }}" [namespace_bindings] default = "fd717fd6-70f1-44c1-81de-287d5e101089" diff --git a/charts/chronicle/templates/chronicle-init.yaml b/charts/chronicle/templates/chronicle-init.yaml deleted file mode 100644 index 90fdd1581..000000000 --- a/charts/chronicle/templates/chronicle-init.yaml +++ /dev/null @@ -1,231 +0,0 @@ -{{$stlServiceName := include "lib.call-nested" (list . "sawtooth" "common.names.fullname")}} ---- -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "common.names.fullname" . }}-init - labels: {{ include "chronicle.labels" . | nindent 4 }} - component: chronicle -spec: - ttlSecondsAfterFinished: 100 - template: - metadata: - labels: {{ include "chronicle.labels" . | nindent 8 }} - component: chronicle - spec: - restartPolicy: Never - serviceAccountName: {{ include "lib.serviceAccountName" . }} - automountServiceAccountToken: true - volumes: {{- include "lib.volumes" .Values.opa.tp.extraVolumes | nindent 8 }} - - name: shared-data - emptyDir: {} - initContainers: - - name: get-secret - image: alpine/k8s:1.24.13 - command: [ "sh", "-ec" ] - args: - - | - if kubectl get secret {{ include "chronicle.root-key.secret" . }} -n {{.Release.Namespace}} >/dev/null 2>&1; then - echo "Secret found." - kubectl get secret {{ include "chronicle.root-key.secret" . }} -n {{.Release.Namespace}} -o jsonpath='{.data.*}' | base64 -d > /shared-data/root.pem - touch /shared-data/secret-found - else - echo "Secret not found." - fi - volumeMounts: - - name: shared-data - mountPath: /shared-data - - name: generate-secret - {{- include "lib.image" (dict "imageRoot" .Values.opa.opaInit.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-ec"] - args: - - | - if [[ ! -f "/shared-data/root.pem" ]]; then - echo "Generating new root key." - opactl generate --output /shared-data/root.pem - else - echo "Root key already exists." - fi - env: {{ include "lib.safeToYaml" .Values.env | nindent 12 }} - - name: RUST_LOG - value: {{ .Values.logLevel }} - - name: RUST_BACKTRACE - value: {{ .Values.backtraceLevel }} - volumeMounts: - - name: shared-data - mountPath: /shared-data - - name: create-secret - image: alpine/k8s:1.24.13 - command: [ "sh", "-ec" ] - args: - - | - if [ -f "/shared-data/secret-found" ]; then - echo "Secret already exists." - else - echo "Creating k8s secret from key." - kubectl create secret generic {{ include "chronicle.root-key.secret" . }} \ - -n {{ .Release.Namespace }} \ - --from-file=/shared-data/root.pem - fi - volumeMounts: - - name: shared-data - mountPath: /shared-data - {{ if .Values.opa.enabled }} - - name: opa-bootstrap-root - {{- include "lib.image" (dict "imageRoot" .Values.opa.opaInit.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-ec"] - args: - - | - wait-for-it $HOST:$PORT --timeout=0 - echo "Waiting to ensure Sawtooth validator is ready ..." - sleep 100 - - if [[ -f "/shared-data/secret-found" ]]; then - echo "Skipping root key bootstrap." - else - opactl \ - --sawtooth-address tcp://$HOST:$PORT \ - bootstrap \ - --root-key /shared-data/root.pem - fi - env: {{ include "lib.safeToYaml" .Values.env | nindent 12 }} - - name: HOST - value: {{ $stlServiceName }}.{{ .Release.Namespace }}.svc.cluster.local - - name: PORT - value: "{{ include "chronicle.sawtooth.sawcomp" . }}" - - name: RUST_LOG - value: {{ .Values.logLevel }} - - name: RUST_BACKTRACE - value: {{ .Values.backtraceLevel }} - volumeMounts: - - name: shared-data - mountPath: /shared-data - {{ if .Values.opa.policy.url }} - - name: wait-for-sawtooth-rest-api - {{- include "lib.image" (dict "imageRoot" .Values.opa.opaInit.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-ec"] - args: - - | - wait-for-it $HOST:$PORT --timeout=0 - echo "Sawtooth rest API is ready." - env: - - name: HOST - value: {{ $stlServiceName }}.{{ .Release.Namespace }}.svc.cluster.local - - name: PORT - value: "{{ include "chronicle.sawtooth.rest" . }}" - - name: RUST_LOG - value: {{ .Values.logLevel }} - - name: RUST_BACKTRACE - value: {{ .Values.backtraceLevel }} - volumeMounts: - - name: shared-data - mountPath: /shared-data - - name: opa-settings - {{- include "lib.image" (dict "imageRoot" .Values.sawset.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-ec"] - args: - - | - if sawtooth settings list --url http://$HOST:$PORT | grep -q "chronicle.opa.policy_name"; then - echo "Skipping setting Sawtooth OPA settings." - exit 0 - else - echo "Creating Sawtooth settings batch." - sawset proposal create \ - -k /etc/sawtooth/keys/{{ $stlServiceName }}-0 \ - chronicle.opa.policy_name={{ required "opa.policy.id required!" .Values.opa.policy.id }} \ - chronicle.opa.entrypoint={{ required "opa.policy.entrypoint required!" .Values.opa.policy.entrypoint }} \ - -o /shared-data/opa-settings.batch - - echo "Submitting Sawtooth OPA settings batch." - sawtooth batch submit \ - -f /shared-data/opa-settings.batch \ - --url http://$HOST:$PORT \ - --wait 60 - fi - env: - - name: HOST - value: {{ $stlServiceName }}.{{ .Release.Namespace }}.svc.cluster.local - - name: PORT - value: "{{ include "chronicle.sawtooth.rest" . }}" - volumeMounts: - - name: shared-data - mountPath: /shared-data - - name: validator-secret - mountPath: /etc/sawtooth/keys - readOnly: true - - name: get-policy - {{- include "lib.image" (dict "imageRoot" .Values.opa.opaInit.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-ec"] - args: - - | - echo "Attempting to get policy." - opactl \ - --sawtooth-address tcp://$HOST:$PORT \ - get-policy \ - --id {{ .Values.opa.policy.id }} \ - --output /shared-data/policy.bin - - if [ -f "/shared-data/policy.bin" ]; then - echo "Policy already set." - touch /shared-data/policy-already-set - exit 0 - else - echo "Policy not found." - exit 0 - fi - env: {{ include "lib.safeToYaml" .Values.env | nindent 12 }} - - name: HOST - value: {{ $stlServiceName }}.{{ .Release.Namespace }}.svc.cluster.local - - name: PORT - value: "{{ include "chronicle.sawtooth.sawcomp" . }}" - - name: RUST_LOG - value: {{ .Values.logLevel }} - - name: RUST_BACKTRACE - value: {{ .Values.backtraceLevel }} - volumeMounts: - - name: shared-data - mountPath: /shared-data - - name: set-policy - {{- include "lib.image" (dict "imageRoot" .Values.opa.opaInit.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-ec"] - args: - - | - if [[ -f "/shared-data/policy-already-set" ]]; then - echo "Skipping setting policy." - exit 0 - else - echo "Policy not found on chain. Setting policy." - opactl \ - --sawtooth-address tcp://$HOST:$PORT \ - set-policy \ - --id {{ .Values.opa.policy.id }} \ - -p {{ .Values.opa.policy.url }} \ - --root-key /shared-data/root.pem - fi - env: {{ include "lib.safeToYaml" .Values.env | nindent 12 }} - - name: HOST - value: {{ $stlServiceName }}.{{ .Release.Namespace }}.svc.cluster.local - - name: PORT - value: "{{ include "chronicle.sawtooth.sawcomp" . }}" - - name: RUST_LOG - value: {{ .Values.logLevel }} - - name: RUST_BACKTRACE - value: {{ .Values.backtraceLevel }} - volumeMounts: - - name: shared-data - mountPath: /shared-data - {{ end }} - {{ end }} - containers: - - name: chronicle-init - image: busybox:1.36 - command: [ "sh", "-c"] - args: - - | - echo "Chronicle bootstrap and OPA settings initialization complete." - volumes: - - name: shared-data - emptyDir: {} - - name: validator-secret - configMap: - name: validator-secret diff --git a/charts/chronicle/templates/statefulset.yaml b/charts/chronicle/templates/statefulset.yaml index 0f3116a8d..cff0e60a1 100644 --- a/charts/chronicle/templates/statefulset.yaml +++ b/charts/chronicle/templates/statefulset.yaml @@ -1,4 +1,4 @@ -{{$stlServiceName := include "lib.call-nested" (list . "sawtooth" "common.names.fullname")}} +{{$substrateServiceName := include "lib.call-nested" (list . "node" "common.names.fullname")}} --- apiVersion: apps/v1 kind: StatefulSet @@ -19,57 +19,6 @@ spec: spec: serviceAccountName: {{ include "lib.serviceAccountName" . }} affinity: {{ include "lib.safeToYaml" .Values.affinity | nindent 8 }} - initContainers: - - name: chronicle-permissions - image: busybox:1.36 - command: [ "sh", "-c"] - args: - - | - chown -R 999:999 /var/lib/chronicle || true - volumeMounts: - - name: chronicle-config - mountPath: /etc/chronicle/config/ - - name: chronicle-secrets - mountPath: /var/lib/chronicle/secrets/ - readOnly: false - - name: chronicle-keystore - {{- include "lib.image" (dict "imageRoot" .Values.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-c"] - args: - - | - /usr/local/bin/chronicle \ - -c /etc/chronicle/config/config.toml \ - verify-keystore - env: {{ include "lib.safeToYaml" .Values.env | nindent 12 }} - - name: RUST_LOG - value: {{ .Values.logLevel }} - volumeMounts: - - name: chronicle-config - mountPath: /etc/chronicle/config/ - - name: chronicle-secrets - mountPath: /var/lib/chronicle/secrets/ - readOnly: false - {{- if and .Values.opa.enabled .Values.opa.policy.url }} - - name: wait-for-opa-settings - {{- include "lib.image" (dict "imageRoot" .Values.sawset.image "global" .Values.global ) | nindent 10 }} - command: [ "bash", "-exc"] - args: - - | - keepTrying=true - while [ $keepTrying = "true" ]; do - if sawtooth settings list --url http://$HOST:$PORT | grep -q "chronicle.opa.policy_name"; then - break - else - echo "Waiting for OPA policy id." - sleep 10 - fi - done - env: - - name: HOST - value: {{ $stlServiceName }}.{{ .Release.Namespace }}.svc.cluster.local - - name: PORT - value: "{{ include "chronicle.sawtooth.rest" . }}" - {{- end }} containers: {{- if .Values.postgres.enabled }} - name: postgres @@ -113,7 +62,7 @@ spec: chronicle \ -c /etc/chronicle/config/config.toml \ --console-logging json \ - --sawtooth tcp://{{ include "chronicle.sawtooth.service" . }}:{{ include "chronicle.sawtooth.sawcomp" . }} \ + --substrate grpc://{{ include "chronicle.substrate.service" . }}:{{ include "chronicle.substrate.rpc" . }} \ --remote-database \ --database-name {{ .Values.postgres.database }} \ --database-username {{ .Values.postgres.user }} \ diff --git a/charts/chronicle/templates/tests/api-test.yaml b/charts/chronicle/templates/tests/api-test.yaml deleted file mode 100644 index 354a69d7f..000000000 --- a/charts/chronicle/templates/tests/api-test.yaml +++ /dev/null @@ -1,130 +0,0 @@ -{{- if .Values.test.api.enabled }} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "common.names.fullname" . }}-api-test - labels: {{ include "chronicle.labels" . | nindent 4 }} - component: api-test - annotations: - "helm.sh/hook": test - "helm.sh/hook-delete-policy": hook-succeeded -spec: - backoffLimit: 0 - template: - spec: - restartPolicy: Never - serviceAccountName: {{ include "lib.serviceAccountName" . }} - automountServiceAccountToken: true - {{- if .Values.auth.required }} - {{ if not .Values.test.auth.token }} - {{ if not .Values.devIdProvider.enabled }} - {{ required "If 'auth.required' when using the api-test 'test.auth.token' must be provided or 'devIdProvider.enabled' must be set to 'true'!" .Values.devIdProvider.enabled }} - {{ end }} - initContainers: - - name: wait-for-id-provider - {{- include "lib.image" (dict "imageRoot" .Values.test.api.image "global" .Values.global ) | nindent 10 }} - command: [ "sh", "-c" ] - args: - - | - URL="{{ include "chronicle.id-provider.service.jwks.url" . }}" - - wait_for_url() { - local url=$1 - scheme=$(echo "$url" | cut -f 1 -d :) - hostAndPort=$(echo "$url" | cut -f 3 -d /) - HOST=$(echo "$hostAndPort" | cut -f 1 -d :) - port=$(echo "$hostAndPort" | awk -F: '{print $2}') - - case $scheme in - "http") - defaultPort=80 - ;; - "https") - defaultPort=443 - ;; - *) - defaultPort=80 - ;; - esac - - PORT=${port:-$defaultPort} - wait-for-it "$HOST:$PORT" --timeout=120 - } - - echo "Waiting for id-provider to be ready ..." - wait_for_url "$URL" - - if [ $? -eq 0 ]; then - echo "Id-provider is ready. Exiting." - exit 0 - else - echo "Timeout occurred. Please check if the correct URL has been provided." - exit 1 - fi - - name: token-loader - image: alpine/k8s:1.24.13 - command: [ "sh", "-ec" ] - args: - - | - echo "Waiting to ensure id-provider is ready ..." - sleep 20 - echo "Getting token from id-provider ..." - kubectl exec {{ include "chronicle.id-provider.service" . }}-0 -c id-provider -- oauth-token > /shared-data/jwks-token - echo "Token loaded. Exiting." - volumeMounts: - - name: shared-data - mountPath: /shared-data - {{ end }} - {{- end }} - containers: - - name: test - {{- include "lib.image" (dict "imageRoot" .Values.test.api.image "global" .Values.global ) | nindent 10 }} - command: [ "sh", "-ec" ] - args: - - | - {{ if not .Values.test.auth.token }} - {{ if or .Values.auth.jwks.url .Values.auth.userinfo.url }} - echo "Auth endpoints provided but no token provided." - echo "Please provide 'test.auth.token' in the values.yaml file." - exit 1 - {{ end }} - {{ end }} - - API={{ include "chronicle.api.service" . }} - export PORT={{ .Values.port }} - echo "Waiting for API to be ready ..." - wait-for-it $API:$PORT --timeout=0 - echo "Getting IP address for API ..." - getent hosts $API | cut -f 1 -d \ | head -n 1 > /shared-data/api-ip || exit 1 - - {{- if .Values.test.auth.token }} - echo "{{ .Values.test.auth.token }}" > /shared-data/jwks-token - {{- end }} - - if [ -f "/shared-data/jwks-token" ]; then - echo "Found token." - sleep 5 - export TOKEN=$(cat "/shared-data/jwks-token") - fi - - export HOST=$(cat /shared-data/api-ip) - echo "Testing API with subscribe-submit-test..." - subscribe-submit-test - exit_code=$? - if [ $exit_code -eq 0 ]; then - echo "Test complete." - exit $exit_code - else - echo "Test failed." - exit $exit_code - fi - env: - - name: REQUIRE_AUTH - value: {{ .Values.auth.required | quote }} - volumeMounts: - - name: shared-data - mountPath: /shared-data - volumes: {{- include "lib.volumes" .Values.opa.tp.extraVolumes | nindent 8 }} - - name: shared-data - emptyDir: {} -{{- end }} diff --git a/charts/chronicle/templates/tests/auth-endpoints-test.yaml b/charts/chronicle/templates/tests/auth-endpoints-test.yaml deleted file mode 100644 index 79f80f9e9..000000000 --- a/charts/chronicle/templates/tests/auth-endpoints-test.yaml +++ /dev/null @@ -1,160 +0,0 @@ -{{- if .Values.test.auth.enabled }} -{{ if not (or (.Values.devIdProvider.enabled) (or (.Values.auth.jwks.url) (.Values.auth.userinfo.url)))}} -{{ required "If 'test.auth.enabled' you need to provide 'auth.jwks.url', 'auth.userinfo.url', or enable the `devIdProvider`!" .Values.devIdProvider.enabled }} -{{ end }} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "common.names.fullname" . }}-auth-endpoints-test - labels: {{ include "chronicle.labels" . | nindent 4 }} - component: auth-endpoints-test - annotations: - "helm.sh/hook": test - "helm.sh/hook-delete-policy": hook-succeeded -spec: - backoffLimit: 0 - template: - spec: - restartPolicy: Never - serviceAccountName: {{ include "lib.serviceAccountName" . }} - {{- if .Values.devIdProvider.enabled }} - automountServiceAccountToken: true - initContainers: - - name: wait - {{- include "lib.image" (dict "imageRoot" .Values.test.api.image "global" .Values.global ) | nindent 10 }} - command: [ "sh", "-c" ] - args: - - | - URL="{{ include "chronicle.id-provider.service.jwks.url" . }}" - - wait_for_url() { - local url=$1 - scheme=$(echo "$url" | cut -f 1 -d :) - hostAndPort=$(echo "$url" | cut -f 3 -d /) - HOST=$(echo "$hostAndPort" | cut -f 1 -d :) - port=$(echo "$hostAndPort" | awk -F: '{print $2}') - - case $scheme in - "http") - defaultPort=80 - ;; - "https") - defaultPort=443 - ;; - *) - defaultPort=80 - ;; - esac - - PORT=${port:-$defaultPort} - wait-for-it "$HOST:$PORT" --timeout=120 - } - - echo "Waiting for id-provider to be ready ..." - wait_for_url "$URL" - - if [ $? -eq 0 ]; then - echo "Id-provider is ready. Exiting." - exit 0 - else - echo "Timeout occurred. Please check if the correct URL has been provided." - exit 1 - fi - - name: tok - image: alpine/k8s:1.24.13 - command: [ "sh", "-ec" ] - args: - - | - echo "Waiting to ensure id-provider is ready ..." - sleep 20 - echo "Getting token from id-provider ..." - kubectl exec {{ include "chronicle.id-provider.service" . }}-0 -c id-provider -- oauth-token > /shared-data/jwks-token - echo "Token loaded. Exiting." - volumeMounts: - - name: shared-data - mountPath: /shared-data - {{- end }} - containers: - - name: jwks - image: alpine/k8s:1.24.13 - command: [ "sh", "-c"] - args: - - | - {{ if or (.Values.auth.jwks.url) (.Values.devIdProvider.enabled) }} - {{ if .Values.auth.jwks.url }} - echo "Checking provided JWKS endpoint: {{ .Values.auth.jwks.url }}." - endPoint="{{ .Values.auth.jwks.url }}" - {{ else if .Values.auth.userinfo.url }} - echo "JWKS endpoint not set but userinfo url is set - skipping JWKS check." - exit 0 - {{ else }} - echo "Checking JWKS endpoint from id-provider: {{ include "chronicle.id-provider.service.jwks.url" . }}." - endPoint="{{ include "chronicle.id-provider.service.jwks.url" . }}" - {{ end }} - - time curl -s -o /shared-data/jwks.json $endPoint - - cat /shared-data/jwks.json | jq . > /dev/null \ - || { echo "JWKS endpoint did not return a valid JSON object."; echo "DEBUG: $(cat /shared-data/jwks.json)"; exit 1; } - echo "JWKS endpoint returned a valid JSON object:" - cat /shared-data/jwks.json - echo - {{ else }} - echo "Skipping JWKS endpoint check." - {{ end }} - - echo -e "Exiting." - volumeMounts: - - name: shared-data - mountPath: /shared-data - - name: userinfo - image: alpine/k8s:1.24.13 - command: [ "sh", "-c"] - args: - - | - {{ if or (.Values.auth.userinfo.url) (.Values.devIdProvider.enabled) }} - {{ if .Values.auth.userinfo.url }} - {{ if not .Values.test.auth.token }} - {{ required "If providing 'auth.userinfo.url' you need to provide a 'test.auth.token'!" .Values.test.auth.token}} - {{ end }} - echo "Checking user-provided userinfo endpoint: $endPoint" - endPoint="{{ .Values.auth.userinfo.url }}" - {{ else if .Values.auth.jwks.url }} - echo "Userinfo endpoint not set but JWKS url is set - skipping userinfo check." - exit 0 - {{ else }} - echo "Checking id-provider userinfo endpoint: $endPoint" - endPoint="{{ include "chronicle.id-provider.service.userinfo.url" . }}" - {{ end }} - - {{ if .Values.test.auth.token }} - {{ if not .Values.auth.userinfo.url }} - {{ required "If providing 'test.auth.token' you need to provide a 'auth.userinfo.url'!" .Values.auth.userinfo.url }} - {{ end }} - echo "Using 'test.auth.token' to check userinfo endpoint." - time curl -s -H "Authorization: Bearer {{ .Values.test.auth.token }}" -o /shared-data/userinfo.json $endPoint - {{ else }} - echo "Using token from id-provider to check userinfo endpoint." - time curl -s -H "Authorization: Bearer $(cat /shared-data/jwks-token)" -o /shared-data/userinfo.json $endPoint - {{ end }} - - if jq -e 'has("error")' /shared-data/userinfo.json > /dev/null; then - echo "Userinfo endpoint returned an error:" - echo "DEBUG: $(cat /shared-data/userinfo.json)" - exit 1 - else - echo "Userinfo endpoint returned a valid JSON object: $(cat /shared-data/userinfo.json)" - echo - fi - {{ else }} - echo "Skipping userinfo endpoint check." - {{ end }} - - echo -e "Exiting." - volumeMounts: - - name: shared-data - mountPath: /shared-data - volumes: - - name: shared-data - emptyDir: {} -{{- end }} diff --git a/charts/chronicle/values.yaml b/charts/chronicle/values.yaml index 945a12087..6e725af6b 100644 --- a/charts/chronicle/values.yaml +++ b/charts/chronicle/values.yaml @@ -36,8 +36,13 @@ devIdProvider: ## @md | `devIdProvider.image.tag` | the image tag | latest | tag: BTP2.1.0-0.7.4 -## @md | `endpoints` | which API endpoints to offer | data, graphql | +opa: + enabled: false + +## @md | `endpoints` | which API endpoints to offer | arrow, json-ld, graphql | endpoints: + arrow: + enabled: true data: enabled: true graphql: @@ -184,8 +189,19 @@ resources: volumes: {} -## @md | `sawtooth` | sawtooth options may be configured | see [Sawtooth](../sawtooth/README.md) | node: + vault: + keys: + - name: aura + type: aura + scheme: secp256k1 + vaultPath: kv/secret/chronicle-node # path at which the secret is located in Vault + vaultKey: aura # key under which the secret value is stored in Vault + extraDerivation: "//${HOSTNAME}//aura" # allows to have unique derived keys for each pod of the statefulset + nodeKey: + name: bootnode-key + vaultPath: kv/secret/chronicle-node + vaultKey: bootnode-key image: ## @md | `test.api.image.pullPolicy` | the image pull policy | IfNotPresent | pullPolicy: IfNotPresent @@ -196,3 +212,5 @@ node: node: chain: "chronicle" command: "node-chronicle" + + diff --git a/crates/chronicle-domain-lint/Cargo.toml b/crates/chronicle-domain-lint/Cargo.toml index a1a6db246..4261e6d58 100644 --- a/crates/chronicle-domain-lint/Cargo.toml +++ b/crates/chronicle-domain-lint/Cargo.toml @@ -2,7 +2,7 @@ build = "build.rs" edition = "2021" name = "chronicle-domain-lint" -version = "0.7.5" +version = "0.7.6" [dependencies] chronicle = { path = "../chronicle" } diff --git a/crates/chronicle-telemetry/src/telemetry.rs b/crates/chronicle-telemetry/src/telemetry.rs index 0778d0463..353fe5ad7 100644 --- a/crates/chronicle-telemetry/src/telemetry.rs +++ b/crates/chronicle-telemetry/src/telemetry.rs @@ -1,6 +1,4 @@ -use std::net::SocketAddr; -use opentelemetry_otlp::WithExportConfig; use tracing::subscriber::set_global_default; use tracing_flame::FlameLayer; use tracing_log::{log::LevelFilter, LogTracer}; @@ -31,11 +29,11 @@ macro_rules! stdio_layer { } macro_rules! oltp_exporter_layer { - ( $address: expr ) => {{ + () => {{ let tracer = opentelemetry_otlp::new_pipeline() .tracing() .with_exporter( - opentelemetry_otlp::new_exporter().tonic().with_endpoint($address.to_string()), + opentelemetry_otlp::new_exporter().tonic().with_env(), ) .install_simple() .expect("Failed to install OpenTelemetry tracer"); @@ -61,14 +59,14 @@ impl Drop for OptionalDrop { } pub fn telemetry( - collector_endpoint: Option, + otel_enable: bool, console_logging: ConsoleLogging, ) -> impl Drop { - full_telemetry(collector_endpoint, None, console_logging) + full_telemetry(otel_enable, None, console_logging) } pub fn full_telemetry( - exporter_port: Option, + otel_export: bool, flame_file: Option<&str>, console_logging: ConsoleLogging, ) -> impl Drop { @@ -82,35 +80,35 @@ pub fn full_telemetry( LogTracer::init_with_filter(LevelFilter::Trace).ok(); let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("error")); - match (exporter_port, flame_layer, console_logging) { - (Some(otel), Some(flame_layer), ConsoleLogging::Json) => set_global_default( + match (otel_export, flame_layer, console_logging) { + (true, Some(flame_layer), ConsoleLogging::Json) => set_global_default( Registry::default() .with(env_filter) .with(flame_layer) .with(stdio_layer!().json()) - .with(oltp_exporter_layer!(otel)), + .with(oltp_exporter_layer!()), ), - (Some(otel), Some(flame_layer), ConsoleLogging::Pretty) => set_global_default( + (true, Some(flame_layer), ConsoleLogging::Pretty) => set_global_default( Registry::default() .with(env_filter) .with(flame_layer) .with(stdio_layer!().pretty()) - .with(oltp_exporter_layer!(otel)), + .with(oltp_exporter_layer!()), ), - (Some(otel), Some(flame_layer), ConsoleLogging::Off) => set_global_default( + (true, Some(flame_layer), ConsoleLogging::Off) => set_global_default( Registry::default() .with(env_filter) .with(flame_layer) - .with(oltp_exporter_layer!(otel)), + .with(oltp_exporter_layer!()), ), - (None, Some(flame_layer), ConsoleLogging::Json) => set_global_default( + (false, Some(flame_layer), ConsoleLogging::Json) => set_global_default( Registry::default() .with(env_filter) .with(flame_layer) .with(stdio_layer!().json()), ), - (None, Some(flame_layer), ConsoleLogging::Pretty) => { + (false, Some(flame_layer), ConsoleLogging::Pretty) => { cfg_if::cfg_if! { if #[cfg(feature = "tokio-tracing")] { set_global_default(Registry::default() @@ -127,25 +125,25 @@ pub fn full_telemetry( } } }, - (Some(otel), None, ConsoleLogging::Json) => set_global_default( + (true, None, ConsoleLogging::Json) => set_global_default( Registry::default() .with(env_filter) .with(stdio_layer!().json()) - .with(oltp_exporter_layer!(otel)), + .with(oltp_exporter_layer!()), ), - (Some(otel), None, ConsoleLogging::Pretty) => set_global_default( + (true, None, ConsoleLogging::Pretty) => set_global_default( Registry::default() .with(env_filter) .with(stdio_layer!().pretty()) - .with(oltp_exporter_layer!(otel)), + .with(oltp_exporter_layer!()), ), - (Some(otel), None, ConsoleLogging::Off) => { - let otel_layer = oltp_exporter_layer!(otel); + (true, None, ConsoleLogging::Off) => { + let otel_layer = oltp_exporter_layer!(); set_global_default(Registry::default().with(env_filter).with(otel_layer)) }, - (None, None, ConsoleLogging::Json) => + (false, None, ConsoleLogging::Json) => set_global_default(Registry::default().with(env_filter).with(stdio_layer!().json())), - (None, None, ConsoleLogging::Pretty) => { + (false, None, ConsoleLogging::Pretty) => { cfg_if::cfg_if! { if #[cfg(feature = "tokio-tracing")] { set_global_default(Registry::default() diff --git a/node/node-chronicle/src/chain_spec.rs b/node/node-chronicle/src/chain_spec.rs index 9133ac0bf..effc16352 100644 --- a/node/node-chronicle/src/chain_spec.rs +++ b/node/node-chronicle/src/chain_spec.rs @@ -133,3 +133,67 @@ fn testnet_genesis( chronicle: pallet_chronicle::GenesisConfig:: { ..Default::default() }, } } + + +pub fn chronicle_config() -> Result { + let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; + + log::info!("testnet configuration"); + Ok(ChainSpec::from_genesis( + // Name + "Chronicle Mainnet", + // ID + "chronicle", + ChainType::Local, + move || { + mainnet_genesis( + wasm_binary, + // Initial PoA authorities + vec![], + // Sudo account + get_account_id_from_seed::("Alice"), + // Pre-funded accounts + true, + ) + }, + // Bootnodes + vec![], + // Telemetry + None, + // Protocol ID + Some("chronicle"), + // Properties + None, + None, + // Extensions + None, + )) +} + +/// Configure initial storage state for FRAME modules. +fn mainnet_genesis( + wasm_binary: &[u8], + initial_authorities: Vec<(AuraId, GrandpaId)>, + root_key: AccountId, + _enable_println: bool, +) -> RuntimeGenesisConfig { + RuntimeGenesisConfig { + system: SystemConfig { + // Add Wasm runtime to storage. + code: wasm_binary.to_vec(), + ..Default::default() + }, + aura: AuraConfig { + authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), + }, + grandpa: GrandpaConfig { + authorities: initial_authorities.iter().map(|x| (x.1.clone(), 1)).collect(), + ..Default::default() + }, + sudo: SudoConfig { + // Assign network admin rights. + key: Some(root_key), + }, + chronicle: pallet_chronicle::GenesisConfig:: { ..Default::default() }, + } +} diff --git a/node/node-chronicle/src/command.rs b/node/node-chronicle/src/command.rs index 05ee8e997..c425aed2e 100644 --- a/node/node-chronicle/src/command.rs +++ b/node/node-chronicle/src/command.rs @@ -14,7 +14,7 @@ use try_runtime_cli::block_building_info::timestamp_with_aura_info; impl SubstrateCli for Cli { fn impl_name() -> String { - "Substrate Node".into() + "Chronicle Substrate Node".into() } fn impl_version() -> String { @@ -30,7 +30,7 @@ impl SubstrateCli for Cli { } fn support_url() -> String { - "support.anonymous.an".into() + "support.chronicle.works".into() } fn copyright_start_year() -> i32 { @@ -41,6 +41,7 @@ impl SubstrateCli for Cli { Ok(match id { "" | "dev" => Box::new(chain_spec::development_config()?), "local" => Box::new(chain_spec::local_testnet_config()?), + "chronicle" => Box::new(chain_spec::chronicle_config()?), path => Box::new(chain_spec::ChainSpec::from_json_file(std::path::PathBuf::from(path))?), }) diff --git a/terraform/chronicle-substrate/.gitignore b/terraform/chronicle-substrate/.gitignore new file mode 100644 index 000000000..9de4d2724 --- /dev/null +++ b/terraform/chronicle-substrate/.gitignore @@ -0,0 +1,25 @@ +# Local .terraform directories +**/.terraform/* +*.terraform.lock.hcl +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which might contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version control +# as they are data points which are potentially sensitive and subject to change depending on the environment. +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# should not be checked into version control +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/terraform/chronicle-substrate/bootnode.tf b/terraform/chronicle-substrate/bootnode.tf new file mode 100644 index 000000000..3b1cf162d --- /dev/null +++ b/terraform/chronicle-substrate/bootnode.tf @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------------ +# Bootnode deployment +#------------------------------------------------------------------------------ +resource "helm_release" "bootnode" { + name = var.helm_release_name + repository = "https://paritytech.github.io/helm-charts/" + chart = "node" + namespace = var.kubernetes_namespace + + values = [ + templatefile("templates/bootnode.tmpl", { + helm_release_name = var.helm_release_name + + kubernetes_namespace = var.kubernetes_namespace + kubernetes_image_pull_secrets = var.kubernetes_image_pull_secrets + + chronicle_node_repository = var.chronicle-node-repository + bootnode_key = var.bootnode-key + + node = { + command = "node-chronicle" + } + }) + ] +} diff --git a/terraform/chronicle-substrate/provider.tf b/terraform/chronicle-substrate/provider.tf new file mode 100644 index 000000000..d9112c608 --- /dev/null +++ b/terraform/chronicle-substrate/provider.tf @@ -0,0 +1,10 @@ +provider "kubernetes" { + config_path = "~/.kube/config" +} + +provider "helm" { + kubernetes { + config_path = "~/.kube/config" + } +} + diff --git a/terraform/chronicle-substrate/templates/bootnode.tmpl b/terraform/chronicle-substrate/templates/bootnode.tmpl new file mode 100644 index 000000000..49882025c --- /dev/null +++ b/terraform/chronicle-substrate/templates/bootnode.tmpl @@ -0,0 +1,115 @@ +fullnameOverride: bootnode +image: + repository: ${chronicle_node_repository} + tag: local + pullPolicy: IfNotPresent +node: + chain: local + customChainspec: true # see extraInitContainers, chainspec-generator + role: full + replicas: 1 + command: node-chronicle + chainData: + pruning: archive + storageClass: "" + chainKeystore: + mountInMemory: + enabled: true + perNodeServices: + relayP2pService: + enabled: true + vault: + enabled: false + keys: + - name: aura + type: aura + scheme: sr25519 + vaultPath: kv/chronicle_substrate/aura # path at which the secret is located in Vault + vaultKey: secretSeed # key under which the secret value is stored in Vault + extraDerivation: "//$${HOSTNAME}//aura" # allows to have unique derived keys for each pod of the statefulset + nodeKey: + name: bootnode_key + vaultPath: kv/chronicle-substrate/bootnode_key + vaultKey: key + env: + - name: VAULT_LOG_LEVEL + value: "debug" + + customNodeKey: ${bootnode_key} + flags: + - "--allow-private-ipv4" + - "--discover-local" + +ingress: + enabled: false + annotations: + kubernetes.io/ingress.class: TODO + external-dns.alpha.kubernetes.io/target: TODO + cert-manager.io/cluster-issuer: TODO + rules: + - host: chronicle-bootnode.paravela.io + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: bootnode + port: + number: 9944 + tls: + - secretName: chronicle-bootnode.paravela.io + hosts: + - chronicle-bootnode.paravela.io + +# Generate chainspec, and expose it as url +extraInitContainers: + - name: chainspec-generator + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/role: 'bootnode' + vault.hashicorp.com/agent-inject-secret-bootnode_key: 'kv/chronicle_substrate/bootnode_key' + vault.hashicorp.com/agent-inject-secret-bootnode_peer: 'kv/chronicle_substrate/bootnode_peer' + securityContext: + runAsUser: 0 + command: [ "/bin/bash" ] + args: + - -c + - | + apt update && apt install -y jq && + {{ .Values.node.command }} build-spec --chain {{ .Values.node.chain }} > base.json + if [ ! -s base.json ]; then + echo "base.json is empty or missing." + exit 1 + fi + echo '{"bootNodes":["/dns/bootnode-0/tcp/30333/p2p/12D3KooWRpzRTivvJ5ySvgbFnPeEE6rDhitQKL1fFJvvBGhnenSk"]}' > override1.json + jq -s '.[0] * .[1]' base.json override1.json | sed 's/1e+18/1000000000000000000/' > plain.json + if [ ! -s plain.json ]; then + echo "plain.json is empty or malformed after jq processing." + exit 1 + fi + cut -c -256 plain.json + {{ .Values.node.command }} build-spec --chain plain.json --raw > chainspec.json + if [ ! -s chainspec.json ]; then + echo "chainspec.json failed to generate." + exit 1 + fi + cp chainspec.json {{ .Values.node.customChainspecPath }} + volumeMounts: + - mountPath: /chain-data + name: chain-data + env: + - name: VAULT_LOG_LEVEL + value: "debug" +extraContainers: + - name: chainspec + image: nginxinc/nginx-unprivileged:stable + ports: + - containerPort: 8080 + name: web + volumeMounts: + - name: chain-data + subPath: chainspec.json + mountPath: /usr/share/nginx/html/chainspec.json + readOnly: true diff --git a/terraform/chronicle-substrate/variables.tf b/terraform/chronicle-substrate/variables.tf new file mode 100644 index 000000000..289fb9836 --- /dev/null +++ b/terraform/chronicle-substrate/variables.tf @@ -0,0 +1,60 @@ +variable "chronicle-node-repository" { + type = string + description = "The repository to pull the chronicle node image from" + default = "node-chronicle-arm64" +} + +variable "kubernetes_namespace" { + type = string + description = "The Kubernetes namespace to deploy Vault into" + default = "vault" +} + +variable "kubernetes_sa_name" { + type = string + description = "The Kubernetes Service Account that Vault will use" + default = "vault-sa" +} + +variable "kubernetes_image_pull_secrets" { + type = list(string) + description = "A list of Kubernetes secrets that hold any required image registry credentials" + default = null +} + +variable "kubernetes_extra_secret_environment_variables" { + type = list(map(string)) + description = "A list of maps referencing Kubernetes secrets and their environment variable to mount to the Vault pods" + default = null +} + + +variable "bootnode-key" { + type = string + description = "The bootnode key" + default = "80c30ac6ba927c6e5c0c9681aa9674f1d181d180853bcd3485cee9d18e931238" +} + +variable "helm_release_name" { + type = string + description = "The name of the Helm release" + default = "chronicle-substrate" +} + +variable "helm_chart_name" { + type = string + description = "The chart name in the Helm repository" + default = "node" +} + +variable "helm_repository" { + type = string + description = "The location of the Helm repository" + default = "https://paritytech.github.io/helm-charts/" +} + +variable "vault_replicas" { + type = number + description = "The number of Vault replicas to deploy" + default = 3 +} diff --git a/terraform/chronicle/.gitignore b/terraform/chronicle/.gitignore new file mode 100644 index 000000000..00b1cb0d8 --- /dev/null +++ b/terraform/chronicle/.gitignore @@ -0,0 +1,25 @@ +# Local .terraform directories +**/.terraform/* +.terraform.lock.hcl +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which might contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version control +# as they are data points which are potentially sensitive and subject to change depending on the environment. +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# should not be checked into version control +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/terraform/signoz/.gitignore b/terraform/signoz/.gitignore new file mode 100644 index 000000000..5815822bc --- /dev/null +++ b/terraform/signoz/.gitignore @@ -0,0 +1,26 @@ +# Local .terraform directories +**/.terraform/* +*.terraform.lock.hcl + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which might contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version control +# as they are data points which are potentially sensitive and subject to change depending on the environment. +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# should not be checked into version control +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/terraform/signoz/output.tf b/terraform/signoz/output.tf new file mode 100644 index 000000000..4bf1b4a3e --- /dev/null +++ b/terraform/signoz/output.tf @@ -0,0 +1,32 @@ +output "otel_collector_service_name" { + value = "${helm_release.signoz.name}-otel-collector" + description = "The service name of the OpenTelemetry collector deployed with SigNoz." +} + +output "otel_collector_service_url" { + value = "http://${helm_release.signoz.name}-otel-collector.${var.signoz_namespace}.svc.cluster.local" + description = "The URL to access the OpenTelemetry collector service." +} + +output "otel_collector_service_port" { + value = "4317" + description = "The default gRPC port for the OpenTelemetry collector service." +} + +output "signoz_frontend_service_name" { + value = "${helm_release.signoz.name}-frontend" + description = "The service name of the SigNoz frontend." +} + +output "signoz_frontend_service_url" { + value = "http://${helm_release.signoz.name}-frontend.${var.signoz_namespace}.svc.cluster.local" + description = "The URL to access the SigNoz frontend service." +} + +output "signoz_frontend_service_port" { + value = "3000" + description = "The default port for the SigNoz frontend service." +} + + + diff --git a/terraform/signoz/provider.tf b/terraform/signoz/provider.tf new file mode 100644 index 000000000..912fdd629 --- /dev/null +++ b/terraform/signoz/provider.tf @@ -0,0 +1,9 @@ +provider "kubernetes" { + config_path = "~/.kube/config" +} + +provider "helm" { + kubernetes { + config_path = "~/.kube/config" + } +} diff --git a/terraform/signoz/signoz.tf b/terraform/signoz/signoz.tf new file mode 100644 index 000000000..97cb64dd4 --- /dev/null +++ b/terraform/signoz/signoz.tf @@ -0,0 +1,22 @@ +resource "helm_release" "signoz" { + name = "signoz" + repository = var.signoz_helm_repository + chart = var.signoz_helm_chart_name + namespace = var.signoz_namespace + create_namespace = true + + set { + name = "frontend.replicaCount" + value = var.signoz_fe_replicas + } + + set { + name = "queryService.replicaCount" + value = var.signoz_query_replicas + } + + set { + name = "storage.size" + value = var.signoz_storage_size + } +} diff --git a/terraform/signoz/variables.tf b/terraform/signoz/variables.tf new file mode 100644 index 000000000..e7a2f9ebb --- /dev/null +++ b/terraform/signoz/variables.tf @@ -0,0 +1,37 @@ +variable "signoz_namespace" { + type = string + description = "The Kubernetes namespace to deploy SigNoz into" + default = "observabilty" +} + +variable "signoz_helm_chart_name" { + type = string + description = "The chart name in the Helm repository for SigNoz" + default = "signoz" +} + +variable "signoz_helm_repository" { + type = string + description = "The location of the Helm repository for SigNoz" + default = "https://charts.signoz.io/" +} + +variable "signoz_fe_replicas" { + type = number + description = "The number of SigNoz replicas to deploy" + default = 1 +} + + +variable "signoz_query_replicas" { + type = number + description = "The number of SigNoz replicas to deploy" + default = 1 +} + +variable "signoz_storage_size" { + type = string + description = "The size, in Gi, of the storage volume for SigNoz" + default = "20" +} + diff --git a/terraform/vault-configuration/.gitignore b/terraform/vault-configuration/.gitignore new file mode 100644 index 000000000..9de4d2724 --- /dev/null +++ b/terraform/vault-configuration/.gitignore @@ -0,0 +1,25 @@ +# Local .terraform directories +**/.terraform/* +*.terraform.lock.hcl +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Exclude all .tfvars files, which might contain sensitive data, such as +# password, private keys, and other secrets. These should not be part of version control +# as they are data points which are potentially sensitive and subject to change depending on the environment. +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# should not be checked into version control +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Ignore CLI configuration files +.terraformrc +terraform.rc diff --git a/terraform/vault-configuration/Makefile b/terraform/vault-configuration/Makefile new file mode 100644 index 000000000..b2ca114ed --- /dev/null +++ b/terraform/vault-configuration/Makefile @@ -0,0 +1,11 @@ +TF_VAR_bootnode_key := $(shell docker run --platform=linux/amd64 parity/polkadot key generate-node-key 2>/dev/null) +export TF_VAR_bootnode_key + +TF_VAR_bootnode_peer_id := $(shell echo $(TF_VAR_bootnode_key) | docker run --platform=linux/amd64 -i parity/polkadot key inspect-node-key) +export TF_VAR_bootnode_peer_id + +TF_VAR_aura_key := $(shell docker run --platform=linux/amd64 parity/polkadot key generate --network substrate --output-type json) +export TF_VAR_aura_key + +apply: + VAULT_TLS_SERVER_NAME=vault-0.vault-internal terraform apply diff --git a/terraform/vault-configuration/policies/admin.hcl b/terraform/vault-configuration/policies/admin.hcl new file mode 100644 index 000000000..02c929008 --- /dev/null +++ b/terraform/vault-configuration/policies/admin.hcl @@ -0,0 +1,58 @@ +# Manage auth methods broadly across Vault +path "auth/*" +{ + capabilities = ["create", "read", "update", "delete", "list", "sudo"] +} + +# Create, update, and delete auth methods +path "sys/auth/*" +{ + capabilities = ["create", "update", "delete", "sudo"] +} + +# List auth methods +path "sys/auth" +{ + capabilities = ["read"] +} + +# List existing policies +path "sys/policies/acl" +{ + capabilities = ["list"] +} + +# Create and manage ACL policies +path "sys/policies/acl/*" +{ + capabilities = ["create", "read", "update", "delete", "list", "sudo"] +} + +# List, create, update, and delete key/value secrets +path "secret/*" +{ + capabilities = ["create", "read", "update", "delete", "list", "sudo"] +} + +# Manage secrets engines +path "sys/mounts/*" +{ + capabilities = ["create", "read", "update", "delete", "list", "sudo"] +} + +# List existing secrets engines. +path "sys/mounts" +{ + capabilities = ["read"] +} + +# Read health checks +path "sys/health" +{ + capabilities = ["read", "sudo"] +} + +path "kv/*" +{ + capabilities = ["create", "read", "update", "delete", "list"] +} diff --git a/terraform/vault-configuration/policies/bootnode.hcl b/terraform/vault-configuration/policies/bootnode.hcl new file mode 100644 index 000000000..13c870352 --- /dev/null +++ b/terraform/vault-configuration/policies/bootnode.hcl @@ -0,0 +1,4 @@ +path "kv/chronicle-substrate/*" +{ + capabilities = ["create", "read", "update", "delete", "list"] +} diff --git a/terraform/vault-configuration/policies/chronicle.hcl b/terraform/vault-configuration/policies/chronicle.hcl new file mode 100644 index 000000000..0966630e5 --- /dev/null +++ b/terraform/vault-configuration/policies/chronicle.hcl @@ -0,0 +1,4 @@ +path "kv/chronicle/*" +{ + capabilities = ["create", "read", "update", "delete", "list"] +} diff --git a/terraform/vault-configuration/provider.tf b/terraform/vault-configuration/provider.tf new file mode 100644 index 000000000..da874ddf3 --- /dev/null +++ b/terraform/vault-configuration/provider.tf @@ -0,0 +1,4 @@ +provider vault { + address = "https://localhost:8200" + ca_cert_file = var.vault_service_ca +} diff --git a/terraform/vault-configuration/variables.tf b/terraform/vault-configuration/variables.tf new file mode 100644 index 000000000..a0194e067 --- /dev/null +++ b/terraform/vault-configuration/variables.tf @@ -0,0 +1,48 @@ +variable "node_service_account" { + description = "The service account for the Chronicle Substrate node." + type = string + sensitive = false + default = "bootnode" +} + +variable "vault_token" { + description = "A vault access token with root permission" + type = string + sensitive = true + default = "s.VL1OYyHeeQ1Qxyg9FqSyKjBe" +} + +variable "bootnode_key" { + description = "The bootnode key for the Chronicle Substrate node." + type = string + sensitive = true +} + +variable "aura_key" { + description = "The aura key for the Chronicle Substrate node." + type = string + default = < ca.crt + +apply: get-ca + terraform apply + +unseal: apply + kubectl exec --stdin=true --tty=true vault-0 --namespace vault -- vault operator init -key-shares=1 -key-threshold=1 + diff --git a/terraform/vault-deployment/ca.crt b/terraform/vault-deployment/ca.crt new file mode 100644 index 000000000..9f8cbd92b --- /dev/null +++ b/terraform/vault-deployment/ca.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDBTCCAe2gAwIBAgIIRowbf+1CpuEwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE +AxMKa3ViZXJuZXRlczAeFw0yNDA1MDIxNzI0NDVaFw0zNDA0MzAxNzI5NDVaMBUx +EzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCowu170MUmx0kLkx0HA9u/4pwqofAcz+pmu5r6NVtHp9CxqGFMO1Tsr8Yr +kz1rA+yIA4eU+8SF2fbhUNP8Ei6Sux1QkMCbVAHvII0l2JFhw4TUV1vT6Xcg1WTJ +7SyqavBtDLDl0wU0SJrx4YvXAacZQQtj//2u6m/VX1fFa8DtB0tcKMsE3zTicsdo +uI0w57EnVSsKzGY7nhB6NFlwq4CKlcjgQsm8Bi15Ahv+dnXjOoU967Kw6unMc5u9 +8eVcXKPfKuHOtMy60k18VxT4W9Tu6AbyTxOOSNp6lMKIxXpiXLN6oxXzmCHTUFf+ +ls56tOhfftuoGwuZZpPotO7mOtG9AgMBAAGjWTBXMA4GA1UdDwEB/wQEAwICpDAP +BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTz3ndg3ynImbZifOEIWO/U/fmcljAV +BgNVHREEDjAMggprdWJlcm5ldGVzMA0GCSqGSIb3DQEBCwUAA4IBAQBtMcla5REp +68IbAMz0/D/cRiXu+9GcZkY/kR26LYC9wnWZBJAEa8mhgSuMyWvGakD6hhMLOr2n +Q2BCMiPZDUs1toAnzzK1TqmeypBNxJhlS6QJlRQUqcrKYHr2asFpMpBghEn5Y4zN +G9uWIgvkWT32XDapcEsrrYdfbUYiZ66312RahCxYeLW6gq3TMR2vCf2NLqomFDzg +RDyR6KPfMovDDEnPbW5bWKXz1c6jC/sRZYrGzU05K0SCgIZrSM8EWnRNyaxUzCtK +WJOnCF5srd5uw/XL4jSJPOD7Rb5ZlOK9ong6GwsDiWPPqjJK6dmwy26sOKhyIeQR +WLMqyYe0y3Tm +-----END CERTIFICATE----- diff --git a/terraform/vault-deployment/locals.tf b/terraform/vault-deployment/locals.tf new file mode 100644 index 000000000..9160d928e --- /dev/null +++ b/terraform/vault-deployment/locals.tf @@ -0,0 +1,3 @@ +locals { + generate_tls_certs = true +} diff --git a/terraform/vault-deployment/provider.tf b/terraform/vault-deployment/provider.tf new file mode 100644 index 000000000..d9112c608 --- /dev/null +++ b/terraform/vault-deployment/provider.tf @@ -0,0 +1,10 @@ +provider "kubernetes" { + config_path = "~/.kube/config" +} + +provider "helm" { + kubernetes { + config_path = "~/.kube/config" + } +} + diff --git a/terraform/vault-deployment/templates/values.tmpl b/terraform/vault-deployment/templates/values.tmpl new file mode 100644 index 000000000..ff37497e9 --- /dev/null +++ b/terraform/vault-deployment/templates/values.tmpl @@ -0,0 +1,85 @@ +global: + tlsDisable: false + %{ if kubernetes_image_pull_secrets != null } + imagePullSecrets: + %{ for secret in kubernetes_image_pull_secrets } + - name: ${secret} + %{ endfor } + %{ endif } +injector: + enabled: ${vault_injector_enable} + logLevel: debug + image: + repository: ${vault_injector_image_repository} + tag: "${vault_injector_image_tag}" + agentImage: + repository: ${vault_image_repository} + tag: "${vault_image_tag}" +server: + serviceAccount: + create: false + name: ${kubernetes_vault_service_account} + logLevel: debug + image: + repository: ${vault_image_repository} + tag: "${vault_image_tag}" + dataStorage: + size: ${vault_data_storage_size}Gi + service: + enabled: true + type: ${kubernetes_vault_server_service_type} + ha: + enabled: true + replicas: ${vault_replica_count} + apiAddr: "https://$(VAULT_K8S_POD_NAME).svc:8200" + raft: + enabled: true + setNodeId: true + + config: | + ui = true + + listener "tcp" { + address = "[::]:8200" + cluster_address = "[::]:8201" + + tls_disable = false + tls_cert_file = "/vault/userconfig/${kubernetes_secret_name_tls_cert}/tls.crt" + tls_key_file = "/vault/userconfig/${kubernetes_secret_name_tls_cert}/tls.key" + tls_client_ca_file = "/vault/userconfig/${kubernetes_secret_name_tls_ca}/ca.crt" + + } + + storage "raft" { + path = "/vault/data" + + retry_join { + auto_join = "provider=k8s namespace=${kubernetes_namespace} label_selector=\"component=server,app.kubernetes.io/name=vault\"" + leader_tls_servername = "${vault_leader_tls_servername}" + leader_ca_cert_file = "/vault/userconfig/${kubernetes_secret_name_tls_ca}/ca.crt" + } + } + + seal "${vault_seal_method}" {} + + service_registration "kubernetes" {} + extraVolumes: + - type: secret + name: ${kubernetes_secret_name_tls_cert} + - type: secret + name: ${kubernetes_secret_name_tls_ca} + extraEnvironmentVars: + VAULT_CAPATH: /vault/userconfig/tls-ca/ca.crt + VAULT_ADDR: https://$(VAULT_K8S_POD_NAME).${helm_release_name}-internal:8200 + VAULT_SKIP_VERIFY: false + %{ if kubernetes_extra_secret_environment_variables != null } + extraSecretEnvironmentVars: + %{ for secret in kubernetes_extra_secret_environment_variables } + - envName: ${secret["envName"]} + secretName: ${secret["secretName"]} + secretKey: ${secret["secretKey"]} + %{ endfor } + %{ endif } +ui: + enabled: true + serviceType: ${kubernetes_vault_ui_service_type} diff --git a/terraform/vault-deployment/tls.tf b/terraform/vault-deployment/tls.tf new file mode 100644 index 000000000..277bda6dc --- /dev/null +++ b/terraform/vault-deployment/tls.tf @@ -0,0 +1,63 @@ +#------------------------------------------------------------------------------ +# Certificate Authority +#------------------------------------------------------------------------------ +resource "tls_private_key" "ca" { + count = local.generate_tls_certs ? 1 : 0 + + algorithm = "RSA" + ecdsa_curve = "P384" + rsa_bits = "2048" +} + + + +#------------------------------------------------------------------------------ +# Certificate +#------------------------------------------------------------------------------ +resource "tls_private_key" "vault_private_key" { + count = local.generate_tls_certs ? 1 : 0 + + algorithm = "RSA" + ecdsa_curve = "P384" + rsa_bits = "2048" +} + +locals { + dns_names = [for i in range(var.vault_replicas) : format("vault-%s.%s-internal", i, var.helm_chart_name)] +} + + +resource "tls_cert_request" "vault_cert_request" { + count = local.generate_tls_certs ? 1 : 0 + + private_key_pem = tls_private_key.vault_private_key[0].private_key_pem + + dns_names = concat(local.dns_names, [ + "*.vault.svc.${var.cluster_name}", + "*.vault.svc", + "*.vault" + ]) + ip_addresses = ["127.0.0.1"] + + subject { + common_name = "system:node:*.${var.kubernetes_namespace}.svc.${var.cluster_name}" + organization = "system:nodes" + } +} + +resource "kubernetes_certificate_signing_request_v1" "kubernetes_certificate_signing_request" { + metadata { + name = "${var.kubernetes_namespace}.vault.svc" + } + + spec { + usages = ["key encipherment", "digital signature", "server auth"] + signer_name = "kubernetes.io/kubelet-serving" + + request = tls_cert_request.vault_cert_request[0].cert_request_pem + + } + + auto_approve = false + +} diff --git a/terraform/vault-deployment/variables.tf b/terraform/vault-deployment/variables.tf new file mode 100644 index 000000000..5c0fc938c --- /dev/null +++ b/terraform/vault-deployment/variables.tf @@ -0,0 +1,126 @@ +# Kubernetes variables +variable "cluster_name" { + type = string + description = "The kubernetes cluster name" + default = "cluster.local" +} + +variable "kubernetes_namespace" { + type = string + description = "The Kubernetes namespace to deploy Vault into" + default = "vault" +} + +variable "kubernetes_sa_name" { + type = string + description = "The Kubernetes Service Account that Vault will use" + default = "vault-sa" +} + +variable "kubernetes_image_pull_secrets" { + type = list(string) + description = "A list of Kubernetes secrets that hold any required image registry credentials" + default = null +} + +variable "kubernetes_extra_secret_environment_variables" { + type = list(map(string)) + description = "A list of maps referencing Kubernetes secrets and their environment variable to mount to the Vault pods" + default = null +} + +variable "kubernetes_vault_server_service_type" { + type = string + description = "The kubernetes service type for the Vault service" + default = "ClusterIP" +} + +variable "kubernetes_vault_ui_service_type" { + type = string + description = "The kubernetes service type for the Vault UI" + default = "ClusterIP" +} + +variable "helm_release_name" { + type = string + description = "The name of the Helm release" + default = "vault" +} + +variable "helm_chart_name" { + type = string + description = "The chart name in the Helm repository" + default = "vault" +} + +variable "helm_repository" { + type = string + description = "The location of the Helm repository" + default = "https://helm.releases.hashicorp.com/" +} + +variable "vault_replicas" { + type = number + description = "The number of Vault replicas to deploy" + default = 1 +} + +variable "single_node_deployment" { + type = bool + description = "Set this if running in a single node environment like minikube or docker desktop" + default = true +} + +variable "vault_injector_enable" { + type = bool + description = "Whether or not to enable the Vault agent injector" + default = true +} + +variable "vault_injector_image_repository" { + type = string + description = "The repository to pull the Vault injector image from" + default = "hashicorp/vault-k8s" +} + +variable "vault_injector_image_tag" { + type = string + description = "The image tag to use when pulling the Vault injector image" + default = "1.4" +} + +variable "vault_image_repository" { + type = string + description = "The repository to pull the Vault image from" + default = "hashicorp/vault" +} + +variable "vault_image_tag" { + type = string + description = "The image tag to use when pulling the Vault image" + default = "1.16" +} + +variable "vault_data_storage_size" { + type = string + description = "The size, in Gi, of the data storage volume" + default = "10" +} + +variable "vault_ui" { + type = bool + description = "Enable the Vault UI" + default = true +} + +variable "vault_seal_method" { + type = string + description = "The Vault seal method to use" + default = "shamir" +} + +variable "vault_enable_audit" { + type = bool + description = "Enables Vault audit storage" + default = true +} diff --git a/terraform/vault-deployment/vault.tf b/terraform/vault-deployment/vault.tf new file mode 100644 index 000000000..7fc1c7cad --- /dev/null +++ b/terraform/vault-deployment/vault.tf @@ -0,0 +1,79 @@ +#------------------------------------------------------------------------------ +# Kubernetes resources +#------------------------------------------------------------------------------ +resource "kubernetes_namespace" "vault" { + metadata { + name = var.kubernetes_namespace + } +} + + +resource "kubernetes_secret" "tls" { + metadata { + name = "tls" + namespace = kubernetes_namespace.vault.metadata.0.name + } + + data = { + "tls.crt" = kubernetes_certificate_signing_request_v1.kubernetes_certificate_signing_request.certificate + "tls.key" = tls_private_key.vault_private_key[0].private_key_pem + } + + type = "kubernetes.io/tls" +} + +resource "kubernetes_secret" "tls_ca" { + metadata { + name = "tls-ca" + namespace = kubernetes_namespace.vault.metadata.0.name + } + + data = { + "ca.crt" = file("ca.crt") + } +} + + +resource "kubernetes_service_account" "vault" { + metadata { + name = var.kubernetes_sa_name + namespace = var.kubernetes_namespace + annotations = null + } +} + +#------------------------------------------------------------------------------ +# Vault deployment +#------------------------------------------------------------------------------ +resource "helm_release" "vault" { + name = var.helm_release_name + repository = var.helm_repository + chart = var.helm_chart_name + namespace = var.kubernetes_namespace + + values = [ + templatefile("templates/values.tmpl", { + helm_release_name = var.helm_release_name + + kubernetes_namespace = var.kubernetes_namespace + kubernetes_vault_service_account = kubernetes_service_account.vault.metadata.0.name + kubernetes_secret_name_tls_cert = kubernetes_secret.tls.metadata.0.name + kubernetes_secret_name_tls_ca = kubernetes_secret.tls_ca.metadata.0.name + kubernetes_image_pull_secrets = var.kubernetes_image_pull_secrets + kubernetes_extra_secret_environment_variables = var.kubernetes_extra_secret_environment_variables + kubernetes_vault_server_service_type = var.kubernetes_vault_server_service_type + kubernetes_vault_ui_service_type = var.kubernetes_vault_ui_service_type + + vault_replica_count = var.vault_replicas + vault_injector_enable = var.vault_injector_enable + vault_injector_image_repository = var.vault_injector_image_repository + vault_injector_image_tag = "${var.vault_injector_image_tag}" + vault_image_repository = var.vault_image_repository + vault_image_tag = "${var.vault_image_tag}" + vault_data_storage_size = var.vault_data_storage_size + vault_leader_tls_servername = "vault.svc" + vault_seal_method = var.vault_seal_method + single_node_deployment = var.single_node_deployment + }) + ] +}