diff --git a/.gitignore b/.gitignore index 41f5824..2f894a6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ package-lock.json .DS_Store PR-SUBMISSION-GUIDE.md ".claude/" +src/fixtures/certs/ +src/fixtures/generate-certs.sh diff --git a/README.md b/README.md index 09a60dc..d864281 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ options: - `--debug`: Enable debug logging - `--shell`: Spawn the server via the user's shell - `--apiKey`: API key for authenticating requests (uses X-API-Key header) +- `--sslCa`: Filename to override the trusted CA certificates +- `--sslCert`: Cert chains filename in PEM format +- `--sslKey`: Private keys filename in PEM format ### Stateless Mode diff --git a/src/bin/mcp-proxy.ts b/src/bin/mcp-proxy.ts index 146cdc8..ba2a457 100644 --- a/src/bin/mcp-proxy.ts +++ b/src/bin/mcp-proxy.ts @@ -78,6 +78,18 @@ const argv = await yargs(hideBin(process.argv)) describe: "The SSE endpoint to listen on", type: "string", }, + sslCa: { + describe: "Filename to override the trusted CA certificates", + type: "string", + }, + sslCert: { + describe: "Cert chains filename in PEM format", + type: "string", + }, + sslKey: { + describe: "Private keys filename in PEM format", + type: "string", + }, stateless: { default: false, describe: @@ -171,6 +183,9 @@ const proxy = async () => { argv.server && argv.server !== "sse" ? null : (argv.sseEndpoint ?? argv.endpoint), + sslCa: argv.sslCa, + sslCert: argv.sslCert, + sslKey: argv.sslKey, stateless: argv.stateless, streamEndpoint: argv.server && argv.server !== "stream" diff --git a/src/fixtures/certs/ca-cert.pem b/src/fixtures/certs/ca-cert.pem new file mode 100644 index 0000000..c18b969 --- /dev/null +++ b/src/fixtures/certs/ca-cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFTTCCAzWgAwIBAgIUBu6DtJfJmIycUYfyrmm7rbBGy00wDQYJKoZIhvcNAQEL +BQAwNTETMBEGA1UEAwwKTXkgVGVzdCBDQTERMA8GA1UECgwIVGVzdCBPcmcxCzAJ +BgNVBAYTAlVTMCAXDTI1MTIyMzE0MzE1M1oYDzIxMjUxMTI5MTQzMTUzWjA1MRMw +EQYDVQQDDApNeSBUZXN0IENBMREwDwYDVQQKDAhUZXN0IE9yZzELMAkGA1UEBhMC +VVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDZVkXDF0+wxaXwzfDQ +ooGhSYAob5HF+FS45sjD4nXvJK9fWt1brUn5aeQbj8a9UzCwP2Acq1va+zauEEe0 +KB0FPNs34lbMgcF218StNQZQ+6eFMbyNx59LjrTrIfgk8VUhnQZhk6nz7G5gkPqZ +T3UZwgL9wn5FOHCfEN+Udsf6p2XosZ2ggvHMoKHFJ3DfdMmD/gpeSW884FxRebt1 +Oc/kPttgasL5FRCDmU5TFsTgmBzo2VPllHFiaxnT/72ENWhyIgtLpIJrJOvqvjim +Cr8QSHANR7WBkqndEGeHbk/aIx4myVYfUQQiStr/FVtLaUX3wqjjedhBmFYzwR4q +HZzQ5McqM8xcG4wPwcjXP1VGxMP5qwG483EvQnK8sKqBS04CP8l4sPLdTG4c2nn0 +1voI8aVWYoLG+69uz1mXDd2b4mqfKVynbzINM6wyKLGtWzETPbtDS7xJFW9OyD7p +bs4bV3UUPCxPe6WQOpV+sfYut/2d/D5Ceie2vKIcozIZH/hyJEMSnaRSwGxfeNmr +HtsrTquGbVZBcAGx+MaEL9ngg5rTKnTd3oduFyWSFPRed/m3s53bQf2uXyeym3u3 +KEwncuv841YtihrYgM7QU85W8Dm5omLjMGa8taqGibrRl1X5OYOVcNkJoS3STlKx +WiR2X/Xf6N3CvMFjIBd4YBfVlQIDAQABo1MwUTAdBgNVHQ4EFgQUl0ycKiKN2i6N +1DRfHpLbL4uI4P8wHwYDVR0jBBgwFoAUl0ycKiKN2i6N1DRfHpLbL4uI4P8wDwYD +VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEANaKKvaWiBTqjW/K3abPm +OSyMvWtTVSfAjZOYN7DYVSu5ejwHH/SeoSVMVCSpZEfkBE+FHJltlemAWxtHmfIr +r1m9p15z/lTdVLpJOnEKz9+jRt26WSzqx4OjuyR7BN6rXGLAev9nprXrGYZZymX4 +sHwvLS/9jt0G7wstO+VAHLK7Lx7aQf1aZYert3vuLIIomEMpqqtX3Su6l7AsTlpn +lDGfs9ia4pEvfynBF9D25T80o5d6MmFWRtLBzyw9fxaT6nnYDJXTtUq6MQSSIXDe +sE9QgtPOFdicS+i1AU5QzaHHrn+pBcizYDxauKt+95LdoQWtxCNzxIF3NJBki7Gr +fDTfc2VEpsL3144dGblI/tbJQgxQ+UibLC1EHbxXE0NU5Btuw8vYJ96p6bJMsiBR +Sj/2S9nxYpJkXasvG7fYXLo0PeA09KG6xO/R0eSsvCGRRaaEeJxHzXCyOpbBRJ4U ++Zc2XlSZ5+AoiKypGCD7WrNqygufkEOtsumezixO1DSCuXaNn1zoQwFmMDqrqE5V +bXQTUqV1RhtYIHx5lhvLAg5dID+49nXJbKuUpJGePp5pOYcuJC4usLQRC+s2vjIE +Ao4sZ6SaYkU8vcMKLeptj5g7wQmP+JU3kEi2ts/pwKvkbIvPkgVfwm0LfIJ0tMoh +1yH5n+BwqdNMjChwehcGc/4= +-----END CERTIFICATE----- diff --git a/src/fixtures/certs/ca-cert.srl b/src/fixtures/certs/ca-cert.srl new file mode 100644 index 0000000..47426db --- /dev/null +++ b/src/fixtures/certs/ca-cert.srl @@ -0,0 +1 @@ +0DC0F44BC9FB9F9DDC0EB3A0647462238160C860 diff --git a/src/fixtures/certs/ca-key.pem b/src/fixtures/certs/ca-key.pem new file mode 100644 index 0000000..72073b8 --- /dev/null +++ b/src/fixtures/certs/ca-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +g4WLqVTtQLCY/u1oinlx8+XA02ly4iA= +-----END PRIVATE KEY----- diff --git a/src/fixtures/certs/client-cert.pem b/src/fixtures/certs/client-cert.pem new file mode 100644 index 0000000..5ec7cbd --- /dev/null +++ b/src/fixtures/certs/client-cert.pem @@ -0,0 +1,31 @@ +-----BEGIN CERTIFICATE----- +MIIFPTCCAyWgAwIBAgIUDcD0S8n7n53cDrOgZHRiI4FgyGAwDQYJKoZIhvcNAQEL +BQAwNTETMBEGA1UEAwwKTXkgVGVzdCBDQTERMA8GA1UECgwIVGVzdCBPcmcxCzAJ +BgNVBAYTAlVTMCAXDTI1MTIyMzE0MzE1NVoYDzIxMjUxMTI5MTQzMTU1WjA2MRQw +EgYDVQQDDAt0ZXN0LWNsaWVudDERMA8GA1UECgwIVGVzdCBPcmcxCzAJBgNVBAYT +AlVTMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7A6Yj7FOvKouS7Xt +p3eQ3oTteV3TrcenP0ahepMBUNRx/XcN4Qmr75/KVQC2KOYXi1CmcGGEIDeIDR4x +DF9qRcQ30k6r+yQAthAHdUeud8SkgO3S5WzI7uOSagAGLGq3FInURiE0TZkLJ9We +RNhqShc+RI0FsLr4E3E8PvWq5Pv48wo86o4W3nx4hgqHV9OTUNkE24cwPR1Ws1H0 +HFrDOI3lenuKdAHFTX/O6SU2Yy3mZjN4b8Hm1pT9IowhI+UXWSs7BIdfvJlK7VgP +rpaSe+lG3ZBLe4MBn/GwwQWXE2UoEp+TzwvCoopWICD7gA/6X9vSE+5NYg4oiNcT +Axgc0HKY0+HXrmJ+CWZRZ93LTk7ipfXhf937FAkg4J96mHF5+XMJny/BUKcxEkok +ul6c98fZA2S245ZOlYHanvz/2Vsp3YGwe94XSEd11/rQC0nH76izX86FxqiinBfD +pXKu8OMkxJwBUkNCfTDGY+5pywUqLJ4CzJwleO+sQPLVwaU9di0v5CeaKuStw/IJ +KjuNfkF8bZXjiDQdrtC75sLoQZ84RpqflsWBm4+ex7tJVimfI4aFllmRY18qAPtF +0fuoiNjOnXxJ3MJ+hAEpDypE8ab6bCX6FemJ4Zg1kiMvmd5qczKw/L6mYXHofBqE +slJPtMhX8zEhUFTULC1tIgyqWasCAwEAAaNCMEAwHQYDVR0OBBYEFD9ma6JfkIA+ +g/vLkok7hRuKbZUjMB8GA1UdIwQYMBaAFJdMnCoijdoujdQ0Xx6S2y+LiOD/MA0G +CSqGSIb3DQEBCwUAA4ICAQCnj+42ATkby3jWKu9fKLYDFgPxUUoOa8XSgUUyEtdy +konakXzC2xmxA14JxbV8G3cz81+5O+sEFct+Nre0T2IMFVBe3AmjY9zLqxOqsQsd +qaXJQH+ZvVd9wUebFd/YoaFU3rcPvrwV13LDJkMZNuggwKaiUEk2zRTMXuPRBD8A +/e01aa0uXV/twD/OfPvqL2jH1SCguZGt9G4KMx2s9UeztU5DhwNaFVVb9O0KwZOE +uwEe1gX9/BOU6XZ4DpN763vc/mZnIidnVY0zEER9cf36lWMbCqRkThY+K7gfacMd +YtuqP38ZsFkjyaE9cnZvBCf16HJTYhQlpNB/aYeiotiqzF7LhIdQ5yrKQMVovDd4 +UOHhv4ED81HSlQweBlpOQ5hKePtC7uR3AxLT0jRcChVK7nyn/bnYybEt3giCfPI/ +gbIa/2AKIwUUwmC5ZZ20XVzRHcrRejF0l3Fv60ka2jy9nKf3FKHEIZTqTX+v4BRf +AGG0yujQXblwTVpYgeIQvRWQA0tmLYxdBEGWvo95t9nuYGJtath/oejevarUOwxX +SuV5RhNe3JxNkrHvJj1LplzEP5JttS0jKjrYTcV8I4w2CNIUVkRjikdUQSurnedH +dkk3X8bccQfHSpL5+C+1wXsdxNUycl9tLjPwwQWuk1L7fENs1O0YOsCDZM6NVW7z +bQ== +-----END CERTIFICATE----- diff --git a/src/fixtures/certs/client-csr.pem b/src/fixtures/certs/client-csr.pem new file mode 100644 index 0000000..761ff3b --- /dev/null +++ b/src/fixtures/certs/client-csr.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEezCCAmMCAQAwNjEUMBIGA1UEAwwLdGVzdC1jbGllbnQxETAPBgNVBAoMCFRl +c3QgT3JnMQswCQYDVQQGEwJVUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC +ggIBAOwOmI+xTryqLku17ad3kN6E7Xld063Hpz9GoXqTAVDUcf13DeEJq++fylUA +tijmF4tQpnBhhCA3iA0eMQxfakXEN9JOq/skALYQB3VHrnfEpIDt0uVsyO7jkmoA +BixqtxSJ1EYhNE2ZCyfVnkTYakoXPkSNBbC6+BNxPD71quT7+PMKPOqOFt58eIYK +h1fTk1DZBNuHMD0dVrNR9BxawziN5Xp7inQBxU1/zuklNmMt5mYzeG/B5taU/SKM +ISPlF1krOwSHX7yZSu1YD66WknvpRt2QS3uDAZ/xsMEFlxNlKBKfk88LwqKKViAg ++4AP+l/b0hPuTWIOKIjXEwMYHNBymNPh165ifglmUWfdy05O4qX14X/d+xQJIOCf +ephxeflzCZ8vwVCnMRJKJLpenPfH2QNktuOWTpWB2p78/9lbKd2BsHveF0hHddf6 +0AtJx++os1/OhcaoopwXw6VyrvDjJMScAVJDQn0wxmPuacsFKiyeAsycJXjvrEDy +1cGlPXYtL+QnmirkrcPyCSo7jX5BfG2V44g0Ha7Qu+bC6EGfOEaan5bFgZuPnse7 +SVYpnyOGhZZZkWNfKgD7RdH7qIjYzp18SdzCfoQBKQ8qRPGm+mwl+hXpieGYNZIj +L5neanMysPy+pmFx6HwahLJST7TIV/MxIVBU1CwtbSIMqlmrAgMBAAGgADANBgkq +hkiG9w0BAQsFAAOCAgEAOdw8qDnzpAYBdK+/JcTSzZRNzdJbe776wTuJYM61Tyzk +8b9jVgMLWNWwgXO/itealdRGh1/B5b7fhnEMr6J8aglrGEj6JBS89KzbH4jmgYtD +u+lzj0/hJmMhIcv+sKNduHhaZIeX5bKzzm4jR92zh8gllu0d47imLI5jJ9U7MNvQ +WnL3NYK3Kp1bXQhX0O7Wrxz0tmGSkBC+cCgQpgvuZG9w9FYruzrkNhkunQFgm70W +zWy0sm6innpX0OwfM2etQgoUSa3E1h3scbyc0xpcdgPRUxmRv17ETQ3kyRecNsd8 +g005es74LD20N0zck+y0UrahJi25+RklxhLcsNKj/EuOZJFplrMA4B32wyt6TSBQ ++Fbv1aJL7pnE9t+zjhtG+/9TBjZpSOUw+FltvaSWqVxsdR/dKEdx4T8e0385b1YR +tBWBQpagVSiLaeAj3tmrzIegCdm3kiJFAcGpD/Kncb6x73tA9iKYkIc19smOrh0f +uzcPoRuV4FAsZPGEJHCYRx9cz9nakX40qc0iS/ImONLP0P3+cDcKeqZmAPS7EBjM +Epo4tHI/t4QEUEdYNXo8A1daC/CJC31Ma88UlxG94Q/MfcOtawTlrze0Er/nhmv8 +zNIA8/PMU5XQRpox1t4DCA/0hAV6FKqJHzr+fNiC/8BSY9sWff7Dvx4hrZAyZ2I= +-----END CERTIFICATE REQUEST----- diff --git a/src/fixtures/certs/client-key.pem b/src/fixtures/certs/client-key.pem new file mode 100644 index 0000000..a170a63 --- /dev/null +++ b/src/fixtures/certs/client-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +cg00VLx9bxHaQxbEpG1SKrk9b8Bn +-----END PRIVATE KEY----- diff --git a/src/fixtures/certs/server-cert.pem b/src/fixtures/certs/server-cert.pem new file mode 100644 index 0000000..a205359 --- /dev/null +++ b/src/fixtures/certs/server-cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFOzCCAyOgAwIBAgIUDcD0S8n7n53cDrOgZHRiI4FgyF8wDQYJKoZIhvcNAQEL +BQAwNTETMBEGA1UEAwwKTXkgVGVzdCBDQTERMA8GA1UECgwIVGVzdCBPcmcxCzAJ +BgNVBAYTAlVTMCAXDTI1MTIyMzE0MzE1NFoYDzIxMjUxMTI5MTQzMTU0WjA0MRIw +EAYDVQQDDAlsb2NhbGhvc3QxETAPBgNVBAoMCFRlc3QgT3JnMQswCQYDVQQGEwJV +UzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKxnvwdLZzCXGBWooMJz +8y8HfHdNEjeHi4ueQPtnkk45Awse2PWwuHSB0XMj6Pz6H7UG5RyfphEyokydFiur +Nf5hk4T04HZi4Kwtx273wjE7/dkyxfD9kimf39beW+N9+92fUPpBXjmdv9VcRRLP +zgpPMsmeoIh0ebvVxawr52x5LdGUXgJw38pXgfIfaBTKP7wj9SrfqDp+QKSF7Z/f +ek07l23YhBNsDYfbz6AfpgT0gk8x6WNvLU6tCahLZNoZDjwU51Fj8DcHCjx3UePg +UWUonR+d10PH+lISxqpefT0/vURK/moebeki5q/mdWwgRTMEpSHEhtsa36D5VJ2R +PeQ1oUMS8oy0WCxV13f+0jQ7v694q3Av171kKQw15bkXR8qAlbE55I/jCW8vDDde +2nkROz6xIzSGQseKGN41EO3imzSfr4HPY3LfRy0sr5K26iICu5nWOVPGDU4Yhcj9 +IVhOCEtDoWQoD1IZZ0O4X3xveZRk7wkwWdM45t7ViZ2psJeuSdue5eO58l9oygf5 +3YWsv9Z1QX7/Dp+u1NUMTOJ+Od6U4QWhiZZvkMooFHFPXSQqspPFHJq4BXbnpM+D +zX5UmaZUqOnDaI6b6HLZ+PCsBJsGpcIh3iFLx3dZK9XtOnvSs69tPSvxz1eqa56B +8saA2iZcycI/cIPobozfE1NLAgMBAAGjQjBAMB0GA1UdDgQWBBSNzEYTY16iepNb +p9BlKwMYz16YyTAfBgNVHSMEGDAWgBSXTJwqIo3aLo3UNF8ektsvi4jg/zANBgkq +hkiG9w0BAQsFAAOCAgEAK7jhZVBDdL/+5Qhf+FIU66HcKnL3uXSSSItQUEkYOTWO +TmoXJCKXv3Munx0F2rJoMZAzj6jc6xvDjThYD15jqIoHUVmnhEB7v75uRar2Gxed +cCQYq0l36NWOAyUbpLkieHlNNkWLwoYPoQ0sp8vk01i7Nb1YTp06zpHmj2X/jX2C +Gt4U3zdAx/6iduSf90vgZZ96EeEatfFCcik8ox0a/F9Mi6BVtXq22XNuB0W/F1d9 +aQp9pAcYtQZXaRzf49s68tagIH95arcmYFLUdyyFdt+odLYhsybhlSzdx5xmGyYL +twQ5wSqHm0RzpvwvzHWAWkKlIxNWu//BFZxTYOfUi8gxuQ/J5FDGvCfaHy8Xhlxw +HgCoiN0+GFzF0ABC/ESQZPZMIjK0L7LwICyua089q0EOw4Q6uu05wAPtk2rTI49G +jxg1CPhyld2sPJpWoMxOTHWPOo5FibEST0U+tJllb1JVecaaJq6intxeeVv5BZiE +z8t8bwpekguAYxFM1B/QgewkzLwmlnpRFoLxE5fALn+8eChNuQINeXNq6kmytwCe +QtxlZcjWHnYediug8BrsyuV3DMPBJNhfJmN5j3fs/u9vqMvmzGFACnEY8vw7X6NU +TFa4m4zqgtlMjTfuxkSYHOWrXDBM3GJbWjT3OTppWzrxSZw8uzkiUs8W2+FM6N4= +-----END CERTIFICATE----- diff --git a/src/fixtures/certs/server-csr.pem b/src/fixtures/certs/server-csr.pem new file mode 100644 index 0000000..badde78 --- /dev/null +++ b/src/fixtures/certs/server-csr.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEeTCCAmECAQAwNDESMBAGA1UEAwwJbG9jYWxob3N0MREwDwYDVQQKDAhUZXN0 +IE9yZzELMAkGA1UEBhMCVVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC +AQCsZ78HS2cwlxgVqKDCc/MvB3x3TRI3h4uLnkD7Z5JOOQMLHtj1sLh0gdFzI+j8 ++h+1BuUcn6YRMqJMnRYrqzX+YZOE9OB2YuCsLcdu98IxO/3ZMsXw/ZIpn9/W3lvj +ffvdn1D6QV45nb/VXEUSz84KTzLJnqCIdHm71cWsK+dseS3RlF4CcN/KV4HyH2gU +yj+8I/Uq36g6fkCkhe2f33pNO5dt2IQTbA2H28+gH6YE9IJPMeljby1OrQmoS2Ta +GQ48FOdRY/A3Bwo8d1Hj4FFlKJ0fnddDx/pSEsaqXn09P71ESv5qHm3pIuav5nVs +IEUzBKUhxIbbGt+g+VSdkT3kNaFDEvKMtFgsVdd3/tI0O7+veKtwL9e9ZCkMNeW5 +F0fKgJWxOeSP4wlvLww3Xtp5ETs+sSM0hkLHihjeNRDt4ps0n6+Bz2Ny30ctLK+S +tuoiAruZ1jlTxg1OGIXI/SFYTghLQ6FkKA9SGWdDuF98b3mUZO8JMFnTOObe1Ymd +qbCXrknbnuXjufJfaMoH+d2FrL/WdUF+/w6frtTVDEzifjnelOEFoYmWb5DKKBRx +T10kKrKTxRyauAV256TPg81+VJmmVKjpw2iOm+hy2fjwrASbBqXCId4hS8d3WSvV +7Tp70rOvbT0r8c9XqmuegfLGgNomXMnCP3CD6G6M3xNTSwIDAQABoAAwDQYJKoZI +hvcNAQELBQADggIBAGXu3hbNQW47cL+aVpwMmumh1959FvNvh271KBsnLmfnAJjS +ZMUHtxLUzzcQ6KKE72/qvLrV2f+X8PWF5D25HwNxBF/cGp1324oeLpzf5prFHEqR +W/RQLc+LWhJ6Plyj8ASz8rLWZMAc3AataexdA1Sle/2usDYrl2D23FvBGf4R42Ce +KZd94IUqXw4szHWKgPuP8+qwb9rtlZIuo69bggD9BOozg6pPRK5OjVnDjUWM9QcJ +XUOmo+CiS5XHWhIKKRAiBkCGefbBuEJQpIh5YDRe4kgdi8zwsfU8DlEXRbmahqa4 +9/P8M3gONeCBTb9NVTLjT4fRoYRW6VzP5egMO6NT2trIEQPNpgULKdvArDIgdIsY +2UKSS4EEqNWyK6lXvQ8eBv8uX75izzRLBmVXPLfCAqOhreFE4/IgRddCBtdZFoW4 +Oq65pJ/+TJdJ5uELma5KZ4+7BKVbyZVFfd4zg3HzEBLq7Z5iBqEL0/HGDvCxLI1b +KR4hxCUAY0oP9SUGFags3/c5nacMChsbzcQg9YZLrUn4Vh4o/X4T1r94v/viDIE+ +a24yjLFb0+AlzQlAWy6Xircg6PY5iZ8laWcdJg8mAQ9ex7xp12/FEeCyObLI23fS +CgoY6Jc73w2r0x9LTdm+Rx+oQ81KoLZpwIW9r7k2CvH4RdozXgMc8/yt40by +-----END CERTIFICATE REQUEST----- diff --git a/src/fixtures/certs/server-key.pem b/src/fixtures/certs/server-key.pem new file mode 100644 index 0000000..c3f5d02 --- /dev/null +++ b/src/fixtures/certs/server-key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +fX8pgOAQlKQkNTK3xjcXrloHtkpTjQ== +-----END PRIVATE KEY----- diff --git a/src/fixtures/generate-certs.sh b/src/fixtures/generate-certs.sh new file mode 100755 index 0000000..c14b7f8 --- /dev/null +++ b/src/fixtures/generate-certs.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +mkdir -p certs +cd certs + +# Generate CA private key +openssl genrsa -out ca-key.pem 4096 + +# Generate CA certificate +openssl req -new -x509 -days 36500 -key ca-key.pem -out ca-cert.pem \ + -subj '/CN=My Test CA/O=Test Org/C=US' + +# Generate server private key +openssl genrsa -out server-key.pem 4096 + +# Create certificate signing request (CSR) +openssl req -new -key server-key.pem -out server-csr.pem \ + -subj '/CN=localhost/O=Test Org/C=US' + +# Sign server cert with CA +openssl x509 -req -days 36500 -in server-csr.pem \ + -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \ + -out server-cert.pem + +# Generate client private key +openssl genrsa -out client-key.pem 4096 + +# Create client CSR +openssl req -new -key client-key.pem -out client-csr.pem \ + -subj '/CN=test-client/O=Test Org/C=US' + +# Sign client cert with CA +openssl x509 -req -days 36500 -in client-csr.pem \ + -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial \ + -out client-cert.pem + +echo "Certificates created successfully!" diff --git a/src/startHTTPServer.test.ts b/src/startHTTPServer.test.ts index b7f7c49..4254b34 100644 --- a/src/startHTTPServer.test.ts +++ b/src/startHTTPServer.test.ts @@ -4,7 +4,9 @@ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { EventSource } from "eventsource"; +import fs from "fs"; import { getRandomPort } from "get-port-please"; +import https from "https"; import { setTimeout as delay } from "node:timers/promises"; import { expect, it, vi } from "vitest"; @@ -2085,3 +2087,59 @@ it("supports custom methods and maxAge", async () => { await httpServer.close(); }); + +// SSL Tests + +it("supports creating an SSL server", async () => { + const port = await getRandomPort(); + + const httpServer = await startHTTPServer({ + createServer: async () => { + const mcpServer = new Server( + { name: "test", version: "1.0.0" }, + { capabilities: {} }, + ); + return mcpServer; + }, + port, + sslCert: 'src/fixtures/certs/server-cert.pem', + sslKey: 'src/fixtures/certs/server-key.pem', + }); + + const options = { + ca: fs.readFileSync('src/fixtures/certs/ca-cert.pem'), + cert: fs.readFileSync('src/fixtures/certs/client-cert.pem'), + hostname: 'localhost', + key: fs.readFileSync('src/fixtures/certs/client-key.pem'), + method: 'GET', + path: '/ping', + port, + }; + + // Use https.get to test client certificate authentication + // (Node's fetch API doesn't support custom HTTPS agents with client certs) + const response = await new Promise<{statusCode?:number, text:string}>((resolve, reject) => { + https.get(options, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + resolve({ statusCode: res.statusCode, text: data }); + }); + + res.on('error', (err) => { + reject(err); + }); + }).on('error', (err) => { + reject(err); + }); + }); + + expect(response.statusCode).toBe(200); + expect(response.text).toBe("pong"); + + await httpServer.close(); +}); diff --git a/src/startHTTPServer.ts b/src/startHTTPServer.ts index 2224eee..ffaaa38 100644 --- a/src/startHTTPServer.ts +++ b/src/startHTTPServer.ts @@ -5,7 +5,9 @@ import { StreamableHTTPServerTransport, } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js"; +import fs from "fs"; import http from "http"; +import https from "https"; import { randomUUID } from "node:crypto"; import { AuthConfig, AuthenticationMiddleware } from "./authentication.js"; @@ -857,6 +859,9 @@ export const startHTTPServer = async ({ onUnhandledRequest, port, sseEndpoint = "/sse", + sslCa, + sslCert, + sslKey, stateless, streamEndpoint = "/mcp", }: { @@ -876,6 +881,9 @@ export const startHTTPServer = async ({ ) => Promise; port: number; sseEndpoint?: null | string; + sslCa?: null | string; + sslCert?: null | string; + sslKey?: null | string; stateless?: boolean; streamEndpoint?: null | string; }): Promise => { @@ -894,7 +902,7 @@ export const startHTTPServer = async ({ /** * @author https://dev.classmethod.jp/articles/mcp-sse/ */ - const httpServer = http.createServer(async (req, res) => { + const requestListener: http.RequestListener = async (req, res) => { // Apply CORS headers applyCorsHeaders(req, res, cors); @@ -958,7 +966,36 @@ export const startHTTPServer = async ({ } else { res.writeHead(404).end(); } - }); + }; + + let httpServer; + if (sslCa || sslCert || sslKey) { + const options: https.ServerOptions = {}; + if (sslCa) { + try { + options.ca = fs.readFileSync(sslCa); + } catch (error) { + throw new Error(`Failed to read CA file '${sslCa}': ${(error as Error).message}`); + } + } + if (sslCert) { + try { + options.cert = fs.readFileSync(sslCert); + } catch (error) { + throw new Error(`Failed to read certificate file '${sslCert}': ${(error as Error).message}`); + } + } + if (sslKey) { + try { + options.key = fs.readFileSync(sslKey); + } catch (error) { + throw new Error(`Failed to read key file '${sslKey}': ${(error as Error).message}`); + } + } + httpServer = https.createServer(options, requestListener); + }else{ + httpServer = http.createServer(requestListener); + } await new Promise((resolve) => { httpServer.listen(port, host, () => {