Skip to content

Commit

Permalink
SECURESIGN-1014 | Add support for Trusted Timestamp Authorities in Se…
Browse files Browse the repository at this point in the history
…cureSign (#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
  • Loading branch information
JasonPowr authored Aug 19, 2024
1 parent 1d4ca37 commit 372011c
Show file tree
Hide file tree
Showing 70 changed files with 9,848 additions and 211 deletions.
14 changes: 8 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ jobs:

- name: Add service hosts to /etc/hosts
run: |
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
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
- name: Install cosign
run: go install github.com/sigstore/cosign/v2/cmd/[email protected].2
run: go install github.com/sigstore/cosign/v2/cmd/[email protected].4

- name: Replace images
run: make dev-images && cat internal/controller/constants/images.go
Expand Down Expand Up @@ -292,9 +292,9 @@ jobs:

- name: Add service hosts to /etc/hosts
run: |
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
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
- name: Install cosign
run: go install github.com/sigstore/cosign/v2/cmd/[email protected].2
run: go install github.com/sigstore/cosign/v2/cmd/[email protected].4

- name: Replace images
run: make dev-images && cat internal/controller/constants/images.go
Expand Down Expand Up @@ -371,7 +371,7 @@ jobs:
kubectl create ns ${{ env.TEST_NAMESPACE }}
kubectl create secret generic redhat-registry -n ${{ env.TEST_NAMESPACE }} --from-file=.dockerconfigjson=/tmp/config.json --type=kubernetes.io/dockerconfigjson
kubectl patch serviceaccount default --type=merge -p '{"imagePullSecrets": [{"name":"redhat-registry"}]}' -n ${{ env.TEST_NAMESPACE }}
for NAME in "fulcio" "ctlog" "trillian" "rekor" "tuf"
for NAME in "fulcio" "ctlog" "trillian" "rekor" "tuf" "tsa"
do
echo """
apiVersion: v1
Expand Down Expand Up @@ -400,7 +400,7 @@ jobs:
- name: Until shell script to wait for deployment to be created
run: |
for i in trillian fulcio rekor tuf ctlog; do
for i in trillian fulcio rekor tuf ctlog timestampAuthority; do
until [ ! -z "$(kubectl get $i -n ${{ env.TEST_NAMESPACE }} 2>/dev/null)" ]
do
echo "Waiting for $i to be created."
Expand All @@ -416,6 +416,7 @@ jobs:
kubectl wait --for=condition=ready rekor/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
kubectl wait --for=condition=ready ctlog/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
kubectl wait --for=condition=ready tuf/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
kubectl wait --for=condition=ready timestampAuthority/securesign-sample -n ${{ env.TEST_NAMESPACE }} --timeout=5m
- name: Test deployments are ready
run: |
Expand All @@ -428,6 +429,7 @@ jobs:
kubectl wait --for=condition=available deployment/rekor-search-ui -n ${{ env.TEST_NAMESPACE }}
kubectl wait --for=condition=available deployment/tuf -n ${{ env.TEST_NAMESPACE }}
kubectl wait --for=condition=available deployment/ctlog -n ${{ env.TEST_NAMESPACE }}
kubectl wait --for=condition=available deployment/tsa-server -n ${{ env.TEST_NAMESPACE }}
- name: Archive test artifacts
uses: actions/upload-artifact@v4
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ go.work

# K8s dump from e2e tests
k8s-dump-*.tar.gz

#tsa certificate chain file
ts_chain.pem
9 changes: 9 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,13 @@ resources:
kind: CTlog
path: github.com/securesign/secure-sign-operator/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: redhat.com
group: rhtas
kind: TimestampAuthority
path: github.com/securesign/secure-sign-operator/api/v1alpha1
version: v1alpha1
version: "3"
10 changes: 10 additions & 0 deletions api/v1alpha1/common.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v1alpha1

import (
core "k8s.io/api/core/v1"
k8sresource "k8s.io/apimachinery/pkg/api/resource"
)

Expand Down Expand Up @@ -96,3 +97,12 @@ type Pvc struct {
//+optional
StorageClass string `json:"storageClass,omitempty"`
}

type Auth struct {
// Environmental variables used to define authentication parameters
//+optional
Env []core.EnvVar `json:"env,omitempty"`
// Secret ref to be mounted inside a pod, Mount path defaults to /var/run/secrets/tas/auth
//+optional
SecretMount []SecretKeySelector `json:"secretMount,omitempty"`
}
12 changes: 9 additions & 3 deletions api/v1alpha1/securesign_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ type SecuresignSpec struct {
Rekor RekorSpec `json:"rekor,omitempty"`
Fulcio FulcioSpec `json:"fulcio,omitempty"`
Trillian TrillianSpec `json:"trillian,omitempty"`
//+kubebuilder:default:={keys:{{name: rekor.pub},{name: ctfe.pub},{name: fulcio_v1.crt.pem}}}
Tuf TufSpec `json:"tuf,omitempty"`
Ctlog CTlogSpec `json:"ctlog,omitempty"`
//+kubebuilder:default:={keys:{{name: rekor.pub},{name: ctfe.pub},{name: fulcio_v1.crt.pem},{name: tsa.certchain.pem}}}
Tuf TufSpec `json:"tuf,omitempty"`
Ctlog CTlogSpec `json:"ctlog,omitempty"`
TimestampAuthority TimestampAuthoritySpec `json:"tsa,omitempty"`
}

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

type SecuresignRekorStatus struct {
Expand All @@ -59,6 +61,10 @@ type SecuresignTufStatus struct {
Url string `json:"url,omitempty"`
}

type SecuresignTSAStatus struct {
Url string `json:"url,omitempty"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The Deployment status"
Expand Down
208 changes: 208 additions & 0 deletions api/v1alpha1/timestampauthority_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*
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 v1alpha1

import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TimestampAuthoritySpec defines the desired state of TimestampAuthority
type TimestampAuthoritySpec struct {
//Define whether you want to export service or not
ExternalAccess ExternalAccess `json:"externalAccess,omitempty"`
//Signer configuration
//+required
Signer TimestampAuthoritySigner `json:"signer"`
//Enable Service monitors for Timestamp Authority
Monitoring MonitoringConfig `json:"monitoring,omitempty"`
//ConfigMap with additional bundle of trusted CA
//+optional
TrustedCA *LocalObjectReference `json:"trustedCA,omitempty"`
//Configuration for NTP monitoring
//+optional
NTPMonitoring NTPMonitoring `json:"ntpMonitoring,omitempty"`
}

// TimestampAuthoritySigner defines the desired state of the Timestamp Authority Signer
// +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"
// +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"
// +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"
type TimestampAuthoritySigner struct {
//Configuration for the Certificate Chain
//+required
CertificateChain CertificateChain `json:"certificateChain"`
//Configuration for file-based signer
//+optional
File *File `json:"file,omitempty"`
//Configuration for KMS based signer
//+optional
Kms *KMS `json:"kms,omitempty"`
//Configuration for Tink based signer
//+optional
Tink *Tink `json:"tink,omitempty"`
}

// Certificate chain config
// +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"
// +kubebuilder:validation:XValidation:rule=(has(self.certificateChainRef) || self.rootCA.organizationName != ""),message=organizationName cannot be empty for root certificate authority
// +kubebuilder:validation:XValidation:rule=(has(self.certificateChainRef) || self.leafCA.organizationName != ""),message=organizationName cannot be empty for leaf certificate authority
type CertificateChain struct {
//Reference to the certificate chain
//+optional
CertificateChainRef *SecretKeySelector `json:"certificateChainRef,omitempty"`
//Root Certificate Authority Config
//+optional
RootCA TsaCertificateAuthority `json:"rootCA,omitempty"`
//Intermediate Certificate Authority Config
//+optional
IntermediateCA []TsaCertificateAuthority `json:"intermediateCA,omitempty"`
//Leaf Certificate Authority Config
//+optional
LeafCA TsaCertificateAuthority `json:"leafCA,omitempty"`
}

// TSA Certificate Authority configuration
type TsaCertificateAuthority struct {
//CommonName specifies the common name for the TimeStampAuthorities cert chain.
//If not provided, the common name will default to the host name.
//+optional
CommonName string `json:"commonName,omitempty"`
//+optional
//OrganizationName specifies the Organization Name for the TimeStampAuthorities cert chain.
OrganizationName string `json:"organizationName,omitempty"`
//+optional
//Organization Email specifies the Organization Email for the TimeStampAuthorities cert chain.
OrganizationEmail string `json:"organizationEmail,omitempty"`
//Password to decrypt the signer's root private key
//+optional
PasswordRef *SecretKeySelector `json:"passwordRef,omitempty"`
// Reference to the signer's root private key
//+optional
PrivateKeyRef *SecretKeySelector `json:"privateKeyRef,omitempty"`
}

// TSA File signer configuration
type File struct {
//Password to decrypt the signer's root private key
//+optional
PasswordRef *SecretKeySelector `json:"passwordRef,omitempty"`
//Reference to the signer's root private key
//+optional
PrivateKeyRef *SecretKeySelector `json:"privateKeyRef,omitempty"`
}

// TSA KMS signer config
type KMS struct {
//KMS key for signing timestamp responses. Valid options include: [gcpkms://resource, azurekms://resource, hashivault://resource, awskms://resource]
//+required
KeyResource string `json:"keyResource,omitempty"`
//Configuration for authentication for key management services
//+optional
Auth *Auth `json:"auth,omitempty"`
}

// TSA Tink signer config
type Tink struct {
//KMS key for signing timestamp responses for Tink keysets. Valid options include: [gcp-kms://resource, aws-kms://resource, hcvault://]"
//+required
KeyResource string `json:"keyResource,omitempty"`
//+required
//Path to KMS-encrypted keyset for Tink, decrypted by TinkKeyResource
KeysetRef *SecretKeySelector `json:"keysetRef,omitempty"`
// Configuration for authentication for key management services
//+optional
Auth *Auth `json:"auth,omitempty"`
}

type NTPMonitoring struct {
//Enable or disable NTP(Network Time Protocol) Monitoring, Enabled by default
//+kubebuilder:default:=true
Enabled bool `json:"enabled"`
//Configuration for Network time protocol monitoring
Config *NtpMonitoringConfig `json:"config,omitempty"`
}

type NtpMonitoringConfig struct {
//ConfigMap containing YAML configuration for NTP monitoring
//Default configuration: https://github.com/securesign/timestamp-authority/blob/main/pkg/ntpmonitor/ntpsync.yaml
NtpConfigRef *LocalObjectReference `json:"ntpConfigRef,omitempty"`
//Number of attempts to contact a ntp server before giving up.
RequestAttempts int `json:"requestAttempts,omitempty"`
//The timeout in seconds for a request to respond. This value must be
//smaller than max_time_delta.
RequestTimeout int `json:"requestTimeout,omitempty"`
//Number of randomly selected ntp servers to interrogate.
NumServers int `json:"numServers,omitempty"`
//Maximum number of seconds the local time is allowed to drift from the
//response of a ntp server
MaxTimeDelta int `json:"maxTimeDelta,omitempty"`
//Number of servers who must agree with local time.
ServerThreshold int `json:"serverThreshold,omitempty"`
//Period (in seconds) for polling ntp servers
Period int `json:"period,omitempty"`
//List of servers to contact. Many DNS names resolves to multiple A records.
Servers []string `json:"servers,omitempty"`
}

func (i *TimestampAuthority) GetConditions() []metav1.Condition {
return i.Status.Conditions
}

func (i *TimestampAuthority) SetCondition(newCondition metav1.Condition) {
meta.SetStatusCondition(&i.Status.Conditions, newCondition)
}

// TimestampAuthorityStatus defines the observed state of TimestampAuthority
type TimestampAuthorityStatus struct {
NTPMonitoring *NTPMonitoring `json:"ntpMonitoring,omitempty"`
Signer *TimestampAuthoritySigner `json:"signer,omitempty"`
Url string `json:"url,omitempty"`
// +listType=map
// +listMapKey=type
// +patchStrategy=merge
// +patchMergeKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}

//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`,description="The component status"
//+kubebuilder:printcolumn:name="URL",type=string,JSONPath=`.status.url`,description="The component url"

// TimestampAuthority is the Schema for the timestampauthorities API
type TimestampAuthority struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec TimestampAuthoritySpec `json:"spec,omitempty"`
Status TimestampAuthorityStatus `json:"status,omitempty"`
}

//+kubebuilder:object:root=true

// TimestampAuthorityList contains a list of TimestampAuthority
type TimestampAuthorityList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []TimestampAuthority `json:"items"`
}

func init() {
SchemeBuilder.Register(&TimestampAuthority{}, &TimestampAuthorityList{})
}
Loading

0 comments on commit 372011c

Please sign in to comment.