From e66526267cfe8b9f0d6f2ddbe165ec56a1e0a2a2 Mon Sep 17 00:00:00 2001 From: Firas Ghanmi Date: Wed, 3 Jul 2024 13:06:12 +0200 Subject: [PATCH] Add TLS support for Trillian server Signed-off-by: Firas Ghanmi --- cmd/rekor-server/app/root.go | 2 ++ docker-compose.backfill-test.yml | 3 ++ docker-compose.debug.yml | 3 ++ docker-compose.test.yml | 3 ++ docker-compose.yml | 6 ++++ pkg/api/api.go | 31 ++++++++++++++++++- tests/issue-872-e2e-test.sh | 4 +++ tests/sharding-e2e-test.sh | 2 ++ tests/tls/ca.crt | 29 ++++++++++++++++++ tests/tls/tls.crt | 31 +++++++++++++++++++ tests/tls/tls.key | 52 ++++++++++++++++++++++++++++++++ 11 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 tests/tls/ca.crt create mode 100644 tests/tls/tls.crt create mode 100644 tests/tls/tls.key diff --git a/cmd/rekor-server/app/root.go b/cmd/rekor-server/app/root.go index cebc28d2a..ff5f9850e 100644 --- a/cmd/rekor-server/app/root.go +++ b/cmd/rekor-server/app/root.go @@ -117,6 +117,8 @@ Memory and file-based signers should only be used for testing.`) rootCmd.PersistentFlags().String("redis_server.password", "", "Redis server password") rootCmd.PersistentFlags().Bool("redis_server.enable-tls", false, "Whether to enable TLS verification when connecting to Redis endpoint") rootCmd.PersistentFlags().Bool("redis_server.insecure-skip-verify", false, "Whether to skip TLS verification when connecting to Redis endpoint, only applicable when 'redis_server.enable-tls' is set to 'true'") + rootCmd.PersistentFlags().String("tls_ca_cert", "", "Certificate file to use for secure connections with Trillian server") + rootCmd.PersistentFlags().Bool("trillian_log_server.tls", false, "Use TLS when connecting to Trillian") rootCmd.PersistentFlags().Bool("enable_attestation_storage", false, "enables rich attestation storage") rootCmd.PersistentFlags().String("attestation_storage_bucket", "", "url for attestation storage bucket") diff --git a/docker-compose.backfill-test.yml b/docker-compose.backfill-test.yml index 39b9eab6e..6fac05fb6 100644 --- a/docker-compose.backfill-test.yml +++ b/docker-compose.backfill-test.yml @@ -36,7 +36,10 @@ services: "--max_request_body_size=32792576", "--search_index.storage_provider=${INDEX_BACKEND:-mysql}", "--search_index.mysql.dsn=test:zaphod@tcp(mysql:3306)/test", + "--tls_ca_cert=/tests/tls/ca.crt" ] + volumes: + - "./tests/tls:/tests/tls:z" ports: - "3000:3000" - "2112:2112" diff --git a/docker-compose.debug.yml b/docker-compose.debug.yml index f76c8b2b2..7404bdb64 100644 --- a/docker-compose.debug.yml +++ b/docker-compose.debug.yml @@ -35,7 +35,10 @@ services: "--redis_server.port=6379", "--rekor_server.address=0.0.0.0", "--rekor_server.signer=memory", + "--tls_ca_cert=/tests/tls/ca.crt" ] + volumes: + - "./tests/tls:/tests/tls:z" restart: always # keep the server running ports: - "3000:3000" diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 1f31f21c1..5083dea05 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -40,7 +40,10 @@ services: "--rekor_server.publish_events_json=true", "--search_index.storage_provider=${INDEX_BACKEND:-mysql}", "--search_index.mysql.dsn=test:zaphod@tcp(mysql:3306)/test", + "--tls_ca_cert=/tests/tls/ca.crt" ] + volumes: + - "./tests/tls:/tests/tls:z" ports: - "3000:3000" - "2112:2112" diff --git a/docker-compose.yml b/docker-compose.yml index 91e7ccff7..599c6b879 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,7 +58,11 @@ services: "--rpc_endpoint=0.0.0.0:8090", "--http_endpoint=0.0.0.0:8091", "--alsologtostderr", + "--tls_cert_file=/tests/tls/tls.crt", + "--tls_key_file=/tests/tls/tls.key" ] + volumes: + - "./tests/tls:/tests/tls:z" restart: always # retry while mysql is starting up ports: - "8090:8090" @@ -102,11 +106,13 @@ services: "--enable_stable_checkpoint", "--search_index.storage_provider=mysql", "--search_index.mysql.dsn=test:zaphod@tcp(mysql:3306)/test", + "--tls_ca_cert=/tests/tls/ca.crt" # Uncomment this for production logging # "--log_type=prod", ] volumes: - "/var/run/attestations:/var/run/attestations:z" + - "./tests/tls:/tests/tls:z" restart: always # keep the server running ports: - "3000:3000" diff --git a/pkg/api/api.go b/pkg/api/api.go index aba59b25e..d6463b170 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -22,12 +22,15 @@ import ( "crypto/x509" "encoding/hex" "fmt" + "os" + "path/filepath" "github.com/google/trillian" "github.com/redis/go-redis/v9" "github.com/spf13/viper" "golang.org/x/exp/slices" "google.golang.org/grpc" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" "github.com/sigstore/rekor/pkg/indexstorage" @@ -47,7 +50,33 @@ import ( func dial(rpcServer string) (*grpc.ClientConn, error) { // Set up and test connection to rpc server - creds := insecure.NewCredentials() + var creds credentials.TransportCredentials + tlsCACertFile := viper.GetString("tls_ca_cert") + useSystemTrustStore := viper.GetBool("trillian_log_server.tls") + + switch { + case useSystemTrustStore: + creds = credentials.NewTLS(&tls.Config{ + ServerName: rpcServer, + MinVersion: tls.VersionTLS12, + }) + case tlsCACertFile != "": + tlsCaCert, err := os.ReadFile(filepath.Clean(tlsCACertFile)) + if err != nil { + log.Logger.Fatalf("Failed to load tls_ca_cert:", err) + } + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(tlsCaCert) { + return nil, fmt.Errorf("failed to append CA certificate to pool") + } + creds = credentials.NewTLS(&tls.Config{ + ServerName: rpcServer, + RootCAs: certPool, + MinVersion: tls.VersionTLS12, + }) + default: + creds = insecure.NewCredentials() + } conn, err := grpc.NewClient(rpcServer, grpc.WithTransportCredentials(creds)) if err != nil { log.Logger.Fatalf("Failed to connect to RPC server:", err) diff --git a/tests/issue-872-e2e-test.sh b/tests/issue-872-e2e-test.sh index 9ea991d72..520e74c05 100755 --- a/tests/issue-872-e2e-test.sh +++ b/tests/issue-872-e2e-test.sh @@ -80,11 +80,13 @@ services: "--rekor_server.signer=memory", "--enable_attestation_storage", "--attestation_storage_bucket=file:///ko-app/attestations", + "--tls_ca_cert=/tests/tls/ca.crt" # Uncomment this for production logging # "--log_type=prod", ] volumes: - "$ATT_VOLUME:/ko-app/attestations:z" + - "./tls:/tests/tls:z" restart: always # keep the server running ports: - "0.0.0.0:3000:3000" @@ -160,11 +162,13 @@ services: "--enable_attestation_storage", "--attestation_storage_bucket=file:///var/run/attestations", "--trillian_log_server.tlog_id=$REKOR_TRILLIAN_LOG_SERVER_TLOG_ID", + "--tls_ca_cert=/tests/tls/ca.crt" # Uncomment this for production logging # "--log_type=prod", ] volumes: - "$ATT_VOLUME:/var/run/attestations:z" + - "./tls:/tests/tls:z" restart: always # keep the server running ports: - "3000:3000" diff --git a/tests/sharding-e2e-test.sh b/tests/sharding-e2e-test.sh index 700f4ad2a..9053d851b 100755 --- a/tests/sharding-e2e-test.sh +++ b/tests/sharding-e2e-test.sh @@ -169,12 +169,14 @@ services: "--attestation_storage_bucket=file:///var/run/attestations", "--trillian_log_server.tlog_id=$SHARD_TREE_ID", "--trillian_log_server.sharding_config=/$SHARDING_CONFIG" + "--tls_ca_cert=/tests/tls/ca.crt" # Uncomment this for production logging # "--log_type=prod", ] volumes: - "/var/run/attestations:/var/run/attestations:z" - "./$SHARDING_CONFIG:/$SHARDING_CONFIG:z" + - "./tls:/tests/tls:z" restart: always # keep the server running ports: - "3000:3000" diff --git a/tests/tls/ca.crt b/tests/tls/ca.crt new file mode 100644 index 000000000..75cebbad4 --- /dev/null +++ b/tests/tls/ca.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIFAzCCAuugAwIBAgIUHuxfcTh3qTOQMRYyg4mU1igOpsYwDQYJKoZIhvcNAQEL +BQAwEDEOMAwGA1UEAwwFTXkgQ0EwIBcNMjQwNzAyMjIyNjA0WhgPMjEyNDA2MDgy +MjI2MDRaMBAxDjAMBgNVBAMMBU15IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAwYGFTlBioC1JHRMvoPrJPCOpA8ns+6hGENCfhhbRFIZRKI0DDfI0 +r/XYTsDLFgjdq/jFU2M+SFHz/fwVfTFi4aJPgOoVFBzuuVGQbtGzM7ci8MEwt0Wq +ZvD8pkXDldZ94s9lZ6VI3UryStHqCC1hE2z3pr3siIZ2/nkXGeOown14Un7FthlE +Uh2iAyiU785lrE3WZE9rZq/h9xFMIfers8+0t9EOU+jajRbkzvVRpNf1YpDvZe+j +OOzE0Htt8nrI84K/hWy1qtwbQhHqzXZ4E3CrQNvxcybounmGKgfzsingRt80pntw +iJU/nKnKv4wu66zuZ2hv2DNGPsbHAeFkVSTUcKBfyv7XZbXoNNhC2orjDzo4P9MQ +KCHX6k0NPKMmWDX4/HMU60HYjkGYcvQK2ydKdXoWIiUv20zLGvLXt88pW6LStZ80 +2aKGPADH+gfeMKbmJGex0cTIfcyy9DI4QV0RUiGwLscx5t75RDKgF5oPCs4sKWVQ +nSxTPSrb466Ha5WdlA2EUfFOeKZtJ2IIaYkemXrvUbYP40xxQaR/T9eCMf+Dev05 +bxIzsq8rqR/Yemefd+S82V1Eom+nSGLoZ0lwlijxcW6SKMENldqWY7W8loDfsiZS +2OvMLXqXgfAmbgqjYGu1o9ssEB+B6RCnfjGzJEqmEo+OA9d1Jl5xXyUCAwEAAaNT +MFEwHQYDVR0OBBYEFH4ovZVIpdQzE0ng2ybqR/YWXtdCMB8GA1UdIwQYMBaAFH4o +vZVIpdQzE0ng2ybqR/YWXtdCMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL +BQADggIBAADfa83u8Hs4b39+OSY7IAnBAoyrNIHRE+mFAjmc5sEyYZJ0cmXxcNqs +ib6bsUHkm9MwXdpNt3nx//P7MQBctO/YBSDTcg+8a2y4e1tv3Gy2Nv8YbxX0XRMR +bu2lk3Ltqkt8FgAimCs5RBvxyp8GTfz1efkyxRE+cOTdV0fhgEVHEPKQNjgrEQmN +rfdk9x0hA0hx0UCI849w0Ojgk+9gvqpS3WaN2Y7oWicam+6B3ExMNMg69aGx1c0U +mWB4T7igbPTU33H4N4Uz/LWzGhUFgx0iUToME3JkewTA1oBpbZI7A10YVkCliLtk +N91iLH1v7NqDpA/4W/sgkTlOIu3JELMTnMWlNGI+Rc1kyEebWoqrpn5kVhb5oFul +ikDG4ha+4JUSgKWCNhkQrFcLClHvt3nxgSWt0g3tEWQsLtbtyE6xgPHvG9aycpNh +hqsZ90miFX4mUa8P9wi/ab4MbHOawUD1HDgj4bJtXBsYQuSsdaB0OgDxhrVZwOkU ++rurdjboEAZXa0uDjE1fBYaI8KnAuUpsMHl4Hk8RvqMQOUJehlp6Bfemjsa9iHrE +sQXmB9XhL/zXNp+6ZRDGDOak+cYfsDPnNQ15Zl8BCauui7rrOrcevKatdPolcPZc +v50vUwGQ+dZfsi5SaZ6VCvh1fP9mQbfpLKQ+6YJaCDktQTIDLlhV +-----END CERTIFICATE----- diff --git a/tests/tls/tls.crt b/tests/tls/tls.crt new file mode 100644 index 000000000..1f903efa6 --- /dev/null +++ b/tests/tls/tls.crt @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFSTCCAzGgAwIBAgIUPhroVzKX5z8H8OtnSUGxz1fKY7EwDQYJKoZIhvcNAQEL +BQAwEDEOMAwGA1UEAwwFTXkgQ0EwHhcNMjQwNzAyMjIyNjE4WhcNMjQwODMxMjIy +NjE4WjA7MQ8wDQYDVQQLDAZTZXJ2ZXIxCjAIBgNVBAMMASoxHDAaBgkqhkiG9w0B +CQEWDXRsc0BnbWFpbC5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQDOcdP6F1gB6RUVV6DeLYVXKhf5ivYOHeEhygDoiKyFxEvi6+e3TVN3E0w+c1IA +BgDmDEQ4FduW7/Eo+EDh4GyUKL7GuncqOc60+yVl7wm/fmUIaWJGJlHnZFuSG0yC +UCxuJCHT4xRettOM989ylX0qmko1f1PPplFh5OkaktnxoUxg+ql+Msc562lF4MZz +KV6WgtNnLCwSR/EzjRrAc3rIpCVA7+oO6v4rToR7G+yVvC5hvUHPsNcooHgvmncG +TvFEq2bzOTMxCutDHoQ6A9bh1tn5rPiMJYyL/TssK9LUR/RbHk0f3hJQXU2nBwk9 +xA5MjTdGAbfUZ9A9aNY81DyO6D71z1UTBCc56mCuHiwuX4tdpIi5tcWBEKig2Mcm +fxXh96TOaJZUyXGOiejn6HvbHoydpGTk1rJovZlpLoS9AJxaIE9ep/j3hHm3nXUe +w8x80eYplFIsv7N2qfDwcgl59o2Y2ESjNOkl/SOlxfvu60pnhX5AMSguD3LUvQnZ +iRVFH2tueoHziB/uAqLF929rDau8ET6408MXWgxtwyM10mqG0iGz+vDIlPtxnJMo +L6OmdEO+g9Wm+XMaAY+nDKctJBV2jUyKnSJfzN71TN65KlCIRhtP/nu+M1EcJT/8 +FPnloiWoI1E8/g29FfpA4wf8vcR6iTiBf+6cI57Zj5VDQwIDAQABo3AwbjAsBgNV +HREEJTAjggEqghh0cmlsbGlhbi1sb2ctc2VydmVyOjgwOTCHBAAAAAAwHQYDVR0O +BBYEFFL0qq0HKhYm/Nz7yBlGRdgQqXu5MB8GA1UdIwQYMBaAFH4ovZVIpdQzE0ng +2ybqR/YWXtdCMA0GCSqGSIb3DQEBCwUAA4ICAQAazZXiSvVBW2ZF2FG0DRxs3fnF +mNQm5XH16o3Zpk2HbEbeXOIFWdP8J+pddHKobIG5KYxUq/eL6bHQK281J8256l07 +JfYRHJ7ROJjNhz2fa4Elm7bp/k/J6EeskArFCdwqu90Ab/LNs5BmMxJ3fugNy9MN +e+hSPXCNu6UmukNWuzzquc28kREtwdsNldP7oNfrUEheGGmuOMhEUJR2oIOAMGd2 +wNwwfgksc3HXhck5uE0WsacGkpre1efOPZJWomdcLMR3aR73jmtZzRa0UH0kL4FN +UhbsajtXTL/pbpfL7n2kfmls6sVDLGNm4IjOx5MJCFDe29nNbdw0/beww10A60K+ +OqgctvV35+ogb1KfWUkG55iLu1mGZX7sJu0ZQ++CJbWHIdX+bp5dimOX0v3EvChq +tg1JeJwSW7rqoHMp0WmpmcO4hHrCn/Vv90/VTwPPp4Cn7SE0aHBqISylyuARh0BO +A8xCpltiCAF3wlaNW0jtpW/ks4VaXCSCFyJKjZKYFk5ZMN3vS7fDah4XMmTb/m7N +ey2yx/Pl+gy5ynPLX2fsL15OAkz2awXL/AfzOy9PqiaVKXGbVwvy7vfPcEt0+0xt +3XGzO+NygGh6I14sh1b5aD4MzeYxdHFFzel9xZX0F/TF0wrcJGRoLv0St5MBTRZ0 +jtPijI5dTcAUE6TSlA== +-----END CERTIFICATE----- diff --git a/tests/tls/tls.key b/tests/tls/tls.key new file mode 100644 index 000000000..957794229 --- /dev/null +++ b/tests/tls/tls.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +nY3ltIDl7lH9MsaGYloeR+S6wzWcOw== +-----END PRIVATE KEY-----