Skip to content

Commit 372011c

Browse files
authored
SECURESIGN-1014 | Add support for Trusted Timestamp Authorities in SecureSign (#456)
* add tsa to operator * add file signer type * add kms signer type * add tink signer type * improvements * add testing * add fetch tsa certs to cli downloads * refactor, add leaf certs, add multiple intermediate cert support * improve testing, apply requested changes
1 parent 1d4ca37 commit 372011c

File tree

70 files changed

+9848
-211
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+9848
-211
lines changed

.github/workflows/main.yml

+8-6
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,9 @@ jobs:
181181

182182
- name: Add service hosts to /etc/hosts
183183
run: |
184-
sudo echo "127.0.0.1 fulcio-server.local tuf.local rekor-server.local keycloak-internal.keycloak-system.svc rekor-search-ui.local cli-server.local" | sudo tee -a /etc/hosts
184+
sudo echo "127.0.0.1 fulcio-server.local tuf.local rekor-server.local keycloak-internal.keycloak-system.svc rekor-search-ui.local cli-server.local tsa-server.local" | sudo tee -a /etc/hosts
185185
- name: Install cosign
186-
run: go install github.com/sigstore/cosign/v2/cmd/cosign@v2.2.2
186+
run: go install github.com/sigstore/cosign/v2/cmd/cosign@v2.2.4
187187

188188
- name: Replace images
189189
run: make dev-images && cat internal/controller/constants/images.go
@@ -292,9 +292,9 @@ jobs:
292292

293293
- name: Add service hosts to /etc/hosts
294294
run: |
295-
sudo echo "127.0.0.1 fulcio-server.local tuf.local rekor-server.local keycloak-internal.keycloak-system.svc rekor-search-ui.local cli-server.local" | sudo tee -a /etc/hosts
295+
sudo echo "127.0.0.1 fulcio-server.local tuf.local rekor-server.local keycloak-internal.keycloak-system.svc rekor-search-ui.local cli-server.local tsa-server.local" | sudo tee -a /etc/hosts
296296
- name: Install cosign
297-
run: go install github.com/sigstore/cosign/v2/cmd/cosign@v2.2.2
297+
run: go install github.com/sigstore/cosign/v2/cmd/cosign@v2.2.4
298298

299299
- name: Replace images
300300
run: make dev-images && cat internal/controller/constants/images.go
@@ -371,7 +371,7 @@ jobs:
371371
kubectl create ns ${{ env.TEST_NAMESPACE }}
372372
kubectl create secret generic redhat-registry -n ${{ env.TEST_NAMESPACE }} --from-file=.dockerconfigjson=/tmp/config.json --type=kubernetes.io/dockerconfigjson
373373
kubectl patch serviceaccount default --type=merge -p '{"imagePullSecrets": [{"name":"redhat-registry"}]}' -n ${{ env.TEST_NAMESPACE }}
374-
for NAME in "fulcio" "ctlog" "trillian" "rekor" "tuf"
374+
for NAME in "fulcio" "ctlog" "trillian" "rekor" "tuf" "tsa"
375375
do
376376
echo """
377377
apiVersion: v1
@@ -400,7 +400,7 @@ jobs:
400400
401401
- name: Until shell script to wait for deployment to be created
402402
run: |
403-
for i in trillian fulcio rekor tuf ctlog; do
403+
for i in trillian fulcio rekor tuf ctlog timestampAuthority; do
404404
until [ ! -z "$(kubectl get $i -n ${{ env.TEST_NAMESPACE }} 2>/dev/null)" ]
405405
do
406406
echo "Waiting for $i to be created."
@@ -416,6 +416,7 @@ jobs:
416416
kubectl wait --for=condition=ready rekor/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
417417
kubectl wait --for=condition=ready ctlog/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
418418
kubectl wait --for=condition=ready tuf/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
419+
kubectl wait --for=condition=ready timestampAuthority/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
419420
420421
- name: Test deployments are ready
421422
run: |
@@ -428,6 +429,7 @@ jobs:
428429
kubectl wait --for=condition=available deployment/rekor-search-ui -n ${{ env.TEST_NAMESPACE }}
429430
kubectl wait --for=condition=available deployment/tuf -n ${{ env.TEST_NAMESPACE }}
430431
kubectl wait --for=condition=available deployment/ctlog -n ${{ env.TEST_NAMESPACE }}
432+
kubectl wait --for=condition=available deployment/tsa-server -n ${{ env.TEST_NAMESPACE }}
431433
432434
- name: Archive test artifacts
433435
uses: actions/upload-artifact@v4

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ go.work
3535

3636
# K8s dump from e2e tests
3737
k8s-dump-*.tar.gz
38+
39+
#tsa certificate chain file
40+
ts_chain.pem

PROJECT

+9
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,13 @@ resources:
6565
kind: CTlog
6666
path: github.com/securesign/secure-sign-operator/api/v1alpha1
6767
version: v1alpha1
68+
- api:
69+
crdVersion: v1
70+
namespaced: true
71+
controller: true
72+
domain: redhat.com
73+
group: rhtas
74+
kind: TimestampAuthority
75+
path: github.com/securesign/secure-sign-operator/api/v1alpha1
76+
version: v1alpha1
6877
version: "3"

api/v1alpha1/common.go

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package v1alpha1
22

33
import (
4+
core "k8s.io/api/core/v1"
45
k8sresource "k8s.io/apimachinery/pkg/api/resource"
56
)
67

@@ -96,3 +97,12 @@ type Pvc struct {
9697
//+optional
9798
StorageClass string `json:"storageClass,omitempty"`
9899
}
100+
101+
type Auth struct {
102+
// Environmental variables used to define authentication parameters
103+
//+optional
104+
Env []core.EnvVar `json:"env,omitempty"`
105+
// Secret ref to be mounted inside a pod, Mount path defaults to /var/run/secrets/tas/auth
106+
//+optional
107+
SecretMount []SecretKeySelector `json:"secretMount,omitempty"`
108+
}

api/v1alpha1/securesign_types.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ type SecuresignSpec struct {
2929
Rekor RekorSpec `json:"rekor,omitempty"`
3030
Fulcio FulcioSpec `json:"fulcio,omitempty"`
3131
Trillian TrillianSpec `json:"trillian,omitempty"`
32-
//+kubebuilder:default:={keys:{{name: rekor.pub},{name: ctfe.pub},{name: fulcio_v1.crt.pem}}}
33-
Tuf TufSpec `json:"tuf,omitempty"`
34-
Ctlog CTlogSpec `json:"ctlog,omitempty"`
32+
//+kubebuilder:default:={keys:{{name: rekor.pub},{name: ctfe.pub},{name: fulcio_v1.crt.pem},{name: tsa.certchain.pem}}}
33+
Tuf TufSpec `json:"tuf,omitempty"`
34+
Ctlog CTlogSpec `json:"ctlog,omitempty"`
35+
TimestampAuthority TimestampAuthoritySpec `json:"tsa,omitempty"`
3536
}
3637

3738
// SecuresignStatus defines the observed state of Securesign
@@ -45,6 +46,7 @@ type SecuresignStatus struct {
4546
RekorStatus SecuresignRekorStatus `json:"rekor,omitempty"`
4647
FulcioStatus SecuresignFulcioStatus `json:"fulcio,omitempty"`
4748
TufStatus SecuresignTufStatus `json:"tuf,omitempty"`
49+
TSAStatus SecuresignTSAStatus `json:"tsa,omitempty"`
4850
}
4951

5052
type SecuresignRekorStatus struct {
@@ -59,6 +61,10 @@ type SecuresignTufStatus struct {
5961
Url string `json:"url,omitempty"`
6062
}
6163

64+
type SecuresignTSAStatus struct {
65+
Url string `json:"url,omitempty"`
66+
}
67+
6268
//+kubebuilder:object:root=true
6369
//+kubebuilder:subresource:status
6470
//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The Deployment status"
+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/*
2+
Copyright 2023.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
"k8s.io/apimachinery/pkg/api/meta"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
)
23+
24+
// TimestampAuthoritySpec defines the desired state of TimestampAuthority
25+
type TimestampAuthoritySpec struct {
26+
//Define whether you want to export service or not
27+
ExternalAccess ExternalAccess `json:"externalAccess,omitempty"`
28+
//Signer configuration
29+
//+required
30+
Signer TimestampAuthoritySigner `json:"signer"`
31+
//Enable Service monitors for Timestamp Authority
32+
Monitoring MonitoringConfig `json:"monitoring,omitempty"`
33+
//ConfigMap with additional bundle of trusted CA
34+
//+optional
35+
TrustedCA *LocalObjectReference `json:"trustedCA,omitempty"`
36+
//Configuration for NTP monitoring
37+
//+optional
38+
NTPMonitoring NTPMonitoring `json:"ntpMonitoring,omitempty"`
39+
}
40+
41+
// TimestampAuthoritySigner defines the desired state of the Timestamp Authority Signer
42+
// +kubebuilder:validation:XValidation:rule=(!(has(self.file) || has(self.kms) || has(self.tink)) || has(self.certificateChain.certificateChainRef)),message="signer config needs a matching cert chain in certificateChain.certificateChainRef"
43+
// +kubebuilder:validation:XValidation:rule=(has(self.file) || has(self.kms) || has(self.tink) || !has(self.certificateChain.certificateChainRef)),message="certificateChainRef should not be present if no signers are configured"
44+
// +kubebuilder:validation:XValidation:rule=(!(has(self.file) && has(self.kms)) && !(has(self.file) && has(self.tink)) && !(has(self.kms) && has(self.tink))),message="only one signer should be configured at any time"
45+
type TimestampAuthoritySigner struct {
46+
//Configuration for the Certificate Chain
47+
//+required
48+
CertificateChain CertificateChain `json:"certificateChain"`
49+
//Configuration for file-based signer
50+
//+optional
51+
File *File `json:"file,omitempty"`
52+
//Configuration for KMS based signer
53+
//+optional
54+
Kms *KMS `json:"kms,omitempty"`
55+
//Configuration for Tink based signer
56+
//+optional
57+
Tink *Tink `json:"tink,omitempty"`
58+
}
59+
60+
// Certificate chain config
61+
// +kubebuilder:validation:XValidation:rule="(!has(self.rootCA) && !has(self.leafCA)) || (has(self.rootCA.privateKeyRef) == has(self.leafCA.privateKeyRef))",message="must provide private keys for both root and leaf certificate authorities"
62+
// +kubebuilder:validation:XValidation:rule=(has(self.certificateChainRef) || self.rootCA.organizationName != ""),message=organizationName cannot be empty for root certificate authority
63+
// +kubebuilder:validation:XValidation:rule=(has(self.certificateChainRef) || self.leafCA.organizationName != ""),message=organizationName cannot be empty for leaf certificate authority
64+
type CertificateChain struct {
65+
//Reference to the certificate chain
66+
//+optional
67+
CertificateChainRef *SecretKeySelector `json:"certificateChainRef,omitempty"`
68+
//Root Certificate Authority Config
69+
//+optional
70+
RootCA TsaCertificateAuthority `json:"rootCA,omitempty"`
71+
//Intermediate Certificate Authority Config
72+
//+optional
73+
IntermediateCA []TsaCertificateAuthority `json:"intermediateCA,omitempty"`
74+
//Leaf Certificate Authority Config
75+
//+optional
76+
LeafCA TsaCertificateAuthority `json:"leafCA,omitempty"`
77+
}
78+
79+
// TSA Certificate Authority configuration
80+
type TsaCertificateAuthority struct {
81+
//CommonName specifies the common name for the TimeStampAuthorities cert chain.
82+
//If not provided, the common name will default to the host name.
83+
//+optional
84+
CommonName string `json:"commonName,omitempty"`
85+
//+optional
86+
//OrganizationName specifies the Organization Name for the TimeStampAuthorities cert chain.
87+
OrganizationName string `json:"organizationName,omitempty"`
88+
//+optional
89+
//Organization Email specifies the Organization Email for the TimeStampAuthorities cert chain.
90+
OrganizationEmail string `json:"organizationEmail,omitempty"`
91+
//Password to decrypt the signer's root private key
92+
//+optional
93+
PasswordRef *SecretKeySelector `json:"passwordRef,omitempty"`
94+
// Reference to the signer's root private key
95+
//+optional
96+
PrivateKeyRef *SecretKeySelector `json:"privateKeyRef,omitempty"`
97+
}
98+
99+
// TSA File signer configuration
100+
type File struct {
101+
//Password to decrypt the signer's root private key
102+
//+optional
103+
PasswordRef *SecretKeySelector `json:"passwordRef,omitempty"`
104+
//Reference to the signer's root private key
105+
//+optional
106+
PrivateKeyRef *SecretKeySelector `json:"privateKeyRef,omitempty"`
107+
}
108+
109+
// TSA KMS signer config
110+
type KMS struct {
111+
//KMS key for signing timestamp responses. Valid options include: [gcpkms://resource, azurekms://resource, hashivault://resource, awskms://resource]
112+
//+required
113+
KeyResource string `json:"keyResource,omitempty"`
114+
//Configuration for authentication for key management services
115+
//+optional
116+
Auth *Auth `json:"auth,omitempty"`
117+
}
118+
119+
// TSA Tink signer config
120+
type Tink struct {
121+
//KMS key for signing timestamp responses for Tink keysets. Valid options include: [gcp-kms://resource, aws-kms://resource, hcvault://]"
122+
//+required
123+
KeyResource string `json:"keyResource,omitempty"`
124+
//+required
125+
//Path to KMS-encrypted keyset for Tink, decrypted by TinkKeyResource
126+
KeysetRef *SecretKeySelector `json:"keysetRef,omitempty"`
127+
// Configuration for authentication for key management services
128+
//+optional
129+
Auth *Auth `json:"auth,omitempty"`
130+
}
131+
132+
type NTPMonitoring struct {
133+
//Enable or disable NTP(Network Time Protocol) Monitoring, Enabled by default
134+
//+kubebuilder:default:=true
135+
Enabled bool `json:"enabled"`
136+
//Configuration for Network time protocol monitoring
137+
Config *NtpMonitoringConfig `json:"config,omitempty"`
138+
}
139+
140+
type NtpMonitoringConfig struct {
141+
//ConfigMap containing YAML configuration for NTP monitoring
142+
//Default configuration: https://github.com/securesign/timestamp-authority/blob/main/pkg/ntpmonitor/ntpsync.yaml
143+
NtpConfigRef *LocalObjectReference `json:"ntpConfigRef,omitempty"`
144+
//Number of attempts to contact a ntp server before giving up.
145+
RequestAttempts int `json:"requestAttempts,omitempty"`
146+
//The timeout in seconds for a request to respond. This value must be
147+
//smaller than max_time_delta.
148+
RequestTimeout int `json:"requestTimeout,omitempty"`
149+
//Number of randomly selected ntp servers to interrogate.
150+
NumServers int `json:"numServers,omitempty"`
151+
//Maximum number of seconds the local time is allowed to drift from the
152+
//response of a ntp server
153+
MaxTimeDelta int `json:"maxTimeDelta,omitempty"`
154+
//Number of servers who must agree with local time.
155+
ServerThreshold int `json:"serverThreshold,omitempty"`
156+
//Period (in seconds) for polling ntp servers
157+
Period int `json:"period,omitempty"`
158+
//List of servers to contact. Many DNS names resolves to multiple A records.
159+
Servers []string `json:"servers,omitempty"`
160+
}
161+
162+
func (i *TimestampAuthority) GetConditions() []metav1.Condition {
163+
return i.Status.Conditions
164+
}
165+
166+
func (i *TimestampAuthority) SetCondition(newCondition metav1.Condition) {
167+
meta.SetStatusCondition(&i.Status.Conditions, newCondition)
168+
}
169+
170+
// TimestampAuthorityStatus defines the observed state of TimestampAuthority
171+
type TimestampAuthorityStatus struct {
172+
NTPMonitoring *NTPMonitoring `json:"ntpMonitoring,omitempty"`
173+
Signer *TimestampAuthoritySigner `json:"signer,omitempty"`
174+
Url string `json:"url,omitempty"`
175+
// +listType=map
176+
// +listMapKey=type
177+
// +patchStrategy=merge
178+
// +patchMergeKey=type
179+
// +optional
180+
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
181+
}
182+
183+
//+kubebuilder:object:root=true
184+
//+kubebuilder:subresource:status
185+
//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status"
186+
//+kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.status.url`,description="The component url"
187+
188+
// TimestampAuthority is the Schema for the timestampauthorities API
189+
type TimestampAuthority struct {
190+
metav1.TypeMeta `json:",inline"`
191+
metav1.ObjectMeta `json:"metadata,omitempty"`
192+
193+
Spec TimestampAuthoritySpec `json:"spec,omitempty"`
194+
Status TimestampAuthorityStatus `json:"status,omitempty"`
195+
}
196+
197+
//+kubebuilder:object:root=true
198+
199+
// TimestampAuthorityList contains a list of TimestampAuthority
200+
type TimestampAuthorityList struct {
201+
metav1.TypeMeta `json:",inline"`
202+
metav1.ListMeta `json:"metadata,omitempty"`
203+
Items []TimestampAuthority `json:"items"`
204+
}
205+
206+
func init() {
207+
SchemeBuilder.Register(&TimestampAuthority{}, &TimestampAuthorityList{})
208+
}

0 commit comments

Comments
 (0)