-
Notifications
You must be signed in to change notification settings - Fork 3
feat(ledger): integrate Ledger v3 operator #490
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e0059ec
3971821
de1e325
c0fdbff
6aa2687
7f56f8c
ba358fc
abb0643
5740dcb
959063a
122316d
7261997
062bea6
2d6e7de
3daf0df
1b0cb68
b5bf838
9ca4316
9e85a4f
cfbc18e
e605fad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,3 +31,6 @@ charts | |
|
|
||
| # E2E test artifacts | ||
| tests/e2e/artifacts/ | ||
|
|
||
| # Local scratch files | ||
| tmp/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| FROM golang:1.26-alpine AS builder | ||
|
|
||
| RUN apk add --no-cache git | ||
|
|
||
| WORKDIR /src | ||
|
|
||
| COPY go.mod go.sum ./ | ||
| RUN go mod download | ||
|
|
||
| COPY . . | ||
|
|
||
| ARG VERSION=latest | ||
| ARG COMMIT=unknown | ||
| ARG LICENCE_PUBLIC_KEY_B64="" | ||
|
|
||
| WORKDIR /src/cmd | ||
|
|
||
| RUN set -e; \ | ||
| LDFLAGS="-X github.com/formancehq/operator/v3/cmd.Version=${VERSION} \ | ||
| -X github.com/formancehq/operator/v3/cmd.BuildDate=$(date +%s) \ | ||
| -X github.com/formancehq/operator/v3/cmd.Commit=${COMMIT}"; \ | ||
| if [ -n "$LICENCE_PUBLIC_KEY_B64" ]; then \ | ||
| LICENCE_PUBLIC_KEY="$(printf '%s' "$LICENCE_PUBLIC_KEY_B64" | base64 -d)"; \ | ||
| LDFLAGS="${LDFLAGS} -X 'github.com/formancehq/go-libs/v5/pkg/authn/licence.formancePublicKey=${LICENCE_PUBLIC_KEY}'"; \ | ||
| fi; \ | ||
| CGO_ENABLED=0 go build -buildvcs=false -o /usr/bin/operator -ldflags="${LDFLAGS}" . | ||
|
|
||
| FROM alpine:3.20 | ||
|
|
||
| RUN apk update && apk add --no-cache ca-certificates curl | ||
|
|
||
| ENTRYPOINT ["/usr/bin/operator"] | ||
|
|
||
| COPY --from=builder /usr/bin/operator /usr/bin/operator | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟠 [major] Operator container runs as root The final image stage does not declare an unprivileged user, so the operator process runs as root. This weakens containment if the operator is ever compromised. Suggestion: Add |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,9 +45,13 @@ compile: | |
| SAVE ARTIFACT main | ||
|
|
||
| build-image: | ||
| FROM core+final-image | ||
| ENTRYPOINT ["/usr/bin/operator"] | ||
| COPY --pass-args (+compile/main) /usr/bin/operator | ||
| ARG LICENCE_PUBLIC_KEY_B64="" | ||
| ARG EARTHLY_BUILD_SHA | ||
| FROM DOCKERFILE \ | ||
| --build-arg LICENCE_PUBLIC_KEY_B64=$LICENCE_PUBLIC_KEY_B64 \ | ||
| --build-arg VERSION=$tag \ | ||
| --build-arg COMMIT=$EARTHLY_BUILD_SHA \ | ||
| -f Dockerfile . | ||
|
Comment on lines
+48
to
+54
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
awk '/^build-image:/{active=1} active && /ARG tag=/{declared=NR} active && /\$tag/{used=NR; exit} END {
if (!declared || declared > used) exit 1
}' EarthfileRepository: formancehq/operator Length of output: 157 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '--- Earthfile excerpt ---\n'
nl -ba Earthfile | sed -n '35,70p'
printf '\n--- Search for ARG tag occurrences ---\n'
rg -n '^\s*ARG\s+tag(\s*=|$)|\$tag\b' EarthfileRepository: formancehq/operator Length of output: 248 🏁 Script executed: #!/bin/bash
set -euo pipefail
nl -ba Earthfile | sed -n '35,70p'
rg -n '^\s*ARG\s+tag(\s*=|$)|\$tag\b' EarthfileRepository: formancehq/operator Length of output: 197 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- Earthfile excerpt (35-70) ---'
sed -n '35,70p' Earthfile | awk '{printf "%4d %s\n", NR+34, $0}'
echo
echo '--- ARG tag / $tag references ---'
awk '
/^\s*ARG\s+tag(\s*=|$)/ { print FNR ":" $0 }
/\$tag\b/ { print FNR ":" $0 }
' EarthfileRepository: formancehq/operator Length of output: 1846 Declare 🤖 Prompt for AI Agents |
||
| ARG REPOSITORY=ghcr.io | ||
| ARG tag=latest | ||
| DO --pass-args core+SAVE_IMAGE --COMPONENT=operator --TAG=$tag | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /* | ||
| Copyright 2022. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| package v1beta1 | ||
|
|
||
| const GatewayBackendTLSSecretLabel = "formance.com/gateway-backend-tls" | ||
|
|
||
| // GatewayBackendTLS configures TLS when Gateway connects to a backend. | ||
| type GatewayBackendTLS struct { | ||
| // SecretName contains the CA used to verify the backend certificate. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| SecretName string `json:"secretName"` | ||
| // CASecretKey is the key containing the CA certificate. | ||
| // +optional | ||
| // +kubebuilder:default:="ca.crt" | ||
| CASecretKey string `json:"caSecretKey,omitempty"` | ||
| // ServerName is used for backend certificate verification. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| ServerName string `json:"serverName"` | ||
| } | ||
|
|
||
| // GatewayBackendRef selects the Kubernetes Service used by a Gateway route. | ||
| // When omitted, Gateway keeps using the module's historical Service. | ||
| type GatewayBackendRef struct { | ||
| // Name is the backend Service name in the Stack namespace. | ||
| // +kubebuilder:validation:MinLength=1 | ||
| Name string `json:"name"` | ||
| // Port is the backend Service port. | ||
| // +kubebuilder:validation:Minimum=1 | ||
| // +kubebuilder:validation:Maximum=65535 | ||
| Port int32 `json:"port"` | ||
| // TLS enables a verified TLS connection to the backend. | ||
| // +optional | ||
| TLS *GatewayBackendTLS `json:"tls,omitempty"` | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| /* | ||
| Copyright 2022. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| package v1beta1 | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
| ) | ||
|
|
||
| type GatewayGRPCAPISpec struct { | ||
| StackDependency `json:",inline"` | ||
| // Name indicates the module name (e.g. "ledger") | ||
| Name string `json:"name"` | ||
| // GRPCServices is the list of fully-qualified gRPC service names | ||
| // exposed by this module (e.g. "formance.ledger.v1.LedgerService") | ||
| GRPCServices []string `json:"grpcServices"` | ||
| // Port is the gRPC port on the backend service | ||
| //+optional | ||
| //+kubebuilder:default:=8081 | ||
| Port int32 `json:"port,omitempty"` | ||
| // BackendRef overrides the historical <name>-grpc Service. | ||
| // +optional | ||
| BackendRef *GatewayBackendRef `json:"backendRef,omitempty"` | ||
| } | ||
|
|
||
| type GatewayGRPCAPIStatus struct { | ||
| Status `json:",inline"` | ||
| //+optional | ||
| Ready bool `json:"ready,omitempty"` | ||
| } | ||
|
|
||
| //+kubebuilder:object:root=true | ||
| //+kubebuilder:subresource:status | ||
| //+kubebuilder:resource:scope=Cluster | ||
| //+kubebuilder:printcolumn:name="Stack",type=string,JSONPath=".spec.stack",description="Stack" | ||
| //+kubebuilder:printcolumn:name="Ready",type=string,JSONPath=".status.ready",description="Ready" | ||
|
|
||
| // GatewayGRPCAPI is the Schema for the GRPCAPIs API | ||
| type GatewayGRPCAPI struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
|
||
| Spec GatewayGRPCAPISpec `json:"spec,omitempty"` | ||
| Status GatewayGRPCAPIStatus `json:"status,omitempty"` | ||
| } | ||
|
|
||
| func (in *GatewayGRPCAPI) SetReady(b bool) { | ||
| in.Status.Ready = b | ||
| } | ||
|
|
||
| func (in *GatewayGRPCAPI) IsReady() bool { | ||
| return in.Status.Ready | ||
| } | ||
|
|
||
| func (in *GatewayGRPCAPI) SetError(s string) { | ||
| in.Status.Info = s | ||
| } | ||
|
|
||
| func (a GatewayGRPCAPI) GetStack() string { | ||
| return a.Spec.Stack | ||
| } | ||
|
|
||
| func (in *GatewayGRPCAPI) GetConditions() *Conditions { | ||
| return &in.Status.Conditions | ||
| } | ||
|
|
||
| //+kubebuilder:object:root=true | ||
|
|
||
| // GatewayGRPCAPIList contains a list of GatewayGRPCAPI | ||
| type GatewayGRPCAPIList struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ListMeta `json:"metadata,omitempty"` | ||
| Items []GatewayGRPCAPI `json:"items"` | ||
| } | ||
|
|
||
| func init() { | ||
| SchemeBuilder.Register(&GatewayGRPCAPI{}, &GatewayGRPCAPIList{}) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| /* | ||
| Copyright 2023. | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| */ | ||
|
|
||
| package v1beta1 | ||
|
|
||
| import ( | ||
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
|
||
| ledgerv1alpha1 "github.com/formancehq/ledger/misc/operator/api/v1alpha1" | ||
| ) | ||
|
|
||
| const DefaultLedgerConfigurationName = "default" | ||
|
|
||
| type LedgerConfigurationSpec struct { | ||
| // Stacks on which the configuration is applied. Can contain `*` to | ||
| // indicate a wildcard, following the same convention as Settings. | ||
| // +optional | ||
| Stacks []string `json:"stacks,omitempty"` | ||
|
|
||
| // Cluster is the base Ledger v3 Cluster specification. Stack-specific | ||
| // Settings and values owned by the Operator are applied on top of it. | ||
| // +optional | ||
| Cluster ledgerv1alpha1.ClusterSpec `json:"cluster,omitempty"` | ||
| } | ||
|
|
||
| // +kubebuilder:object:root=true | ||
| // +kubebuilder:resource:scope=Cluster | ||
| // LedgerConfiguration defines the base specification applied to every Ledger v3 | ||
| // Cluster targeted by spec.stacks. A configuration targeting a stack by name | ||
| // takes priority over a configuration targeting all stacks with `*`. | ||
| type LedgerConfiguration struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ObjectMeta `json:"metadata,omitempty"` | ||
|
|
||
| Spec LedgerConfigurationSpec `json:"spec,omitempty"` | ||
| } | ||
|
|
||
| func (in *LedgerConfiguration) GetStacks() []string { | ||
| return in.Spec.Stacks | ||
| } | ||
|
|
||
| func (in *LedgerConfiguration) IsWildcard() bool { | ||
| return len(in.Spec.Stacks) == 1 && in.Spec.Stacks[0] == "*" | ||
| } | ||
|
|
||
| // +kubebuilder:object:root=true | ||
|
|
||
| // LedgerConfigurationList contains a list of LedgerConfiguration | ||
| type LedgerConfigurationList struct { | ||
| metav1.TypeMeta `json:",inline"` | ||
| metav1.ListMeta `json:"metadata,omitempty"` | ||
| Items []LedgerConfiguration `json:"items"` | ||
| } | ||
|
|
||
| func init() { | ||
| SchemeBuilder.Register(&LedgerConfiguration{}, &LedgerConfigurationList{}) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Run the operator as a non-root user.
The final image defaults to root, weakening containment if the operator is compromised. Add an unprivileged user and switch to it after copying the binary.
Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Linters/SAST tools