@@ -3,17 +3,40 @@ package actions
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "strings"
6
7
7
8
rhtasv1alpha1 "github.com/securesign/operator/api/v1alpha1"
9
+ "github.com/securesign/operator/internal/controller/annotations"
8
10
"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"
9
14
"github.com/securesign/operator/internal/controller/constants"
10
15
"github.com/securesign/operator/internal/controller/labels"
11
16
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"
12
20
"k8s.io/apimachinery/pkg/api/meta"
13
21
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22
+ "k8s.io/apimachinery/pkg/util/intstr"
14
23
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
15
24
)
16
25
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
+
17
40
type deployAction struct {
18
41
action.BaseAction
19
42
}
@@ -39,51 +62,41 @@ func (i deployAction) CanHandle(ctx context.Context, instance *rhtasv1alpha1.Tim
39
62
40
63
func (i deployAction ) Handle (ctx context.Context , instance * rhtasv1alpha1.TimestampAuthority ) * action.Result {
41
64
var (
42
- updated bool
43
- err error
65
+ result controllerutil. OperationResult
66
+ err error
44
67
)
45
68
46
69
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
66
75
}
67
76
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 {
70
91
Type : TSAServerCondition ,
71
92
Status : metav1 .ConditionFalse ,
72
93
Reason : constants .Failure ,
73
94
Message : err .Error (),
74
95
ObservedGeneration : instance .Generation ,
75
96
})
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 )
84
97
}
85
98
86
- if updated {
99
+ if result != controllerutil . OperationResultNone {
87
100
meta .SetStatusCondition (& instance .Status .Conditions , metav1.Condition {
88
101
Type : TSAServerCondition ,
89
102
Status : metav1 .ConditionFalse ,
@@ -96,3 +109,214 @@ func (i deployAction) Handle(ctx context.Context, instance *rhtasv1alpha1.Timest
96
109
return i .Continue ()
97
110
}
98
111
}
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
+ }
0 commit comments