diff --git a/build/test-certs/raft-certs/ca.crt b/build/test-certs/raft-certs/ca.crt new file mode 100644 index 000000000..c33fd4766 --- /dev/null +++ b/build/test-certs/raft-certs/ca.crt @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEmjCCAoICCQCil41TcogAhTANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARy +b290MB4XDTI2MDUyMTE0MzIxN1oXDTM2MDUxODE0MzIxN1owDzENMAsGA1UEAwwE +cm9vdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJEhc9CTeLEzVGzT +IWjMzIc7yR//RZrSzESQej87ClxcCb5iKterWUeFcdVE5prYhAlFVpm/UyuVOhCM +OrvyJxuXFZZWGhH4bOwp6xrCkwhsc5LIKZs14LJk2kCqYcF+U5wqH9KL2Jyl5m9d +NP9yLqPdW3fXWUxp7K/AYDWFRIoPw2XOe320tvM1lRqt1Ebb82bFfP2+XneJsTJW +Mbv82cuWJGmc4l/dozC5HcFf18UjaGufqtXQxenHcUOrNgvNGlbXAYMCzsB1DpkQ +oOPprDQe6qteUUQ72WYotzW2pHT+FgMx6JranGfY3idIOWuA6FXjXwVf8yzUUTaf +hjGfuKwiD0L4Po71ZeR0PrGd3Y0CNf0Hvzy/fXCJcWmrUtGqU0heDhqS46aUD3xw +mYaQhmi9WtcYOh+1xz89q2ulQ3rGPAgm44AzjWVN85hVJ5VIo+ZIZ0qADW53vm9l +9mr6hVW36dmr22q+/JHb74+tQxlPLNnu3mCCFmISLA4793+5Vg6vrEuNxTIPAoCl +EJtiotJo71fvpCVZ1f8CnkKDmm/6emExZLpC1LgG2EC6LhCrAYUw3thQYo73PBGA +yAzCIy8cm8ULTTly4Fsm4r8ITDCqCp/6zIjyulhf2Hp1c9i4wbQxcQF/xVJ3mmmr +xbu/Q9RwdiusJugD2KDFtgOXVLaFAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAC2P +IZp8x1uyNHkR7oUyp6YwARTnIfhsjHNrc1CU5Ie+AkWzcYx2scQyXnBqG0sk9god +UE+Dc/g8qMhxGwyVh8mwk9znwcnqXhBuacSqxAu+spFK9qptEiuONqI486Hbcn7j +ymdzaz8gQHgG1dIyYjyAmC/Qqu+gKqBGmnYXLSS5p7x+1QfUcitL3LWMWaPJQnzO +0rxLpvPXogrtzTa+pzHlXqJnZVZso5FsrocLvKY+BFbeuiAw9i/ESXA6QuTrHg5s +9xSecVbhzGUkL+4X/t6O080GY8KMUkDMDaKLr9iUZyTX/BS3OJtmzyOHkbJ7xKoP +Yk3FO6prlgVTGcEbmq6ZXriiLOaAoznGhia6CNZwWCO4MMTwdU9eX0LBJzZglpHG +HLgRRLlZDfOKgM2pf7O+bqo3iUSCb9kYF106nHZ08KQ1t9lNKg7cUerNSMpOVWrw +4iBC7YGq8OBMwVosAjIPs4sYyBKC8UdJJ2LTF+UaFwa0uEPXtcdub3xh5/eZPfON +t+dz0COKu27H4tKxzB/mG7xdBgjjkvSo+sWtNQX+37qbFRjA71U+FdXGmsRCs+Gr +SXNXvMXk79sYaNl/iyvpxkbHtVMteqCRcjhck7FeUoyd3AzAcIjk2nAMn2O+2cfp +wXr8N9mnfS9D0ee6wUox+yDC1iLyg1/5LHEaYlB5 +-----END CERTIFICATE----- diff --git a/build/test-certs/raft-certs/ca.key b/build/test-certs/raft-certs/ca.key new file mode 100644 index 000000000..dcb4fe27c --- /dev/null +++ b/build/test-certs/raft-certs/ca.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +e7UOU5HyC8sE7/+d531pZnKJCo3unGw= +-----END PRIVATE KEY----- diff --git a/build/test-certs/raft-certs/gen-cert.sh b/build/test-certs/raft-certs/gen-cert.sh new file mode 100755 index 000000000..cca5fba49 --- /dev/null +++ b/build/test-certs/raft-certs/gen-cert.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Default values +NODE_COUNT=3 + +# Help function +show_help() { + echo "Usage: ./gen-cert.sh [options]" + echo "" + echo "Options:" + echo " -n, --nodes Number of nodes to generate (default: 3)" + echo " -c, --clean Delete all .key, .crt, .csr, and .srl files" + echo " -h, --help Show this help message" + exit 0 +} + +# Cleanup function +clean_certs() { + echo "Cleaning up all certificates and keys..." + rm -f ./*.key ./*.crt ./*.csr ./*.srl + echo "Done." + exit 0 +} + +# Parse arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + -n|--nodes) NODE_COUNT="$2"; shift ;; + -c|--clean) clean_certs ;; + -h|--help) show_help ;; + *) echo "Unknown parameter: $1"; show_help ;; + esac + shift +done + +echo "Creating CA and certificates for $NODE_COUNT nodes..." + +# 1. Generate the Root CA +openssl req -x509 -newkey rsa:4096 -keyout ca.key -out ca.crt -days 3650 -sha256 -nodes -subj "/CN=root" + +# 2. Loop to create node certificates +for ((i=1; i<=NODE_COUNT; i++)) +do + echo "Generating node$i..." + + # Create Request + openssl req -out "node$i.csr" -newkey rsa:2048 -keyout "node$i.key" -nodes \ + -subj "/CN=node$i" \ + -addext "subjectAltName=DNS:node$i,DNS:localhost,IP:127.0.0.1" + + # Sign Certificate + openssl x509 -req -in "node$i.csr" -CA ca.crt -CAkey ca.key \ + -out "node$i.crt" -days 365 -sha256 \ + -set_serial "$i" \ + -extfile <(echo "subjectAltName=DNS:node$i,DNS:localhost,IP:127.0.0.1") +done + +# 3. Final Cleanup of temporary request files +rm -f ./*.csr +echo "Success! Generated $NODE_COUNT node certificates." \ No newline at end of file diff --git a/build/test-certs/raft-certs/node1.crt b/build/test-certs/raft-certs/node1.crt new file mode 100644 index 000000000..edd091fdc --- /dev/null +++ b/build/test-certs/raft-certs/node1.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDvzCCAaegAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARyb290 +MB4XDTI2MDUyMTE0MzIxN1oXDTI3MDUyMTE0MzIxN1owEDEOMAwGA1UEAwwFbm9k +ZTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnn5lB23zdNfc1Vwyl +NCSppscQRTewh8OTnghQmN5ND4d5cYqphTenNn2r00ttahdlHc/imTxT5P9V5zdK +y42MSqUAa4PKZaLJnGra5O6zjEF3GV9lYgYs0FjefAAc+meCP/mbQybqrTN460pl +2t9WirKJDisI4xmsem5JaFUFxfI/igUAi/2b8K14PXZJncKnRcEHel11L1J7puon +QOQHCMXiPR8J6GII1V9bsTilTDW7QQ4cr7cxvsIkPWb7S1QtFCKsbxw1zAZhu+y4 +GuzCdKBtqVpO+dggMSq9efmboKKaq1hwAkavkTBdearaSTg77QVfGPFx5GOA8go2 +JVX/AgMBAAGjJTAjMCEGA1UdEQQaMBiCBW5vZGUxgglsb2NhbGhvc3SHBH8AAAEw +DQYJKoZIhvcNAQELBQADggIBAEkJKHoo/QWNk4qbd9nbazQ30GqVfUNXDlGEQrxY +6hJEVkBMaN0XPD34Zv4Jij4oaKjVZv4YrLhGjqktPqlNZrLlx7R/XOTGTvNvOo2H +4zW9CVNJGaLQzr3Dob0W7cjo5nfeO32RMA46ZpLEeWHlQoz58zOyYP7Eg6fmS9Y9 +CwWiSEehhgSy07h08vtYGJJpi7GBm4jAmLSSmkBEJL0VbdoKDcDQJ6AEvlEOwoQb ++bzKhqLYPZKfL+8w31MmSsG4u651PGNPEnJRJCUSnq4QBw2aFZo+e2gb2nNm/UoL +9UlTqpmEXIhlmiO/iCpO68sB453wUcxaBuWhuspgekt8sWY4xOu4QcyIDvi9gH1v +z4Ps8IkG6Yc3ORz09+iWzHsei4mi78bM8M6mfMYKQdhO2GaKHOAOhMLloJurHrju +vSrqQilf3+GEScAA9f44R3ZQ1rvePLcqgI7IaEZjz4O6kx/TUqsJr1cOAql5kSFs +B3fz/NLaRnZlknouFbDMJf3wWvsVYVSTNkxhRoKBl3WxF9sVq/LBg+DMBcaioujK +R8BnIDKdyh7lrsYQuam+RTZCeWocWHG9qB5S+VKi5WnFosnau1aR4uyo8eS+c823 +Ent+pThnCKGMGeTwbpUc/GeA92NwHn3PraEMu2r4wGOWq/lwrgFAsL1c0g9QVrjt +rsmX +-----END CERTIFICATE----- diff --git a/build/test-certs/raft-certs/node1.key b/build/test-certs/raft-certs/node1.key new file mode 100644 index 000000000..713616617 --- /dev/null +++ b/build/test-certs/raft-certs/node1.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCnn5lB23zdNfc1 +VwylNCSppscQRTewh8OTnghQmN5ND4d5cYqphTenNn2r00ttahdlHc/imTxT5P9V +5zdKy42MSqUAa4PKZaLJnGra5O6zjEF3GV9lYgYs0FjefAAc+meCP/mbQybqrTN4 +60pl2t9WirKJDisI4xmsem5JaFUFxfI/igUAi/2b8K14PXZJncKnRcEHel11L1J7 +puonQOQHCMXiPR8J6GII1V9bsTilTDW7QQ4cr7cxvsIkPWb7S1QtFCKsbxw1zAZh +u+y4GuzCdKBtqVpO+dggMSq9efmboKKaq1hwAkavkTBdearaSTg77QVfGPFx5GOA +8go2JVX/AgMBAAECggEBAI4lcbm/f6u3z+OT5k1SYqTboYPSrt8swlW0diii+iEv +QhtihcwsBH3wX7IOhLBMf3poRZpLiDU8xKr8YI80/twxkKtCgYhpmBqQYq0t5p6s +Gff9tEAtWD84j6RcU1w/nPd/eRSJZb8tEbFBFt45NZwWEkDXmvb2Xxc827m0mCVD +0EU1FYLxCZfWgIlk2ZaCKvu9b7dQVv4yBj9JJqZdovH95DgQYJ6a/zpgVI9apU4X +r6MjGQSNhUToGSSDfjLCpz481ZvcO4ZEJDrADNOhLqNXLrX0li+Gb+PpXMQkpIRN +Qxd09w18RpbPUud2ZPAUzU/z8sqeeMSfVK0086IPcMECgYEA0cWtZqqgQA7KgTOA +UYxCfzL+TeKKK7ipnyZyfsDsBeDBVUbEnfjinQRgrZJREooBPUDlUMEmGtKxCq+V +ghthbNhxZdosw5Tie4tiakDeH5AvB91WW1CtG4/3iTIm5kCZ/XGI0hlGHhPmrf+i +lg5PqZABSYDjNKFsOx8BHPya4ZMCgYEAzJAYRE9U0SbaEzXv33IEU5FHSVWBwuWk +hhx+pVSyf6qtyCLy2C+EnmkF0QAaJf4fBLx0mDbEfUuSYx7iLc9KS49IZkGmSZUC +2NrTYEBpyRXL6AbIfjgPWLAYUKtEcWS/DCw648e9bfu1OY5BcrxOLYx7SmAQpoFs +umZkfFs+rWUCgYEAu3CnQNW124db65bZUgTCZmZqVa3XSLn41vzD0f8Q5y+tkUVq +6r05wJPozy+uwaIO+WzDeyUbp1wcVjAOp/NPWrMMoLYSybB/r2Ucy1SrJgjv/VcJ +kw7Dn51E0pK9r4CKGK0aVKHAxZ+CwHdsECk5hYnnnIZOfvi+fRV3KS+ONYECgYEA +kr166uWNjks0fDtgywTbHadFX9G6t7hMAPhH0Qmk0ff5mWuYkIPlukzUI0zTdRWJ +4rfdW6NN6CNh60CoNvxP60vpP5EwW/BMjorKvmz2dYzxLIxRHlnQiGopxxRUkJMY +iP7hb6xEsrSxF4x4Xnm+CuaPOpcW2ppVCJGS27IIkBECgYBjM88z1usyczCDh2C1 +9f11nuUT+sE4sopDQ7oCDfbR7+CZVjjYAnET0M+Kw3VHQjzjqGwJEma+pGqHopXg +1/yjwSWZ5H2IcirzE12213upxBh04+QgOsofLweUtxKZCZzQ8o812ksmZkFKtXhj +CqnqVwVkoDzGbBh5qn6FZZwR9Q== +-----END PRIVATE KEY----- diff --git a/build/test-certs/raft-certs/node2.crt b/build/test-certs/raft-certs/node2.crt new file mode 100644 index 000000000..83d89ea6f --- /dev/null +++ b/build/test-certs/raft-certs/node2.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDvzCCAaegAwIBAgIBAjANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARyb290 +MB4XDTI2MDUyMTE0MzIxOFoXDTI3MDUyMTE0MzIxOFowEDEOMAwGA1UEAwwFbm9k +ZTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCosmZC5eVaIfZ4+L2q +s+nqy+kEpH1XZ9lPexzM7Ri5/5h/2+BCYYN2qX3mQI+ya1NSpQUnqZ/goP9aqrjA +thX26sEjpxdmHqmmpTjgeRFnA6J+N0tDsydxgyuO/nkYaEDo54xDvIeJZSh2joxB +4zBzZZ99Hd2ijtmFKCa/ue2FCotZ2I/F5U2OsUZP+/3eAeg3Qc3moRlIxWhWCdo4 +pVgn7rqYhnyObn2zE3ApGDkgFaoSmPzDkGy0DLh9MbF9HAZypcr6W0FCE4jezXLs +KFE/AjLvTeDmioAIL34vQJzJe84THsK3vbkNK2EDz9PM247HpYpDU0PZwGY6nNmy +U+iPAgMBAAGjJTAjMCEGA1UdEQQaMBiCBW5vZGUygglsb2NhbGhvc3SHBH8AAAEw +DQYJKoZIhvcNAQELBQADggIBAIj9fTd2bQW1+UL4FiJ13Qy+GnbRy4ezauc0SI78 +XLRev5NRfjnM6+wURxLbr6I/YP+GO0hr/5G+mnrHouFWP51MVd2ku+++v+46mTJJ +8lJIXUqthBpqMiY68RxusfDUxw+Xu5dr18gYVmi0g6xarhh60Wypersi31WfKtb1 +BhRkBLRvpIY6Lr2RYqWet7FLumRJlntLpHGa/ewX8htXVb7hpxlaxblkWIoh98jm +6dcQgqpPyO+1HtzedDCVjeSCRSa7iHfq0I0jx77BOEL0xYO59KHrinJAisor8EPe +pujdeaa12xZYQ9VY31jC0RL8p3qUvLvg8giKdDmmlVwfRm4BJ30Ue4/wc6ecD1l6 ++RE0Gtg5804CiQDpcU9V3wRgqHssSzLcZsxxhDUA3o9JpkDw65aBxeay3vY2ALac +pk+e+TEnypOhN0HUyHeuyGha3KNX9FV2+x4/R3nF6t16BTZApHMRIR3VklcqMKOf +Acf3/pZ5B+iaibFgi4zPGYXy90QxlO7jfh7eiqHoPz2Arc0BILVJHZxDlAgcJXl+ +0A5t7akcYOzXUOPXC4rDWAdji6DeJiC5isYZbC5QqeeM+qhOT9YUrWjHVh6NJSEt +bKKthf8MK/gs5s9j8qg/wi+dA6hEwwsoRHORsXNhAFoFzMrJxoB3ZpQnodMkKfxd +kKvi +-----END CERTIFICATE----- diff --git a/build/test-certs/raft-certs/node2.key b/build/test-certs/raft-certs/node2.key new file mode 100644 index 000000000..0a1b8d6b3 --- /dev/null +++ b/build/test-certs/raft-certs/node2.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCosmZC5eVaIfZ4 ++L2qs+nqy+kEpH1XZ9lPexzM7Ri5/5h/2+BCYYN2qX3mQI+ya1NSpQUnqZ/goP9a +qrjAthX26sEjpxdmHqmmpTjgeRFnA6J+N0tDsydxgyuO/nkYaEDo54xDvIeJZSh2 +joxB4zBzZZ99Hd2ijtmFKCa/ue2FCotZ2I/F5U2OsUZP+/3eAeg3Qc3moRlIxWhW +Cdo4pVgn7rqYhnyObn2zE3ApGDkgFaoSmPzDkGy0DLh9MbF9HAZypcr6W0FCE4je +zXLsKFE/AjLvTeDmioAIL34vQJzJe84THsK3vbkNK2EDz9PM247HpYpDU0PZwGY6 +nNmyU+iPAgMBAAECggEAVB+KYSfVZEn1Z8PaiQpoxn5it77zBigyNLFW3lYJc48i +0/eocFHCReUldDGTtsxQY7EIzguHP3PpgqQJt4fVkijkoUvMnk8CGKjhoQaeh/lH +CZu3O45WSAO6L/d0GZi7bakxyAF2sSwxrlVNWfnaCmGN082V3V9Q11b7Y7Zq3GFj +OSkTMFTRU/2GqZOMlm4X1SisNW6rKVL+IpTi6v5sgORZsBZSUht4UlDbAKhYfsB7 +mLuk4SS0VJiAtsRumJt9tI0OXd+GvgaHLFZ9m3D0EGyGicUeq8mqlh9De/Ke1ibW +n9mjIPh/J4a9pMNW924rPdgufVethjCGot+tCH9buQKBgQDZw3t1il2rQVaWF7n3 +ZzWekZRs7ROYa3T8chBeBq1UM885VkY3Ld56VT/jRWOCDmi651vvo2LhexAOS2ox +Te7WJBKF60hqd6bmv71shKLsJyUfbYqS6hPJPIzRXD54e1959gdFmVHRz1bJ1Y0T +kvv6r66Ke/nQEmXSqFuFteY8zQKBgQDGUVuag+jRCYDc3GUqBQGVWJ//JNktHGb+ +UFkaL5e3gYfpD+4MrSp/gqI2uBxjOn65EAgT5TRK3KQ7d/rsaKgVNhMYGqjtks5K +GYDrPzLd/UfUJBpUQOiPbUzbSCmPh3HywYq9fvZFCYlBoxw0kWjOBU95PtxZA21O +ppyKKuh6ywKBgExDkfFQTA22DET0ekSS+MxX38tmG/69Y3Ml32WOoRVkL6a8+EpK +3iepw34sxvS9ZyBN9nyuk+qz1qOq9b3etU55w6LzC6F91g3qCMUAjq515yJikq1x +HQzVD9wimh5H1uA3scRGNcls+9Aj4gyA5Y6eCFqnoI2mY9Kq86KYWdHhAoGBAMWh +giYjWjomvFJdGDudrYxFO3yqHftEmKbLL2BXGVx4KxbpFxNu5jEX38ukAjYoruyB +u2sIOOp5ovjYgihhy08IClM1Di9i400YyU6mNmze4VvZdpuL67Yy+Cp2aWRtV/LR +SQ+6nHUEIdJVV+BTIBxfUmnDaRcTwTRl+Q3/4JmtAoGBAJwJgojJ+NXigYvdUxUI +1KQVCfWfq1wQXFzQJYZpwJKJCrybx8/L50RX9cXg8RC3fMawuVde7Lxs3S+fGiVT +UPkS8Hfx3KYwYQtBCYxCVQPZu01dRGGz8Ga9pt7IyWtb4b5+G158AIPeU6tHjIhd +O02tGVBpFFv4vQkk87vULCHN +-----END PRIVATE KEY----- diff --git a/build/test-certs/raft-certs/node3.crt b/build/test-certs/raft-certs/node3.crt new file mode 100644 index 000000000..13527572b --- /dev/null +++ b/build/test-certs/raft-certs/node3.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIIDvzCCAaegAwIBAgIBAzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDARyb290 +MB4XDTI2MDUyMTE0MzIxOFoXDTI3MDUyMTE0MzIxOFowEDEOMAwGA1UEAwwFbm9k +ZTMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBrxg0O8d++NRWDWyY +AlXf7NlGUzDGHgdcKIrDqGXmLmIwYT7tPWn6J+IgiQz4a30K8Dtsp17iPtuuI4WE +rcIag2vEYrYaSgGFZSn92d+s4h5q9RQOXs1hFMtrQ4+jl2Xd0aFVguSdrQ71LLbd +7DthXYxQ0KzcxLDM1772MQokDAD3Qknk9clO5aTV1z8sde58i+RGxozTfeg5tS97 +yHCY4ymqtmyZmKnW6esiOa4XwEdV3Xn1vZY2MP6ToX0Gqg3rcDtgyIp796noAccO +jZOOBPOeKSr8YOHcrY7pRFEtwTfkX3wOp1L2SwaYoE+w8xsV2KJ2FOe2OZYH9owm +kO49AgMBAAGjJTAjMCEGA1UdEQQaMBiCBW5vZGUzgglsb2NhbGhvc3SHBH8AAAEw +DQYJKoZIhvcNAQELBQADggIBABN9bnBRddMTEizecPhG7wOGyZg8yXp7FKHc9Mkq +bugm3j+VOFNO43PrMYFgDcHeVGsQbpxqxBhdCsZCuJwJv0BGk8Mb0tlWHJebB8ho +boOHTWzLhxS2FLyzOvNGCwBx5xY7DgcPtsABMEuYExM1iRx1juVGHPoj9s5OJw+h +FIo4OgQjkxtHOopWA0cUhcrzH/9TwbIHN9iLtrdK/Tf89+jlooEkZnsfMYsm9ZY3 +8mljvuKZzPmQQXRyy1bbY79of4zpyIoMhfWyXLd89ja+Q0GEZSBXw4qA6+Qz3iII +Kl+gJiE+00djHOf9qYN0lwgg8Xhh9v12EjZMJzlGNr9TNJbuu471cP/lBpvTtD+X +RlMGfVWU/CAQ96+T9kPtqv09VWbpQnS+hwLtkN5gBPWXdfey/hw9GWFkeZJ3z34N +dogSH8pkmnw/Y+zKBVFT1bGIRrIfJdO0BVMwX/LtRX/TCy372KB36TXM1l121bVJ +UI0DKI5ROD7RFSEqqB2kPhnVLveS6EGNeSBjI3RGE4/lV+oaMFyjSA/yd92Ivevm +2f8Kq7eB6o8Ke9rP+bOukyNfVkNZXlrbhb1RxuRwM4y7h/0l6eZ9qVTpffnNAYDJ +u3Ut44wiu4bdvXombq9GN1isvRNql+zueEqHO9vSARo2PcWBHU2t/Df2foo0SFRD +XzoC +-----END CERTIFICATE----- diff --git a/build/test-certs/raft-certs/node3.key b/build/test-certs/raft-certs/node3.key new file mode 100644 index 000000000..fc155a924 --- /dev/null +++ b/build/test-certs/raft-certs/node3.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDBrxg0O8d++NRW +DWyYAlXf7NlGUzDGHgdcKIrDqGXmLmIwYT7tPWn6J+IgiQz4a30K8Dtsp17iPtuu +I4WErcIag2vEYrYaSgGFZSn92d+s4h5q9RQOXs1hFMtrQ4+jl2Xd0aFVguSdrQ71 +LLbd7DthXYxQ0KzcxLDM1772MQokDAD3Qknk9clO5aTV1z8sde58i+RGxozTfeg5 +tS97yHCY4ymqtmyZmKnW6esiOa4XwEdV3Xn1vZY2MP6ToX0Gqg3rcDtgyIp796no +AccOjZOOBPOeKSr8YOHcrY7pRFEtwTfkX3wOp1L2SwaYoE+w8xsV2KJ2FOe2OZYH +9owmkO49AgMBAAECggEALosqMNswvLWFKUy+jOT6x/391KWHMgTjVeU0O4KLQj5h +QCkOWyqH/iJ5d8IXiNcjzG4giaLK7WmXlwhYPKMY4xAz5JSoGDcymGDQL32c/8my +75cHg9CKYEsD+znYGcL3KtAGiisTT0TYNFjTdQ8g0ewOHRmYgNEPf5pU/IqaWkDQ +cvYSdHeDgr1PDQ4DcOJBXUzTltaqNn2c/S5tRXEE2LJ7Aj33gRSC2HPihcXlpUZm +jENqAVoTwGvdWDOJXT/tjvoX8xLYOSxl4CZnoI7gb86jdwIGHp7iCoYGHX//uT2b +xFqw13NWsS8LpKr19VEzlEs+MM8Hu/w9RQMacQ5SoQKBgQD6gMqGbAg1iD/mlQjm +f7ACWm6YaWkIIUiDQdl7kcRx1VaKtfaM9bQnApSceMi0pNFkooPI06WGdGLHoQAk +SzJ0QVSh3g3bF8I6/aZCV8KV2D0JPybvLLCGZVzPNi6UofaRE+fmX2svi1eGti13 +RHgm0TdRzUprZ6oLXr7reJK29QKBgQDF7x7G5Lcn51bUHDcJasyM5wY/oVCxO3f3 +k0xLN8IwtC7ZwUEKOkBJuegwF39BeNlmOaqB5mNWNuFZv6gRJhQ4ZWdRDzVTA5Qy ++XAFA+5UWrgceYuR76FDC2GUIKycngpjKV/huR1yU8rEb8KAQIpZbmA44J+QRIxN +gW9sd0x9KQKBgQDAmFKdhOPO3KFck8sTBKr7NDIBplGleYl5uSAPvGPPrVlrLjmP +6LyInZhKsSaGlI2VFMnDls2VkVf86dDlFpeFAFGkXchDYYHovumb6kNl16oduNsj +WjWiLr5rZdnsVpXutuI9obEUDcrOfLR6FT9XgNVKZE7KQxpewYqCGeKbHQKBgGwW +6hq5Wd/7sGoCwndtECk+HCZxZIx0qk3T7Y0B9RT9wLv/khIQv3zDfi2NF1xcr3Po +t9laRBMXwzZsuE4tGku17JbSlE8yHCM+HlNO0z7Tj8lVAsf+MXVYaMAom7qnf4qp +VIgCvDbr1UYyteet1x+BbCCDYf+5v1iv/3YfK/EJAoGBAIM/JK0KhPc9/69nJONe +aD9VtW+Y0qoNWUn5k33GRVWlkE7QVXnW7nkeJ9gDV8HSjoKgV5kabsoNPF7jB2MK +02H3KKvM1FiDD1ceX0mQyW7UmTeNIuHoR8niAj7GMi0V/NGZLUFO2W9GilqwLCia +Ip3r111crfvtHCUiwZvn4Iwb +-----END PRIVATE KEY----- diff --git a/build/test-certs/raft-certs/start_cluster.sh b/build/test-certs/raft-certs/start_cluster.sh new file mode 100755 index 000000000..d49b174ab --- /dev/null +++ b/build/test-certs/raft-certs/start_cluster.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +ROOT_DIR="$(git rev-parse --show-toplevel)" + +# Ensure the logs directory exists +mkdir -p logs + +PEERS="1=https://127.0.0.1:9021,2=https://127.0.0.1:9022,3=https://127.0.0.1:9023" + +echo "=== Starting 3-node raft cluster ===" + +for NODE_ID in 1 2 3; do + ADDR=":808$NODE_ID" + OUT="logs/node$NODE_ID.log" + + echo "Starting node $NODE_ID on port $ADDR..." + TLS_ARGS="--raft_tls=ca=$ROOT_DIR/build/test-certs/raft-certs/ca.crt,cert=$ROOT_DIR/build/test-certs/raft-certs/node${NODE_ID}.crt,key=$ROOT_DIR/build/test-certs/raft-certs/node${NODE_ID}.key" + + go run "$ROOT_DIR"/cmds/core-service \ + --store_type=raft \ + --raft_node_id=$NODE_ID \ + --addr=$ADDR \ + --raft_peers=$PEERS \ + "$TLS_ARGS" \ + --accepted_jwt_audiences=dss \ + --public_key_files="$ROOT_DIR"/build/test-certs/auth2.pem > "$OUT" 2>&1 & +done \ No newline at end of file diff --git a/pkg/raftstore/consensus/consensus.go b/pkg/raftstore/consensus/consensus.go index ab0c33e67..c751f7d09 100644 --- a/pkg/raftstore/consensus/consensus.go +++ b/pkg/raftstore/consensus/consensus.go @@ -7,10 +7,13 @@ import ( "net/http" "net/url" + raftparams "github.com/interuss/dss/pkg/raftstore/params" "github.com/interuss/stacktrace" + "go.etcd.io/etcd/client/pkg/v3/transport" "go.etcd.io/etcd/client/pkg/v3/types" "go.etcd.io/etcd/server/v3/etcdserver/api/rafthttp" v2stats "go.etcd.io/etcd/server/v3/etcdserver/api/v2stats" + "go.etcd.io/raft/v3" "go.etcd.io/raft/v3/raftpb" "go.uber.org/zap" @@ -43,10 +46,27 @@ func NewConsensus(logger *zap.Logger, nodeID uint64, peers map[uint64]*url.URL) } func (c *Consensus) initTransport(logger *zap.Logger, nodeID uint64, clusterID uint64, peers map[uint64]*url.URL) error { + tlsCerts, err := raftparams.GetConnectParameters().TLSCertificates() + if err != nil { + return stacktrace.Propagate(err, "failed to parse TLS certificates") + } + + tlsInfo := transport.TLSInfo{ + TrustedCAFile: tlsCerts.CAFile, + CertFile: tlsCerts.CertFile, + KeyFile: tlsCerts.KeyFile, + } + + cfg, err := tlsInfo.ServerConfig() + if err != nil { + return stacktrace.NewError("failed to create TLS config") + } + nodeIDStr := fmt.Sprintf("%d", nodeID) transport := &rafthttp.Transport{ Logger: logger, + TLSInfo: tlsInfo, ID: types.ID(nodeID), ClusterID: types.ID(clusterID), Raft: c, @@ -55,7 +75,7 @@ func (c *Consensus) initTransport(logger *zap.Logger, nodeID uint64, clusterID u ErrorC: c.errorC, } - err := transport.Start() + err = transport.Start() if err != nil { return stacktrace.Propagate(err, "failed to start transport") } @@ -75,12 +95,13 @@ func (c *Consensus) initTransport(logger *zap.Logger, nodeID uint64, clusterID u } c.server = &http.Server{ - Addr: listeningAddr, - Handler: transport.Handler(), + Addr: listeningAddr, + Handler: transport.Handler(), + TLSConfig: cfg, } go func() { - err := c.server.ListenAndServe() + err := c.server.ListenAndServeTLS(tlsInfo.CertFile, tlsInfo.KeyFile) if err != nil && !errors.Is(err, http.ErrServerClosed) { logger.Error("http server error", zap.Error(err)) c.errorC <- err diff --git a/pkg/raftstore/params/params.go b/pkg/raftstore/params/params.go index 0d2e8ed94..9f9998d1e 100644 --- a/pkg/raftstore/params/params.go +++ b/pkg/raftstore/params/params.go @@ -14,6 +14,14 @@ type ( ConnectParameters struct { ID uint64 Peers string + TLS string + } + + // TLSCertificates bundles up TLS certificates parsed from the TLS field of ConnectParameters. + TLSCertificates struct { + CAFile string + CertFile string + KeyFile string } ) @@ -45,19 +53,68 @@ func (c ConnectParameters) PeerMap() (map[uint64]*url.URL, error) { return nil, stacktrace.Propagate(err, "invalid peer URL %s", parts[1]) } + if peerURL.Scheme != "https" { + return nil, stacktrace.NewError("invalid peer URL %s: must use https scheme", parts[1]) + } + peers[id] = peerURL } return peers, nil } +// TLSCertificates parses the TLS string into a TLSCertificates struct. +func (c ConnectParameters) TLSCertificates() (TLSCertificates, error) { + tlsCerts := TLSCertificates{} + + if c.TLS == "" { + return TLSCertificates{}, stacktrace.NewError("TLS configuration is empty") + } + + for _, part := range strings.Split(c.TLS, ",") { + kv := strings.SplitN(part, "=", 2) + if len(kv) != 2 { + return TLSCertificates{}, stacktrace.NewError("invalid TLS parameter '%s': must be in format key=value", part) + } + + key, value := kv[0], kv[1] + if value == "" { + return TLSCertificates{}, stacktrace.NewError("invalid TLS parameter '%s': value cannot be empty", key) + } + + switch key { + case "ca": + tlsCerts.CAFile = value + case "cert": + tlsCerts.CertFile = value + case "key": + tlsCerts.KeyFile = value + default: + return TLSCertificates{}, stacktrace.NewError("invalid TLS parameter '%s': must be one of ca, cert, or key", key) + } + } + + if tlsCerts.CAFile == "" { + return TLSCertificates{}, stacktrace.NewError("missing required TLS parameter: ca") + } + if tlsCerts.CertFile == "" { + return TLSCertificates{}, stacktrace.NewError("missing required TLS parameter: cert") + } + if tlsCerts.KeyFile == "" { + return TLSCertificates{}, stacktrace.NewError("missing required TLS parameter: key") + } + + return tlsCerts, nil +} + var ( connectParameters ConnectParameters ) func init() { flag.Uint64Var(&connectParameters.ID, "raft_node_id", 0, "raft node ID for this instance (must be non-zero and unique within the cluster)") - flag.StringVar(&connectParameters.Peers, "raft_peers", "", `comma-separated "nodeID=peerURL" pairs for all cluster members, including the current node, e.g. "1=http://node1:9021,2=http://node2:9021,3=http://node3:9021"`) + flag.StringVar(&connectParameters.Peers, "raft_peers", "", `comma-separated "nodeID=peerURL" pairs for all cluster members, including the current node, e.g. "1=https://node1:9021,2=https://node2:9021,3=https://node3:9021"`) + flag.StringVar(&connectParameters.TLS, "raft_tls", "", `TLS certificates, format: ca=/path/to/ca.crt,cert=/path/to/node.crt,key=/path/to/node.key"`) } // GetConnectParameters returns a ConnectParameters instance that gets populated from well-known CLI flags. diff --git a/pkg/raftstore/params/params_test.go b/pkg/raftstore/params/params_test.go index 7410bf64e..6b6dea223 100644 --- a/pkg/raftstore/params/params_test.go +++ b/pkg/raftstore/params/params_test.go @@ -17,22 +17,22 @@ func TestPeerMap(t *testing.T) { }{ { name: "valid single peer", - peers: "1=http://node1:9021", - want: map[uint64]*url.URL{1: mustParseURL("http://node1:9021")}, + peers: "1=https://node1:9021", + want: map[uint64]*url.URL{1: mustParseURL("https://node1:9021")}, }, { name: "valid multiple peers", - peers: "1=http://node1:9021,2=http://node2:9021,3=http://node3:9021", + peers: "1=https://node1:9021,2=https://node2:9021,3=https://node3:9021", want: map[uint64]*url.URL{ - 1: mustParseURL("http://node1:9021"), - 2: mustParseURL("http://node2:9021"), - 3: mustParseURL("http://node3:9021"), + 1: mustParseURL("https://node1:9021"), + 2: mustParseURL("https://node2:9021"), + 3: mustParseURL("https://node3:9021"), }, }, { name: "valid URL with equals sign in query string", - peers: "1=http://node1:9021?token=abc123", - want: map[uint64]*url.URL{1: mustParseURL("http://node1:9021?token=abc123")}, + peers: "1=https://node1:9021?token=abc123", + want: map[uint64]*url.URL{1: mustParseURL("https://node1:9021?token=abc123")}, }, { name: "invalid empty peers string", @@ -46,27 +46,32 @@ func TestPeerMap(t *testing.T) { }, { name: "invalid non-numeric node ID", - peers: "abc=http://node1:9021", + peers: "abc=https://node1:9021", wantError: true, }, { name: "invalid negative node ID", - peers: "-1=http://node1:9021", + peers: "-1=https://node1:9021", wantError: true, }, { name: "mixed valid and invalid entries", - peers: "1=http://node1:9021,badentry", + peers: "1=https://node1:9021,badentry", wantError: true, }, { name: "invalid zero peer ID", - peers: "0=http://node1:9021", + peers: "0=https://node1:9021", wantError: true, }, { name: "duplicate peer IDs", - peers: "1=http://node1:9021,1=http://node2:9021", + peers: "1=https://node1:9021,1=https://node2:9021", + wantError: true, + }, + { + name: "invalid URL scheme", + peers: "1=http://node1:9021", wantError: true, }, } @@ -93,3 +98,50 @@ func mustParseURL(s string) *url.URL { return u } + +func TestTLSCertificates(t *testing.T) { + tests := []struct { + name string + tls string + want TLSCertificates + wantError bool + }{ + { + name: "valid TLS parameters", + tls: "ca=ca.crt,cert=server.crt,key=server.key", + want: TLSCertificates{ + CAFile: "ca.crt", + CertFile: "server.crt", + KeyFile: "server.key", + }, + }, + { + name: "missing TLS parameters", + tls: "", + wantError: true, + }, + { + name: "invalid TLS parameter format", + tls: "invalidparam", + wantError: true, + }, + { + name: "invalid TLS parameter key", + tls: "unknown=somevalue", + wantError: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c := ConnectParameters{TLS: tc.tls} + got, err := c.TLSCertificates() + if tc.wantError { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.Equal(t, tc.want, got) + }) + } +}