...
1#!/bin/bash
2#
3# Copyright 2021 The Sigstore Authors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17set -ex
18
19# Things to install first:
20# - jq, createtree
21
22# Spin up services as usual
23
24echo "Installing createtree..."
25go install github.com/google/trillian/cmd/createtree@latest
26
27echo "starting services"
28docker-compose up -d
29rm ~/.rekor/state.json || true
30
31echo "building CLI and server"
32go build -o rekor-cli ./cmd/rekor-cli
33REKOR_CLI=$(pwd)/rekor-cli
34go build -o rekor-server ./cmd/rekor-server
35
36
37function check_log_index () {
38 logIndex=$1
39 # make sure we can get this log index from rekor
40 $REKOR_CLI get --log-index $logIndex --rekor_server http://localhost:3000
41 # make sure the entry index matches the log index
42 gotIndex=$($REKOR_CLI get --log-index $logIndex --rekor_server http://localhost:3000 --format json | jq -r .LogIndex)
43 if [[ "$gotIndex" == $logIndex ]]; then
44 echo "New entry has expected virtual log index $gotIndex"
45 else
46 echo "FAIL: expected virtual log index $logIndex, got $gotIndex"
47 exit 1
48 fi
49}
50
51function stringsMatch () {
52 one=$1
53 two=$2
54
55 if [[ "$one" == "$two" ]]; then
56 echo "Strings match"
57 else
58 echo "$one and $two don't match but should"
59 exit 1
60 fi
61}
62
63function waitForRekorServer () {
64 count=0
65
66 echo -n "waiting up to 60 sec for system to start"
67 until [ $(docker-compose ps | grep -c "(healthy)") == 3 ];
68 do
69 if [ $count -eq 6 ]; then
70 echo "! timeout reached"
71 REKOR_CONTAINER_ID=$(docker ps --filter name=rekor-server --format {{.ID}})
72 docker logs $REKOR_CONTAINER_ID
73 exit 1
74 else
75 echo -n "."
76 sleep 10
77 let 'count+=1'
78 fi
79 done
80
81 echo
82}
83
84function collectLogsOnFailure () {
85 if [[ "$1" -ne "0" ]]; then
86 echo "failure detected, collecting docker-compose logs"
87 docker-compose logs --no-color > /tmp/docker-compose.log
88 exit $1
89 elif docker-compose logs --no-color | grep -q "panic: runtime error:" ; then
90 # if we're here, we found a panic
91 echo "failing due to panics detected in logs"
92 docker-compose logs --no-color > /tmp/docker-compose.log
93 exit 1
94 fi
95 exit 0
96}
97trap "collectLogsOnFailure $?" EXIT
98
99echo "Waiting for rekor server to come up..."
100waitForRekorServer
101
102# Add some things to the tlog :)
103pushd tests
104$REKOR_CLI upload --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000
105popd
106
107# Make sure we can prove consistency
108$REKOR_CLI loginfo --rekor_server http://localhost:3000
109
110# Add 2 more entries to the log
111pushd tests/sharding-testdata
112$REKOR_CLI upload --artifact file1 --signature file1.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000
113$REKOR_CLI upload --artifact file2 --signature file2.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000
114popd
115
116
117INITIAL_TREE_ID=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .TreeID)
118echo "Initial Tree ID is $INITIAL_TREE_ID"
119
120# Make sure we have three entries in the log
121check_log_index 2
122$REKOR_CLI logproof --rekor_server http://localhost:3000 --last-size 2
123
124# Now, we want to shard the log.
125# Create a new tree
126echo "creating a new Tree ID...."
127SHARD_TREE_ID=$(createtree --admin_server localhost:8090)
128echo "the new shard ID is $SHARD_TREE_ID"
129
130# Once more
131$REKOR_CLI loginfo --rekor_server http://localhost:3000
132
133# Get the public key for the active tree for later
134ENCODED_PUBLIC_KEY=$(curl http://localhost:3000/api/v1/log/publicKey | base64 -w 0)
135
136# Spin down the rekor server
137echo "stopping the rekor server..."
138REKOR_CONTAINER_ID=$(docker ps --filter name=rekor-server --format {{.ID}})
139docker stop $REKOR_CONTAINER_ID
140
141# Now we want to spin up the Rekor server again, but this time point
142# to the new tree
143SHARDING_CONFIG=sharding-config.yaml
144cat << EOF > $SHARDING_CONFIG
145- treeID: $INITIAL_TREE_ID
146 encodedPublicKey: $ENCODED_PUBLIC_KEY
147EOF
148
149cat $SHARDING_CONFIG
150
151COMPOSE_FILE=docker-compose-sharding.yaml
152cat << EOF > $COMPOSE_FILE
153version: '3.4'
154services:
155 rekor-server:
156 build:
157 context: .
158 target: "deploy"
159 command: [
160 "rekor-server",
161 "serve",
162 "--trillian_log_server.address=trillian-log-server",
163 "--trillian_log_server.port=8090",
164 "--redis_server.address=redis-server",
165 "--redis_server.port=6379",
166 "--rekor_server.address=0.0.0.0",
167 "--rekor_server.signer=memory",
168 "--enable_attestation_storage",
169 "--attestation_storage_bucket=file:///var/run/attestations",
170 "--trillian_log_server.tlog_id=$SHARD_TREE_ID",
171 "--trillian_log_server.sharding_config=/$SHARDING_CONFIG"
172 # Uncomment this for production logging
173 # "--log_type=prod",
174 ]
175 volumes:
176 - "/var/run/attestations:/var/run/attestations:z"
177 - "./$SHARDING_CONFIG:/$SHARDING_CONFIG:z"
178 restart: always # keep the server running
179 ports:
180 - "3000:3000"
181 - "2112:2112"
182 healthcheck:
183 test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
184 interval: 10s
185 timeout: 3s
186 retries: 3
187 start_period: 5s
188EOF
189
190# Spin up the new Rekor
191
192docker-compose -f $COMPOSE_FILE up -d
193waitForRekorServer
194$REKOR_CLI loginfo --rekor_server http://localhost:3000
195
196# Make sure we are pointing to the new tree now
197TREE_ID=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .TreeID)
198# Check that the SHARD_TREE_ID is a substring of the `$REKOR_CLI loginfo` output
199stringsMatch $TREE_ID $SHARD_TREE_ID
200
201# Now, if we run $REKOR_CLI get --log_index 2 again, it should grab the log index
202# from Shard 0
203check_log_index 2
204
205# Add in a new entry to this shard
206pushd tests/sharding-testdata
207$REKOR_CLI upload --artifact file2 --signature file2.sig --pki-format=x509 --public-key=ec_public.pem --rekor_server http://localhost:3000
208popd
209# Pass in the universal log_index & make sure it resolves
210check_log_index 3
211
212# Make sure the shard tree size is 1 and the total tree size is 4
213rm $HOME/.rekor/state.json # We have to remove this since we can't prove consistency between entry 0 and entry 1
214TREE_SIZE=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .ActiveTreeSize)
215stringsMatch $TREE_SIZE "1"
216
217TOTAL_TREE_SIZE=$($REKOR_CLI loginfo --rekor_server http://localhost:3000 --format json | jq -r .TotalTreeSize)
218stringsMatch $TOTAL_TREE_SIZE "4"
219
220
221# Make sure we can still get logproof for the now-inactive shard
222$REKOR_CLI logproof --last-size 2 --tree-id $INITIAL_TREE_ID --rekor_server http://localhost:3000
223# And the logproof for the now active shard
224$REKOR_CLI logproof --last-size 1 --rekor_server http://localhost:3000
225
226echo "Getting public key for inactive shard..."
227GOT_PUB_KEY=$(curl "http://localhost:3000/api/v1/log/publicKey?treeID=$INITIAL_TREE_ID" | base64 -w 0)
228echo "Got encoded public key $GOT_PUB_KEY, making sure this matches the public key we got earlier..."
229stringsMatch $ENCODED_PUBLIC_KEY $GOT_PUB_KEY
230
231echo "Getting the public key for the active tree..."
232NEW_PUB_KEY=$(curl "http://localhost:3000/api/v1/log/publicKey" | base64 -w 0)
233echo "Making sure the public key for the active shard is different from the inactive shard..."
234if [[ "$ENCODED_PUBLIC_KEY" == "$NEW_PUB_KEY" ]]; then
235 echo
236 echo "Active tree public key should be different from inactive shard public key but isn't..."
237 echo "Inactive Shard Public Key: $ENCODED_PUBLIC_KEY"
238 echo "Active Shard Public Key: $NEW_PUB_KEY"
239 exit 1
240fi
241
242# TODO: Try to get the entry via Entry ID (Tree ID in hex + UUID)
243echo
244echo "Testing /api/v1/log/entries/retrieve endpoint..."
245
246ENTRY_ID_1=$($REKOR_CLI get --log-index 1 --rekor_server http://localhost:3000 --format json | jq -r .UUID)
247ENTRY_ID_2=$($REKOR_CLI get --log-index 3 --rekor_server http://localhost:3000 --format json | jq -r .UUID)
248
249
250# Make sure retrieve by UUID in the inactive shard works
251NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\"]}" | jq '. | length')
252stringsMatch $NUM_ELEMENTS "1"
253
254# Make sure we can verify the entry we entered into the now-inactive shard
255pushd tests
256$REKOR_CLI verify --artifact test_file.txt --signature test_file.sig --public-key test_public_key.key --rekor_server http://localhost:3000
257popd
258
259# -f makes sure we exit on failure
260NUM_ELEMENTS=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"entryUUIDs\": [\"$ENTRY_ID_1\", \"$ENTRY_ID_2\"]}" | jq '. | length')
261stringsMatch $NUM_ELEMENTS "2"
262
263# Make sure the /api/v1/log/entries/retrieve endpoint is resolving virtual indexes correctly
264NUM_ELEMENTS=$(curl -f -H "Content-Type: application/json" --data '{"logIndexes": [0,3]}' "http://localhost:3000/api/v1/log/entries/retrieve" | jq '. | length')
265stringsMatch $NUM_ELEMENTS "2"
266
267# Make sure we get the expected LogIndex in the response when calling /retrieve endpoint
268RETRIEVE_LOGINDEX1=$(curl -f http://localhost:3000/api/v1/log/entries/retrieve -H "Content-Type: application/json" -H "Accept: application/json" -d "{ \"logIndexes\": [1]}" | jq '.[0]' | jq -r .$UUID1.logIndex)
269stringsMatch $RETRIEVE_LOGINDEX1 "1"
270
271# Make sure that verification succeeds via UUID
272echo
273echo "Testing rekor-cli verification via UUID..."
274$REKOR_CLI verify --uuid $UUID1 --rekor_server http://localhost:3000
275
276# Make sure that verification succeeds via Entry ID (Tree ID in hex + UUID)
277echo
278echo "Testing rekor-cli verification via Entry ID..."
279DEBUG=1 $REKOR_CLI verify --uuid $ENTRY_ID_1 --rekor_server http://localhost:3000
280
281echo "Test passed successfully :)"
View as plain text