Skip to content

Commit

Permalink
add mTLS to TSA for sign-blob command (sigstore#3200)
Browse files Browse the repository at this point in the history
* add mTLS to TSA for sign-blob

Signed-off-by: Dmitry S <[email protected]>

* add functional test for sign-blobl mTLS to TSA

Signed-off-by: Dmitry S <[email protected]>

---------

Signed-off-by: Dmitry S <[email protected]>
  • Loading branch information
dmitris authored Aug 25, 2023
1 parent 5f747c2 commit 126474c
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 12 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,7 @@ jobs:
- name: Run e2e_tsa_mtls.sh
shell: bash
run: make && PATH="$PWD:$PATH" ./test/e2e_tsa_mtls.sh

- name: Run e2e_signblob_tsa_mtls.sh
shell: bash
run: make && PATH="$PWD:$PATH" ./test/e2e_signblob_tsa_mtls.sh
16 changes: 16 additions & 0 deletions cmd/cosign/cli/options/signblob.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ type SignBlobOptions struct {
BundlePath string
SkipConfirmation bool
TlogUpload bool
TSAClientCACert string
TSAClientCert string
TSAClientKey string
TSAServerName string
TSAServerURL string
RFC3161TimestampPath string
IssueCertificate bool
Expand Down Expand Up @@ -77,6 +81,18 @@ func (o *SignBlobOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().BoolVar(&o.TlogUpload, "tlog-upload", true,
"whether or not to upload to the tlog")

cmd.Flags().StringVar(&o.TSAClientCACert, "timestamp-client-cacert", "",
"path to the X.509 CA certificate file in PEM format to be used for the connection to the TSA Server")

cmd.Flags().StringVar(&o.TSAClientCert, "timestamp-client-cert", "",
"path to the X.509 certificate file in PEM format to be used for the connection to the TSA Server")

cmd.Flags().StringVar(&o.TSAClientKey, "timestamp-client-key", "",
"path to the X.509 private key file in PEM format to be used, together with the 'timestamp-client-cert' value, for the connection to the TSA Server")

cmd.Flags().StringVar(&o.TSAServerName, "timestamp-server-name", "",
"SAN name to use as the 'ServerName' tls.Config field to verify the mTLS connection to the TSA Server")

cmd.Flags().StringVar(&o.TSAServerURL, "timestamp-server-url", "",
"url to the Timestamp RFC3161 server, default none. Must be the path to the API to request timestamp responses, e.g. https://freetsa.org/tsr")

Expand Down
30 changes: 21 additions & 9 deletions cmd/cosign/cli/sign/sign_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,14 @@ import (
"os"
"path/filepath"

"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa/client"
"github.com/sigstore/cosign/v2/internal/ui"
cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle"

"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
internal "github.com/sigstore/cosign/v2/internal/pkg/cosign"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa/client"
"github.com/sigstore/cosign/v2/internal/ui"
"github.com/sigstore/cosign/v2/pkg/cosign"
cbundle "github.com/sigstore/cosign/v2/pkg/cosign/bundle"
"github.com/sigstore/sigstore/pkg/cryptoutils"
signatureoptions "github.com/sigstore/sigstore/pkg/signature/options"
)
Expand Down Expand Up @@ -77,10 +76,23 @@ func SignBlobCmd(ro *options.RootOptions, ko options.KeyOpts, payloadPath string
if ko.RFC3161TimestampPath == "" {
return nil, fmt.Errorf("timestamp output path must be set")
}

respBytes, err := tsa.GetTimestampedSignature(sig, client.NewTSAClient(ko.TSAServerURL))
if err != nil {
return nil, err
var respBytes []byte
var err error
if ko.TSAClientCACert == "" && ko.TSAClientCert == "" { // no mTLS params or custom CA
respBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClient(ko.TSAServerURL))
if err != nil {
return nil, err
}
} else {
respBytes, err = tsa.GetTimestampedSignature(sig, client.NewTSAClientMTLS(ko.TSAServerURL,
ko.TSAClientCACert,
ko.TSAClientCert,
ko.TSAClientKey,
ko.TSAServerName,
))
if err != nil {
return nil, err
}
}

rfc3161Timestamp = cbundle.TimestampToRFC3161Timestamp(respBytes)
Expand Down
4 changes: 4 additions & 0 deletions cmd/cosign/cli/signblob.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func SignBlob() *cobra.Command {
OIDCDisableProviders: o.OIDC.DisableAmbientProviders,
BundlePath: o.BundlePath,
SkipConfirmation: o.SkipConfirmation,
TSAClientCACert: o.TSAClientCACert,
TSAClientCert: o.TSAClientCert,
TSAClientKey: o.TSAClientKey,
TSAServerName: o.TSAServerName,
TSAServerURL: o.TSAServerURL,
RFC3161TimestampPath: o.RFC3161TimestampPath,
IssueCertificateForExistingKey: o.IssueCertificate,
Expand Down
4 changes: 4 additions & 0 deletions doc/cosign_sign-blob.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 93 additions & 0 deletions test/e2e_signblob_tsa_mtls.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
#
# Copyright 2023 The Sigstore Authors.
#
# 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.

# This test checks that verify-blob will iterate over all entries and check for at least one valid entry before erroring out
# This is to prevent verify-blob from only checking the most recent entry, which could result
# in a "denial of service" type attack if someone signs a piece of software
# with their own certificate which doesn't chain up to Sigstore

set -ex

COSIGN_CLI=./cosign
CERT_BASE="test/testdata"

# the certificates listed below are generated with the `gen-tsa-mtls-certs.sh` script.
TIMESTAMP_CACERT=$CERT_BASE/tsa-mtls-ca.crt
TIMESTAMP_CLIENT_CERT=$CERT_BASE/tsa-mtls-client.crt
TIMESTAMP_CLIENT_KEY=$CERT_BASE/tsa-mtls-client.key
TIMESTAMP_SERVER_CERT=$CERT_BASE/tsa-mtls-server.crt
TIMESTAMP_SERVER_KEY=$CERT_BASE/tsa-mtls-server.key
TIMESTAMP_SERVER_NAME="server.example.com"
TIMESTAMP_SERVER_URL=https://localhost:3000/api/v1/timestamp
TIMESTAMP_CHAIN_FILE="timestamp-chain.pem"

set +e
COSIGN_CLI=./cosign
command -v timestamp-server >& /dev/null
exit_code=$?
set -e
if [[ $exit_code != 0 ]]; then
rm -fr /tmp/timestamp-authority
git clone https://github.com/sigstore/timestamp-authority /tmp/timestamp-authority
pushd /tmp/timestamp-authority
make
export PATH="/tmp/timestamp-authority/bin:$PATH"
popd
fi

timestamp-server serve --disable-ntp-monitoring --tls-host 0.0.0.0 --tls-port 3000 \
--scheme https --tls-ca $TIMESTAMP_CACERT --tls-key $TIMESTAMP_SERVER_KEY \
--tls-certificate $TIMESTAMP_SERVER_CERT &

sleep 1
curl -k -s --key test/testdata/tsa-mtls-client.key \
--cert test/testdata/tsa-mtls-client.crt \
--cacert test/testdata/tsa-mtls-ca.crt https://localhost:3000/api/v1/timestamp/certchain \
> $TIMESTAMP_CHAIN_FILE
echo "DONE: $(ls -l $TIMESTAMP_CHAIN_FILE)"
echo "Creating a unique blob"
BLOB=verify-experimental-blob
date > $BLOB
cat $BLOB

rm -f ca-key.pem cacert.pem cert.pem key.pem import-cosign.*
# use gencert to generate CA, keys and certificates
echo "generate keys and certificates with gencert"

passwd=$(uuidgen | head -c 32 | tr 'A-Z' 'a-z')
go run test/gencert/main.go \
&& COSIGN_PASSWORD="$passwd" $COSIGN_CLI import-key-pair --key key.pem

echo "Sign the blob with cosign first and upload to rekor"
COSIGN_PASSWORD="$passwd" $COSIGN_CLI sign-blob --yes \
--key import-cosign.key \
--timestamp-server-url "${TIMESTAMP_SERVER_URL}" \
--timestamp-client-cacert ${TIMESTAMP_CACERT} --timestamp-client-cert ${TIMESTAMP_CLIENT_CERT} \
--timestamp-client-key ${TIMESTAMP_CLIENT_KEY} --timestamp-server-name ${TIMESTAMP_SERVER_NAME} \
--rfc3161-timestamp=timestamp.txt --tlog-upload=false \
--bundle cosign.bundle $BLOB

echo "Verifying ..."
$COSIGN_CLI verify-blob --bundle cosign.bundle \
--certificate-identity-regexp '.*' --certificate-oidc-issuer-regexp '.*' \
--rfc3161-timestamp=timestamp.txt --timestamp-certificate-chain=$TIMESTAMP_CHAIN_FILE \
--insecure-ignore-tlog=true --key import-cosign.pub $BLOB

# cleanup
rm -fr blob.sig ca-key.pem cacert.pem cert.pem cosign.bundle import-cosign.key \
import-cosign.pub key.pem timestamp.txt timestamp-chain.pem \
/tmp/timestamp-authority verify-experimental-blob
pkill -f 'timestamp-server'
7 changes: 4 additions & 3 deletions test/e2e_tsa_mtls.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ TIMESTAMP_SERVER_NAME="server.example.com"
TIMESTAMP_SERVER_URL=https://localhost:3000/api/v1/timestamp

set +e
COSIGN_CLI=./cosign
command -v timestamp-server >& /dev/null
exit_code=$?
set -e
Expand Down Expand Up @@ -71,9 +72,9 @@ rm -f *.pem import-cosign.* key.pem
echo "generate keys and certificates with gencert"

passwd=$(uuidgen | head -c 32 | tr 'A-Z' 'a-z')
rm -f *.pem import-cosign.* && go run test/gencert/main.go && COSIGN_PASSWORD="$passwd" cosign import-key-pair --key key.pem
rm -f *.pem import-cosign.* && go run test/gencert/main.go && COSIGN_PASSWORD="$passwd" $COSIGN_CLI import-key-pair --key key.pem

COSIGN_PASSWORD="$passwd" cosign sign --timestamp-server-url "${TIMESTAMP_SERVER_URL}" \
COSIGN_PASSWORD="$passwd" $COSIGN_CLI sign --timestamp-server-url "${TIMESTAMP_SERVER_URL}" \
--timestamp-client-cacert ${TIMESTAMP_CACERT} --timestamp-client-cert ${TIMESTAMP_CLIENT_CERT} \
--timestamp-client-key ${TIMESTAMP_CLIENT_KEY} --timestamp-server-name ${TIMESTAMP_SERVER_NAME}\
--upload=true --tlog-upload=false --key import-cosign.key --certificate-chain cacert.pem --cert cert.pem $IMG
Expand All @@ -82,7 +83,7 @@ COSIGN_PASSWORD="$passwd" cosign sign --timestamp-server-url "${TIMESTAMP_SERVER
rm -f key.pem import-cosign.*

echo "cosign verify:"
cosign verify --insecure-ignore-tlog --insecure-ignore-sct --check-claims=true \
$COSIGN_CLI verify --insecure-ignore-tlog --insecure-ignore-sct --check-claims=true \
--certificate-identity-regexp '[email protected]' --certificate-oidc-issuer-regexp '.*' \
--certificate-chain cacert.pem $IMG

Expand Down

0 comments on commit 126474c

Please sign in to comment.