diff --git a/.github/workflows/fips.yml b/.github/workflows/fips.yml index 7af7859ae066..aeaf121f1fe3 100644 --- a/.github/workflows/fips.yml +++ b/.github/workflows/fips.yml @@ -32,6 +32,7 @@ jobs: # all SSL related core tests are covered by below two lists. - t/admin/ssl* t/admin/schema.t t/admin/upstream.t t/config-center-yaml/ssl.t t/core/etcd-mtls.t t/core/config_etcd.t t/deployment/conf_server.t t/misc/patch.t - t/node/grpc-proxy-unary.t t/node/upstream-keepalive-pool.t t/node/upstream-websocket.t t/node/client-mtls.t t/node/upstream-mtls.t t/pubsub/kafka.t t/router/radixtree-sni2.t t/router/multi-ssl-certs.t t/router/radixtree-sni.t t/stream-node/mtls.t t/stream-node/tls.t t/stream-node/upstream-tls.t t/stream-node/sni.t + - t/fips runs-on: ${{ matrix.platform }} timeout-minutes: 90 @@ -83,6 +84,9 @@ jobs: if [[ $test_dir =~ 't/plugin' ]]; then echo "type=plugin" >>$GITHUB_OUTPUT fi + if [[ $test_dir =~ 't/fips' ]]; then + echo "type=plugin" >>$GITHUB_OUTPUT + fi if [[ $test_dir =~ 't/admin' ]]; then echo "type=first" >>$GITHUB_OUTPUT fi diff --git a/t/certs/server_1024.crt b/t/certs/server_1024.crt new file mode 100644 index 000000000000..85c6442c9d4d --- /dev/null +++ b/t/certs/server_1024.crt @@ -0,0 +1,12 @@ +-----BEGIN CERTIFICATE----- +MIIBrTCCARYCFGohAv7D46F+kSOf08X/MLwtazC7MA0GCSqGSIb3DQEBCwUAMBcx +FTATBgNVBAMMDGNhLmxvY2FsaG9zdDAeFw0yMzA1MjIwMjQ3MzZaFw0zMzA1MTkw +MjQ3MzZaMBQxEjAQBgNVBAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOB +jQAwgYkCgYEA2YEV7+FWPl2R9EOSvi2iPyymiUnSaYhIaTuSoqRISOjCSmgrmKpJ +yDN1Cg2hqBHPkWW8BuphpV405ja+94xvOEc0qcP/Or6zDhDfWcaoqxGRAcdwHDgQ +XYMfOxlhwWCp5+vWKep3FPXpHamE09PqUbKWqIa/16aK/1sFR7Q+JJkCAwEAATAN +BgkqhkiG9w0BAQsFAAOBgQCA5aSfk4lZjAEFQ9mWN7Z97b9bnwLw2bv4PgAhDtas +0IxIvhuwR4ZTFv+Pe4PNNNvfnMgTRUWFVpjywUX7Km+k9WFavrikPgAGEfzkR2HR +WE4ETNuS7sGxczosqD+00An4lZ+58uYGEitUOJ6xO80NIhNnOGgo5N/d4fFUTyYH +bw== +-----END CERTIFICATE----- diff --git a/t/certs/server_1024.key b/t/certs/server_1024.key new file mode 100644 index 000000000000..28233461133b --- /dev/null +++ b/t/certs/server_1024.key @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANmBFe/hVj5dkfRD +kr4toj8spolJ0mmISGk7kqKkSEjowkpoK5iqScgzdQoNoagRz5FlvAbqYaVeNOY2 +vveMbzhHNKnD/zq+sw4Q31nGqKsRkQHHcBw4EF2DHzsZYcFgqefr1inqdxT16R2p +hNPT6lGylqiGv9emiv9bBUe0PiSZAgMBAAECgYBvf5j7S4ymk9kKWsmS7FnMANuu +bUWMC+zy5TMaZRUZKxjOg/A1ZrZEBvzslmhUfCzn4DsvYF+GInEDwvTKehdY07I+ ++hpv1M9Wa7v12ofRWNvZjsbMHfiWM/pFBZFYryV4sQ4qHFRj3TKgXu3pZWPx41wn +ayMUtxYRR3Lez4UiMQJBAP31OIC65xbWGr1W/YJ6IwOPFBgyB6O6qFTcR/lAdJ6H +6MVVs8XEWC4o/ZTk8RGog2nWzVsRCN2pqQUGUHBGRE8CQQDbQNL6eGbsuSxM1uUS +PjrAs5t9rfrwpx41ubjRoGIukEYeX1YXDf4WICe/51vE3jvVfVvFOJuvGoO9QqzB +LgaXAkEAtyRG0R74VBGnSvAW9idaZNCj7yb1N2/+wOPyy59d+o2MofLCKFcGOJO6 ++8t2xgM+ce9EPO419JTLnSIGlFE4JQJAGueHfCjOKHpIj11HWse8Ge1wRSnWQzWe +pWUW4tJVefVGRW/ZdpbG+RwVBJ11S2Eh4n6xhi/+GqycQdsuq73kHQJBAPCknTOP +KpiH5qtKw84nsF+RkWZQ6BhzvP5OuW4avQBx1jQUjpjUhOWfctFH7MLI92Ti+iUS +MYi+HgfT9Lx4kbc= +-----END PRIVATE KEY----- diff --git a/t/fips/jwt-auth.t b/t/fips/jwt-auth.t new file mode 100644 index 000000000000..ec1061315775 --- /dev/null +++ b/t/fips/jwt-auth.t @@ -0,0 +1,267 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +use t::APISIX 'no_plan'; + +repeat_each(2); +no_long_string(); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: create public API route (jwt-auth sign) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/2', + ngx.HTTP_PUT, + [[{ + "plugins": { + "public-api": {} + }, + "uri": "/apisix/plugin/jwt/sign" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: add consumer with username and plugins with public_key, private_key(private_key numbits = 512) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "kerouac", + "plugins": { + "jwt-auth": { + "key": "user-key-rs256", + "algorithm": "RS256", + "public_key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKebDxlvQMGyEesAL1r1nIJBkSdqu3Hr\n7noq/0ukiZqVQLSJPMOv0oxQSutvvK3hoibwGakDOza+xRITB7cs2cECAwEAAQ==\n-----END PUBLIC KEY-----", + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIBOgIBAAJBAKebDxlvQMGyEesAL1r1nIJBkSdqu3Hr7noq/0ukiZqVQLSJPMOv\n0oxQSutvvK3hoibwGakDOza+xRITB7cs2cECAwEAAQJAYPWh6YvjwWobVYC45Hz7\n+pqlt1DWeVQMlN407HSWKjdH548ady46xiQuZ5Cfx3YyCcnsfVWaQNbC+jFbY4YL\nwQIhANfASwz8+2sKg1xtvzyaChX5S5XaQTB+azFImBJumixZAiEAxt93Td6JH1RF\nIeQmD/K+DClZMqSrliUzUqJnCPCzy6kCIAekDsRh/UF4ONjAJkKuLedDUfL3rNFb\n2M4BBSm58wnZAiEAwYLMOg8h6kQ7iMDRcI9I8diCHM8yz0SfbfbsvzxIFxECICXs\nYvIufaZvBa8f+E/9CANlVhm5wKAyM8N8GJsiCyEG\n-----END RSA PRIVATE KEY-----" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 3: JWT sign and verify use RS256 algorithm(private_key numbits = 512) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 4: sign/verify use RS256 algorithm(private_key numbits = 512) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, err, sign = t('/apisix/plugin/jwt/sign?key=user-key-rs256', + ngx.HTTP_GET + ) + + if code > 200 then + ngx.status = code + ngx.say(err) + return + end + + local code, _, res = t('/hello?jwt=' .. sign, + ngx.HTTP_GET + ) + + ngx.status = code + } + } +--- error_code: 401 +--- error_log +JWT token invalid: invalid jwt string + + + +=== TEST 5: add consumer with username and plugins with public_key, private_key(private_key numbits = 1024) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "kerouac", + "plugins": { + "jwt-auth": { + "key": "user-key-rs256", + "algorithm": "RS256", + "public_key": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDGxOfVe/seP5T/V8pkS5YNAPRC\n3Ffxxedi7v0pyZh/4d4p9Qx0P9wOmALwlOq4Ftgks311pxG0zL0LcTJY4ikbc3r0\nh8SM0yhj9UV1VGtuia4YakobvpM9U+kq3lyIMO9ZPRez0cP3AJIYCt5yf8E7bNYJ\njbJNjl8WxvM1tDHqVQIDAQAB\n-----END PUBLIC KEY-----", + ]] .. [[ + "private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQDGxOfVe/seP5T/V8pkS5YNAPRC3Ffxxedi7v0pyZh/4d4p9Qx0\nP9wOmALwlOq4Ftgks311pxG0zL0LcTJY4ikbc3r0h8SM0yhj9UV1VGtuia4Yakob\nvpM9U+kq3lyIMO9ZPRez0cP3AJIYCt5yf8E7bNYJjbJNjl8WxvM1tDHqVQIDAQAB\nAoGAYFy9eAXvLC7u8QuClzT9vbgksvVXvWKQVqo+GbAeOoEpz3V5YDJFYN3ZLwFC\n+ZQ5nTFXNV6Veu13CMEMA4NBIa8I4r3aYzSjq7X7UEBkLDBtEUge52mYakNfXD8D\nqViHkyJqvtVnBl7jNZVqbBderQnXA0kigaeZPL3+hkYKBgECQQDmiDbUL3FBynLy\nNX6/JdAbO4g1Nl/1RsGg8svhb6vRM8WQyIQWt5EKi7yoP/9nIRXcIgdwpVO6wZRU\nDojL0oy1AkEA3LpjqXxIRzcy2ALsqKN3hoNPGAlkPyG3Mlph91mqSZ2jYpXCX9LW\nhhQdf9GmfO8jZtYhYAJqEMOJrKeZHToLIQJBAJbrJbnTNTn05ztZehh5ELxDRPBR\nIJDaOXi8emyjRsA2PGiEXLTih7l3sZIUE4fYSQ9L18MO+LmScSB2Q2fr9uECQFc7\nIh/dCgN7ARD1Nun+kEIMqrlpHMEGZgv0RDsoqG+naOaRINwVysn6MR5OkGlXaLo/\nbbkvuxMc88/T/GLciYECQQC4oUveCOic4Qs6TQfMUKKv/kJ09slbD70HkcBzA5nY\nyro4RT4z/SN6T3SD+TuWn2//I5QxiQEIbOCTySci7yuh\n-----END RSA PRIVATE KEY-----" + } + } + } + ]] + ) + ngx.status = code + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 6: JWT sign and verify use RS256 algorithm(private_key numbits = 1024) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwt-auth": {} + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 7: sign/verify use RS256 algorithm(private_key numbits = 1024) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, err, sign = t('/apisix/plugin/jwt/sign?key=user-key-rs256', + ngx.HTTP_GET + ) + + if code > 200 then + ngx.status = code + ngx.say(err) + return + end + + local code, _, res = t('/hello?jwt=' .. sign, + ngx.HTTP_GET + ) + + ngx.status = code + } + } +--- error_code: 401 +--- error_log +JWT token invalid: invalid jwt string + + + +=== TEST 8: sign/verify use RS256 algorithm(private_key numbits = 1024,with extra payload) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, err, sign = t('/apisix/plugin/jwt/sign?key=user-key-rs256&payload=%7B%22aaa%22%3A%2211%22%2C%22bb%22%3A%22222%22%7D', + ngx.HTTP_GET + ) + + if code > 200 then + ngx.status = code + ngx.say(err) + return + end + + local code, _, res = t('/hello?jwt=' .. sign, + ngx.HTTP_GET + ) + + ngx.status = code + } + } +--- error_code: 401 +--- error_log +JWT token invalid: invalid jwt string diff --git a/t/fips/openid-connect.t b/t/fips/openid-connect.t new file mode 100644 index 000000000000..8a23501cd93f --- /dev/null +++ b/t/fips/openid-connect.t @@ -0,0 +1,111 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +use t::APISIX 'no_plan'; + +log_level('warn'); +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } +}); + +run_tests(); + +__DATA__ + +=== TEST 1: configure oidc plugin with small public key +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ "plugins": { + "openid-connect": { + "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH", + "client_secret": "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa", + "discovery": "https://samples.auth0.com/.well-known/openid-configuration", + "redirect_uri": "https://iresty.com", + "ssl_verify": false, + "timeout": 10, + "bearer_only": true, + "scope": "apisix", + "public_key": "-----BEGIN PUBLIC KEY-----\n]] .. + [[MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANW16kX5SMrMa2t7F2R1w6Bk/qpjS4QQ\n]] .. + [[hnrbED3Dpsl9JXAx90MYsIWp51hBxJSE/EPVK8WF/sjHK1xQbEuDfEECAwEAAQ==\n]] .. + [[-----END PUBLIC KEY-----", + "token_signing_alg_values_expected": "RS256" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: hit failed +--- config + location /t { + content_by_lua_block { + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { + method = "GET", + headers = { + ["Authorization"] = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9" .. + ".eyJkYXRhMSI6IkRhdGEgMSIsImlhdCI6MTU4NTEyMjUwMiwiZXhwIjoxOTAwNjk" .. + "4NTAyLCJhdWQiOiJodHRwOi8vbXlzb2Z0Y29ycC5pbiIsImlzcyI6Ik15c29mdCB" .. + "jb3JwIiwic3ViIjoic29tZUB1c2VyLmNvbSJ9.u1ISx7JbuK_GFRIUqIMP175FqX" .. + "RyF9V7y86480Q4N3jNxs3ePbc51TFtIHDrKttstU4Tub28PYVSlr-HXfjo7w", + } + }) + ngx.status = res.status + if res.status == 200 then + ngx.say(true) + end + } + } +--- error_code: 401 +--- error_log +jwt signature verification failed: invalid key length diff --git a/t/fips/ssls.t b/t/fips/ssls.t new file mode 100644 index 000000000000..d7f08370483a --- /dev/null +++ b/t/fips/ssls.t @@ -0,0 +1,75 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +log_level('warn'); +no_root_location(); +no_shuffle(); + +run_tests(); + +__DATA__ + +=== TEST 1: configure cert with smaller key +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin") + local json = require("toolkit.json") + local ssl_cert = t.read_file("t/certs/server_1024.crt") + local ssl_key = t.read_file("t/certs/server_1024.key") + + local data = { + upstream = { + type = "roundrobin", + nodes = { + ["127.0.0.1:1980"] = 1, + }, + }, + uri = "/hello" + } + assert(t.test('/apisix/admin/routes/1', + ngx.HTTP_PUT, + json.encode(data) + )) + + local data = { + cert = ssl_cert, + key = ssl_key, + sni = "localhost", + } + local code = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + json.encode(data) + ) + + if code >= 300 then + ngx.status = code + end + } + } +--- request +GET /t + + + +=== TEST 2: curl failed +--- exec +curl -k https://localhost:1994/hello -H "Host: localhost" +--- error_log +failed to set PEM cert: SSL_use_certificate() failed