Skip to content

Commit 02415fd

Browse files
Merge pull request #824 from securesign/migrate_tsa
[SECURESIGN-1393] Migrate tsa
2 parents b11d499 + 07ad99d commit 02415fd

File tree

3 files changed

+262
-345
lines changed

3 files changed

+262
-345
lines changed

internal/controller/tsa/actions/deployment.go

+256-32
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,40 @@ package actions
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1"
9+
"github.com/securesign/operator/internal/controller/annotations"
810
"github.com/securesign/operator/internal/controller/common/action"
11+
cutils "github.com/securesign/operator/internal/controller/common/utils"
12+
"github.com/securesign/operator/internal/controller/common/utils/kubernetes"
13+
"github.com/securesign/operator/internal/controller/common/utils/kubernetes/ensure"
914
"github.com/securesign/operator/internal/controller/constants"
1015
"github.com/securesign/operator/internal/controller/labels"
1116
tsaUtils "github.com/securesign/operator/internal/controller/tsa/utils"
17+
"golang.org/x/exp/maps"
18+
apps "k8s.io/api/apps/v1"
19+
core "k8s.io/api/core/v1"
1220
"k8s.io/apimachinery/pkg/api/meta"
1321
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
"k8s.io/apimachinery/pkg/util/intstr"
1423
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1524
)
1625

26+
const (
27+
chainVolumeName = "tsa-cert-chain"
28+
fileSignerVolumeName = "tsa-file-signer-config"
29+
tinkSignerVolumeName = "tsa-tink-signer-config"
30+
ntpConfigVolumeName = "ntp-config"
31+
authVolumeName = "auth"
32+
secretMountPath = "/var/run/secrets/tas"
33+
authMountPath = secretMountPath + "/auth"
34+
certChainMountPath = secretMountPath + "/certificate_chain"
35+
fileSignerMountPath = secretMountPath + "/file_signer"
36+
tinkSignerMountPath = secretMountPath + "/tink_signer"
37+
NtpMountPath = secretMountPath + "/ntp_config"
38+
)
39+
1740
type deployAction struct {
1841
action.BaseAction
1942
}
@@ -39,51 +62,41 @@ func (i deployAction) CanHandle(ctx context.Context, instance *rhtasv1alpha1.Tim
3962

4063
func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.TimestampAuthority) *action.Result {
4164
var (
42-
updated bool
43-
err error
65+
result controllerutil.OperationResult
66+
err error
4467
)
4568

4669
labels := labels.For(ComponentName, DeploymentName, instance.Name)
47-
deployment, err := tsaUtils.CreateTimestampAuthorityDeployment(instance, DeploymentName, RBACName, labels)
48-
if err != nil {
49-
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
50-
Type: TSAServerCondition,
51-
Status: metav1.ConditionFalse,
52-
Reason: constants.Failure,
53-
Message: err.Error(),
54-
ObservedGeneration: instance.Generation,
55-
})
56-
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
57-
Type: constants.Ready,
58-
Status: metav1.ConditionFalse,
59-
Reason: constants.Failure,
60-
Message: err.Error(),
61-
ObservedGeneration: instance.Generation,
62-
})
63-
}
64-
if err = controllerutil.SetControllerReference(instance, deployment, i.Client.Scheme()); err != nil {
65-
return i.Failed(fmt.Errorf("could not set controller reference for Deployment: %w", err))
70+
71+
caRef := cutils.TrustedCAAnnotationToReference(instance.Annotations)
72+
// override if spec.trustedCA is defined
73+
if instance.Spec.TrustedCA != nil {
74+
caRef = instance.Spec.TrustedCA
6675
}
6776

68-
if updated, err = i.Ensure(ctx, deployment); err != nil {
69-
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
77+
if result, err = kubernetes.CreateOrUpdate(ctx, i.Client,
78+
&apps.Deployment{
79+
ObjectMeta: metav1.ObjectMeta{
80+
Name: DeploymentName,
81+
Namespace: instance.Namespace,
82+
},
83+
},
84+
i.ensureDeployment(instance, RBACName, labels),
85+
ensure.ControllerReference[*apps.Deployment](instance, i.Client),
86+
ensure.Labels[*apps.Deployment](maps.Keys(labels), labels),
87+
ensure.Proxy(),
88+
ensure.TrustedCA(caRef),
89+
); err != nil {
90+
return i.Error(ctx, fmt.Errorf("could not create TSA Server: %w", err), instance, metav1.Condition{
7091
Type: TSAServerCondition,
7192
Status: metav1.ConditionFalse,
7293
Reason: constants.Failure,
7394
Message: err.Error(),
7495
ObservedGeneration: instance.Generation,
7596
})
76-
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
77-
Type: constants.Ready,
78-
Status: metav1.ConditionFalse,
79-
Reason: constants.Failure,
80-
Message: err.Error(),
81-
ObservedGeneration: instance.Generation,
82-
})
83-
return i.FailedWithStatusUpdate(ctx, fmt.Errorf("could not create TSA Server: %w", err), instance)
8497
}
8598

86-
if updated {
99+
if result != controllerutil.OperationResultNone {
87100
meta.SetStatusCondition(&instance.Status.Conditions, metav1.Condition{
88101
Type: TSAServerCondition,
89102
Status: metav1.ConditionFalse,
@@ -96,3 +109,214 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Timest
96109
return i.Continue()
97110
}
98111
}
112+
113+
func (i deployAction) ensureDeployment(instance *rhtasv1alpha1.TimestampAuthority, sa string, labels map[string]string) func(*apps.Deployment) error {
114+
return func(dp *apps.Deployment) error {
115+
116+
appArgs := []string{
117+
"timestamp-server",
118+
"serve",
119+
"--host=0.0.0.0",
120+
"--port=3000",
121+
fmt.Sprintf("--log-type=%s", cutils.GetOrDefault(instance.GetAnnotations(), annotations.LogType, string(constants.Prod))),
122+
fmt.Sprintf("--certificate-chain-path=%s/certificate-chain.pem", certChainMountPath),
123+
fmt.Sprintf("--disable-ntp-monitoring=%v", !instance.Spec.NTPMonitoring.Enabled),
124+
}
125+
126+
spec := &dp.Spec
127+
spec.Replicas = cutils.Pointer[int32](1)
128+
spec.Selector = &metav1.LabelSelector{
129+
MatchLabels: labels,
130+
}
131+
132+
template := &spec.Template
133+
template.Labels = labels
134+
template.Spec.ServiceAccountName = sa
135+
136+
container := kubernetes.FindContainerByNameOrCreate(&template.Spec, DeploymentName)
137+
138+
chainVolume := kubernetes.FindVolumeByNameOrCreate(&template.Spec, chainVolumeName)
139+
if chainVolume.Secret == nil {
140+
chainVolume.Secret = &core.SecretVolumeSource{}
141+
}
142+
chainVolume.Secret.SecretName = instance.Status.Signer.CertificateChain.CertificateChainRef.Name
143+
chainVolume.Secret.Items = []core.KeyToPath{
144+
{
145+
Key: instance.Status.Signer.CertificateChain.CertificateChainRef.Key,
146+
Path: "certificate-chain.pem",
147+
},
148+
}
149+
150+
chainVolumeMount := kubernetes.FindVolumeMountByNameOrCreate(container, chainVolumeName)
151+
chainVolumeMount.MountPath = certChainMountPath
152+
chainVolumeMount.ReadOnly = true
153+
154+
if instance.Spec.NTPMonitoring.Enabled {
155+
if instance.Spec.NTPMonitoring.Config != nil {
156+
ntpConfigVolume := kubernetes.FindVolumeByNameOrCreate(&template.Spec, ntpConfigVolumeName)
157+
if ntpConfigVolume.ConfigMap == nil {
158+
ntpConfigVolume.ConfigMap = &core.ConfigMapVolumeSource{}
159+
}
160+
ntpConfigVolume.ConfigMap.Name = instance.Status.NTPMonitoring.Config.NtpConfigRef.Name
161+
162+
ntpConfigVolumeMount := kubernetes.FindVolumeMountByNameOrCreate(container, ntpConfigVolumeName)
163+
ntpConfigVolumeMount.ReadOnly = true
164+
ntpConfigVolumeMount.MountPath = NtpMountPath
165+
166+
appArgs = append(appArgs,
167+
fmt.Sprintf("--ntp-monitoring=%s/ntp-config.yaml", NtpMountPath),
168+
)
169+
}
170+
}
171+
172+
switch tsaUtils.GetSignerType(&instance.Spec.Signer) {
173+
case tsaUtils.FileType:
174+
{
175+
176+
fileSignerVolume := kubernetes.FindVolumeByNameOrCreate(&template.Spec, fileSignerVolumeName)
177+
if fileSignerVolume.Secret == nil {
178+
fileSignerVolume.Secret = &core.SecretVolumeSource{}
179+
}
180+
fileSignerVolume.Secret.SecretName = instance.Status.Signer.File.PrivateKeyRef.Name
181+
fileSignerVolume.Secret.Items = []core.KeyToPath{
182+
{
183+
Key: instance.Status.Signer.File.PrivateKeyRef.Key,
184+
Path: "private_key.pem",
185+
},
186+
}
187+
188+
fileSignerVolumeMount := kubernetes.FindVolumeMountByNameOrCreate(container, fileSignerVolumeName)
189+
fileSignerVolumeMount.MountPath = fileSignerMountPath
190+
fileSignerVolumeMount.ReadOnly = true
191+
192+
if instance.Status.Signer.File.PasswordRef != nil {
193+
fileSignerPasswordEnv := kubernetes.FindEnvByNameOrCreate(container, "SIGNER_PASSWORD")
194+
fileSignerPasswordEnv.ValueFrom = &core.EnvVarSource{
195+
SecretKeyRef: &core.SecretKeySelector{
196+
LocalObjectReference: core.LocalObjectReference{
197+
Name: instance.Status.Signer.File.PasswordRef.Name,
198+
},
199+
Key: instance.Status.Signer.File.PasswordRef.Key,
200+
},
201+
}
202+
}
203+
204+
appArgs = append(appArgs,
205+
"--timestamp-signer=file",
206+
fmt.Sprintf("--file-signer-key-path=%s/private_key.pem", fileSignerMountPath),
207+
"--file-signer-passwd=$(SIGNER_PASSWORD)",
208+
)
209+
}
210+
case tsaUtils.KmsType:
211+
{
212+
213+
if instance.Spec.Signer.Kms.Auth != nil {
214+
for _, env := range instance.Spec.Signer.Kms.Auth.Env {
215+
e := kubernetes.FindEnvByNameOrCreate(container, env.Name)
216+
e.ValueFrom = env.ValueFrom
217+
}
218+
219+
for _, secret := range instance.Spec.Signer.Kms.Auth.SecretMount {
220+
volumeName := fmt.Sprintf("%s-%s", authVolumeName, secret.Name)
221+
v := kubernetes.FindVolumeByNameOrCreate(&template.Spec, volumeName)
222+
if v.Secret == nil {
223+
v.Secret = &core.SecretVolumeSource{}
224+
}
225+
v.Secret.SecretName = secret.Name
226+
227+
vm := kubernetes.FindVolumeMountByNameOrCreate(container, volumeName)
228+
vm.MountPath = authMountPath
229+
vm.ReadOnly = true
230+
}
231+
}
232+
233+
appArgs = append(appArgs,
234+
"--timestamp-signer=kms",
235+
fmt.Sprintf("--kms-key-resource=%s", instance.Spec.Signer.Kms.KeyResource),
236+
)
237+
}
238+
case tsaUtils.TinkType:
239+
{
240+
241+
if instance.Spec.Signer.Tink.Auth != nil {
242+
for _, env := range instance.Spec.Signer.Tink.Auth.Env {
243+
e := kubernetes.FindEnvByNameOrCreate(container, env.Name)
244+
e.ValueFrom = env.ValueFrom
245+
}
246+
247+
if len(instance.Spec.Signer.Tink.Auth.SecretMount) > 0 {
248+
for _, secret := range instance.Spec.Signer.Tink.Auth.SecretMount {
249+
volumeName := fmt.Sprintf("%s-%s", authVolumeName, secret.Name)
250+
v := kubernetes.FindVolumeByNameOrCreate(&template.Spec, volumeName)
251+
if v.Secret == nil {
252+
v.Secret = &core.SecretVolumeSource{}
253+
}
254+
v.Secret.SecretName = secret.Name
255+
256+
vm := kubernetes.FindVolumeMountByNameOrCreate(container, volumeName)
257+
vm.MountPath = authMountPath
258+
vm.ReadOnly = true
259+
}
260+
}
261+
}
262+
263+
tinkSignerVolume := kubernetes.FindVolumeByNameOrCreate(&template.Spec, tinkSignerVolumeName)
264+
if tinkSignerVolume.Secret == nil {
265+
tinkSignerVolume.Secret = &core.SecretVolumeSource{}
266+
}
267+
tinkSignerVolume.Secret.SecretName = instance.Spec.Signer.Tink.KeysetRef.Name
268+
tinkSignerVolume.Secret.Items = []core.KeyToPath{
269+
{
270+
Key: instance.Spec.Signer.Tink.KeysetRef.Key,
271+
Path: "encryptedKeySet",
272+
},
273+
}
274+
275+
tinkSignerVolumeMount := kubernetes.FindVolumeMountByNameOrCreate(container, tinkSignerVolumeName)
276+
tinkSignerVolumeMount.MountPath = tinkSignerMountPath
277+
tinkSignerVolumeMount.ReadOnly = true
278+
279+
appArgs = append(appArgs,
280+
"--timestamp-signer=tink",
281+
fmt.Sprintf("--tink-key-resource=%s", instance.Spec.Signer.Tink.KeyResource),
282+
fmt.Sprintf("--tink-keyset-path=%s/encryptedKeySet", tinkSignerMountPath),
283+
)
284+
285+
if strings.HasPrefix(instance.Spec.Signer.Tink.KeyResource, "hcvault://") {
286+
appArgs = append(appArgs, "--tink-hcvault-token=$(VAULT_TOKEN)")
287+
}
288+
289+
}
290+
}
291+
292+
container.Image = constants.TimestampAuthorityImage
293+
container.Command = appArgs
294+
295+
port := kubernetes.FindPortByNameOrCreate(container, "3000-tcp")
296+
port.ContainerPort = 3000
297+
port.Protocol = core.ProtocolTCP
298+
299+
if container.LivenessProbe == nil {
300+
container.LivenessProbe = &core.Probe{}
301+
}
302+
if container.LivenessProbe.HTTPGet == nil {
303+
container.LivenessProbe.HTTPGet = &core.HTTPGetAction{}
304+
}
305+
container.LivenessProbe.HTTPGet.Path = "/ping"
306+
container.LivenessProbe.HTTPGet.Port = intstr.FromInt32(3000)
307+
container.LivenessProbe.InitialDelaySeconds = 5
308+
309+
if container.ReadinessProbe == nil {
310+
container.ReadinessProbe = &core.Probe{}
311+
}
312+
if container.ReadinessProbe.HTTPGet == nil {
313+
container.ReadinessProbe.HTTPGet = &core.HTTPGetAction{}
314+
}
315+
316+
container.ReadinessProbe.HTTPGet.Path = "/ping"
317+
container.ReadinessProbe.HTTPGet.Port = intstr.FromInt32(3000)
318+
container.ReadinessProbe.InitialDelaySeconds = 5
319+
320+
return nil
321+
}
322+
}

internal/controller/tsa/utils/common.go

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import (
44
"github.com/securesign/operator/api/v1alpha1"
55
)
66

7+
const (
8+
FileType = "file"
9+
KmsType = "kms"
10+
TinkType = "tink"
11+
)
12+
713
func IsFileType(instance *v1alpha1.TimestampAuthority) bool {
814
return GetSignerType(&instance.Spec.Signer) == FileType
915
}

0 commit comments

Comments
 (0)