ПО промежуточного слоя сервера Node.js ведет себя в кластере AKS иначе, чем когда сервер работает локально

Я развертываю свой сервер Node.js на AKS, и у меня возникает несколько непредвиденных ситуаций, которые не происходят на моем локальном компьютере. Я пробовал разные вещи, чтобы отладить его, но у меня ничего не получается.

Я использую промежуточное ПО как

exports.clientApiKeyValidation = (req, res, next) => {

   try {
      const clientApiKey = req.get('api_key');
      console.info(`received api_key is ${clientApiKey}
      and expected API_KEY is ${process.env.API_KEY}`);
      const receivedTypeof = console.info('clientApiKey ', typeof clientApiKey);
      const expectedTypeof = console.info('expected ', typeof process.env.API_KEY);
      console.info('req.headers is: ', req.headers);
      if (!clientApiKey) {
         return res.status(400).send({
            status: false,
            message: "Missing Api Key"
         });
      }
      if (clientApiKey === process.env.API_KEY) {

         // console.info('Api key correct');
         next();
      }
      else {
         return res.status(400).send({
            status: false,
            message: "Invalid Api Key"
         });
      }
   } catch (error) {
      res.status(401).json({
         error: new Error('Invalid request!')
      });
   }

}

используется на таких маршрутах, как

router.get('/users', auth.clientApiKeyValidation, userController.findUsers); 

В app.js я также установил несколько вещей, в том числе функцию промежуточного программного обеспечения, позволяющую использовать эти параметры как

app.disable('x-powered-by');
app.use(express.json({ limit: '50mb' }));
app.use(function (req, res, next) {
  // either res.setHeader or res.header works.
  // res.setHeader('Access-Control-Allow-Origin', 'http://localhost:5000');

  res.setHeader(
    'Access-Control-Allow-Origin',
    'https://xxx.westeurope.cloudapp.azure.com'
  );
  res.setHeader(
    'Access-Control-Allow-Methods',
    'GET, POST, PUT, DELETE'
  );
  // res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader(
    'Access-Control-Allow-Headers',
    'content-type, api_key, AuthToken, apikey'
  );
  // res.setHeader('Access-Control-Allow-Credentials', true);

  // res.header('Access-Control-Allow-Origin', 'http://localhost:5000');
  // res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
  // res.header('Access-Control-Allow-Headers', 'Origin, Content-Type, api_key, Accept');
  // console.info(' res headers added: ', res.getHeaders());
  next();
});

app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(express.json());
app.use(express.json({ type: 'application/json' }));


Я отправляю ему запросы из своего приложения Flutter, добавляя заголовки, например

    await _firebaseAuth.currentUser?.getIdToken().then((idToken) {
      headers = {
        'Content-Type': 'application/json',
        'api_key': Environment.dbApiKey,
        'AuthToken': idToken
      };
    });
    FixitUser? userDetails;

    // final Uri uri = Uri.http(Environment.dbUrl, '/api/users');
    final Uri uri = Uri.https(Environment.dbUrl, '/server/api/users');
    log('headers are $headers');
    await get(uri, headers: headers).then((resp) {
      log('UserMongoDBRepository.downloadUserDetails resp.body is : ${resp.body}');

...

При запуске сервера напрямую журналы промежуточного программного обеспечения показывают, что все работает так, как ожидалось.

received api_key is hjkdiu-slia7h-si9udd989jw-ols8dh
      and expected API_KEY is hjkdiu-slia7h-si9udd989jw-ols8dh
clientApiKey  string
expected  string
req.headers is:  {
  'user-agent': 'Dart/2.18 (dart:io)',
  'accept-encoding': 'gzip',
  api_key: 'hjkdiu-slia7h-si9udd989jw-ols8dh',
  host: '192.168.1.48:3000',
  'content-type': 'application/json',
  authtoken: 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjVhNTA5ZjAxOWY3MGQ3NzlkODBmMTUyZDFhNWQzMzgxMWFiN2NlZjciLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoidmluY2Vuem8gY2FsaWEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDYuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy13TVNwMUxZd2hPZy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCSS9OdE43TTlTMEVIUS9zOTYtYy9waG90by5qcGciLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZml4LWl0LWI0YjAwIiwiYXVkIjoiZml4LWl0LWI0YjAwIiwiYXV0aF90aW1lIjoxNjc1Nzc1MTg2LCJ1c2VyX2lkIjoiWnhtejJHSmxNUlBXdjBMRGgyRDg4Y0o3T3V6MSIsInN1YiI6Ilp4bXoyR0psTVJQV3YwTERoMkQ4OGNKN091ejEiLCJpYXQiOjE2NzU4NDY4MDAsImV4cCI6MTY3NTg1MDQwMCwiZW1haWwiOiJ2aW5jZW56by5jYWxpYS4xOTc2QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV9udW1iZXIiOiIrMzkzNjYxNDcxMzEzIiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNjkyMTg2MTcwOTA0NTM5MzU5MiJdLCJwaG9uZSI6WyIrMzkzNjYxNDcxMzEzIl0sImVtYWlsIjpbInZpbmNlbnpvLmNhbGlhLjE5NzZAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.ZNJFrxWlycMVgg4VAdWt6Q0WR5yrWPar5_UJwhQ9-hVX25aKC69yDpoM2adx3OIQ-hlsGz1bNvVEUSfvWUWRWF-TaX2TVeLb5z0blAtl8A1cGcvnaryffr0jpCtN_nhDg3WTtHw4B2USDe432TxSXx0ICrk8bx_fum8jsfBvAh_xU8bnr6lLsc0pltcmU-zun0rhZcC6jpxua2d0jIwWyWSXurjNBkLkduzOpUbw6KCLur7wVcxz-HmuD67D0qx5dr37malLbOBt5mcfOtdJEJcEq55XqXqUanopT_OWTWxByMrD_CXyZnLu_q1DgSBMM4kaLrSA14ETD_EvIeqHOQ',
  
}

Когда вместо этого он работает в кластере AKS, api_key отсутствует в заголовках полученных запросов, как показывают журналы, поэтому промежуточное ПО отвечает {"status":false,"message":"Missing Api Key"}

received api_key is undefined
      and expected API_KEY is some api key

req.headers is:  {
  host: 'xxx.westeurope.cloudapp.azure.com',
  'x-request-id': '2ecc2ec74c808cf40f816921374f72d4',
  'x-real-ip': '81.56.11.23',
  'x-forwarded-for': '81.56.11.23',
  'x-forwarded-host': 'xxx.westeurope.cloudapp.azure.com',
  'x-forwarded-port': '443',
  'x-forwarded-proto': 'https',
  'x-forwarded-scheme': 'https',
  'x-scheme': 'https',
  'user-agent': 'Dart/2.18 (dart:io)',
  'accept-encoding': 'gzip',
  'content-type': 'application/json',
  authtoken: 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjVhNTA5ZjAxOWY3MGQ3NzlkODBmMTUyZDFhNWQzMzgxMWFiN2NlZjciLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoidmluY2Vuem8gY2FsaWEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDYuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy13TVNwMUxZd2hPZy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCSS9OdE43TTlTMEVIUS9zOTYtYy9waG90by5qcGciLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZml4LWl0LWI0YjAwIiwiYXVkIjoiZml4LWl0LWI0YjAwIiwiYXV0aF90aW1lIjoxNjcxODc0Nzg2LCJ1c2VyX2lkIjoiWnhtejJHSmxNUlBXdjBMRGgyRDg4Y0o3T3V6MSIsInN1YiI6Ilp4bXoyR0psTVJQV3YwTERoMkQ4OGNKN091ejEiLCJpYXQiOjE2NzU3NjkyNzksImV4cCI6MTY3NTc3Mjg3OSwiZW1haWwiOiJ2aW5jZW56by5jYWxpYS4xOTc2QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV9udW1iZXIiOiIrMzkzNjYxNDcxMzEzIiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNjkyMTg2MTcwOTA0NTM5MzU5MiJdLCJwaG9uZSI6WyIrMzkzNjYxNDcxMzEzIl0sImVtYWlsIjpbInZpbmNlbnpvLmNhbGlhLjE5NzZAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.d8emU2BKNBV5oe3YQVHT8M8otFos_RvEmeyutdmYBDhnzyPgMZTAn_l3JikRAbcTNdDOAVoutZgTb5s8d6di3plAoE240OwwZTuSwxVpSaS7fDPt_rjQf9k2RmVsRa-fq1SWIP2ejdEbma_QngLSpXO0-PSPx4wa7mThjv2enP00TpUB9RDsOPK2QKlwOX9i1gc1_7kOPGJwouG3S3W4_kOXIxSoVjAT0P9k2xtHa99W-_gwn-9YqM1UoHrLkEs-ONKpe5SWLIet9r_PvI2l1zqb-1fGBmoeBhyzSijw_cOLJSayEoImtkCOmAA0rhMNYc--Yxuzd8EMyyp1U9dThg'
}

Печать заголовков из Flutter показывает, что ключ api_key установлен правильно.

headers are {Content-Type: application/json, api_key: some api key, AuthToken: eyJhbGciOiJSUzI1NiIsImtpZCI6IjVhNTA5ZjAxOWY3MGQ3NzlkODBmMTUyZDFhNWQzMzgxMWFiN2NlZjciLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoidmluY2Vuem8gY2FsaWEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDYuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy13TVNwMUxZd2hPZy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCSS9OdE43TTlTMEVIUS9zOTYtYy9waG90by5qcGciLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZml4LWl0LWI0YjAwIiwiYXVkIjoiZml4LWl0LWI0YjAwIiwiYXV0aF90aW1lIjoxNjcxODc0Nzg2LCJ1c2VyX2lkIjoiWnhtejJHSmxNUlBXdjBMRGgyRDg4Y0o3T3V6MSIsInN1YiI6Ilp4bXoyR0psTVJQV3YwTERoMkQ4OGNKN091ejEiLCJpYXQiOjE2NzU3NjkyNzksImV4cCI6MTY3NTc3Mjg3OSwiZW1haWwiOiJ2aW5jZW56by5jYWxpYS4xOTc2QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV9udW1iZXIiOiIrMzkzNjYxNDcxMzEzIiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNjkyMTg2MTcwOTA0NTM5MzU5MiJdLCJwaG9uZSI6WyIrMzkzNjYxNDcxMzEzIl0sImVtYWlsIjpbInZpbmNlbnpvLmNhbGlhLjE5NzZAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.d8emU2BKNBV5oe3YQVHT8M8otFos_RvEmeyutdmYBDhnzyPgMZTAn_l3JikRAbcTNdDOAVoutZgTb5s8d6di3plAoE240OwwZTuSwxVpSaS7fDPt_rjQf9k2RmVsRa-fq1SWIP2ejdEbma_QngLSpXO0-PSPx4wa7mThjv2enP00TpUB9RDsOPK2QKlwOX9i1gc1_7kOPGJwouG3S3W4_kOXIxSoVjAT0P9k2xtHa99W-_gwn-9YqM1UoHrLkEs-ONKpe5SWLIet9r_PvI2l1zqb-1fGBmoeBhyzSijw_cOLJSayEoImtkCOmAA0rhMNYc--Yxuzd8EMyyp1U9dThg}

Итак, в качестве теста вместо api_key я использовал apikey, и он извлекается в запросе, но затем проверка === завершается неудачно, поэтому промежуточное ПО отвечает {"status":false,"message":"Invalid Api Key"}, даже если их тип и значение идентичны, как проверено здесь https://developer. mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality

received api_key is hjkdiu-slia7h-si9udd989jw-ols8dh
      and expected API_KEY is hjkdiu-slia7h-si9udd989jw-ols8dh

clientApiKey  string
expected  string
req.headers is:  {
  host: 'fixit1.westeurope.cloudapp.azure.com',
  'x-request-id': '515ad2a00b1a3db24a69e09f6d181036',
  'x-real-ip': '81.56.11.23',
  'x-forwarded-for': '81.56.11.23',
  'x-forwarded-host': 'fixit1.westeurope.cloudapp.azure.com',
  'x-forwarded-port': '443',
  'x-forwarded-proto': 'https',
  'x-forwarded-scheme': 'https',
  'x-scheme': 'https',
  'user-agent': 'Dart/2.18 (dart:io)',
  'accept-encoding': 'gzip',
  'content-type': 'application/json',
  authtoken: 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjVhNTA5ZjAxOWY3MGQ3NzlkODBmMTUyZDFhNWQzMzgxMWFiN2NlZjciLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoidmluY2Vuem8gY2FsaWEiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDYuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy13TVNwMUxZd2hPZy9BQUFBQUFBQUFBSS9BQUFBQUFBQUFCSS9OdE43TTlTMEVIUS9zOTYtYy9waG90by5qcGciLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vZml4LWl0LWI0YjAwIiwiYXVkIjoiZml4LWl0LWI0YjAwIiwiYXV0aF90aW1lIjoxNjc1Nzc1MTg2LCJ1c2VyX2lkIjoiWnhtejJHSmxNUlBXdjBMRGgyRDg4Y0o3T3V6MSIsInN1YiI6Ilp4bXoyR0psTVJQV3YwTERoMkQ4OGNKN091ejEiLCJpYXQiOjE2NzU4NDMxNzgsImV4cCI6MTY3NTg0Njc3OCwiZW1haWwiOiJ2aW5jZW56by5jYWxpYS4xOTc2QGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwaG9uZV9udW1iZXIiOiIrMzkzNjYxNDcxMzEzIiwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJnb29nbGUuY29tIjpbIjExNjkyMTg2MTcwOTA0NTM5MzU5MiJdLCJwaG9uZSI6WyIrMzkzNjYxNDcxMzEzIl0sImVtYWlsIjpbInZpbmNlbnpvLmNhbGlhLjE5NzZAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.lDMgmTJkrm6ZnoyEU1F7Sjoo7Y8or8ZKzIrwBJ9ssQiR8yN5KD2ZhyM6yyR_Arscmyg1ZV_6RsTnFgGsVsmjiMzyX6TOXmYkmRlvvMjjjFsV8rW_W_gIdVld6vSg-JMrOlLcCeBknFDJC50bbNGYBSwQ2_C_MZIKlbFWWrqME988MOiUBlyT86t5Oofc5uVMETrpBf0a-wsFRdyEX-3uj-T3MRHza62PTcpHURoptQdIzYsBSc6WxR6WCINVjx__DbWlWrGt612Mw4iLv1XReiGriQEjTDc9cXbG0ngbiRsn0ojvZ-Jb8Pb6kj7gWRYDRsKFg2nxxFMhVeSDuIeO-w',
  apikey: 'hjkdiu-slia7h-si9udd989jw-ols8dh'
}

В качестве второго теста я изменил оператор === на == в промежуточном программном обеспечении, но он все равно возвращает false и отвечает {"status":false,"message":"Invalid Api Key"}.

Затем я попытался использовать const serverApiKey = JSON.stringify(process.env.API_KEY); и const clientApiKey = JSON.stringify(req.get('apikey')); для сравнения, и они действительно дают разные результаты.

clientApiKey json string is:  "hjkdiu-slia7h-si9udd989jw-ols8dh"
serverApiKey json string is:  "hjkdiu-slia7h-si9udd989jw-ols8dh\n"

env.API_KEY действительно получает значение из секрета k8s, base64, закодированного с помощью команды echo -n hjkdiu-slia7h-si9udd989jw-ols8dh |base64. Чтобы увидеть, было ли это пустым местом в закодированном значении из секрета, я попытался закодировать его с флагом -n и без него, но они приводят к одной и той же закодированной строке json.

Я не уверен, что это может иметь какое-то отношение к образу докера, который я создаю, поэтому вот Dockrfile

FROM node:18.3.0
WORKDIR /usr/app
# where available (npm@5+)
COPY ./ package.json ./
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
# Istall the dependencies

RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
COPY ./ ./


# Add the following lines for create-react-app bug, workaround
ENV CI=true
ENV WDS_SOCKET_PORT=0

CMD ["npm", "start"]
  1. Почему api_key param отсутствует, а apikey param есть в заголовках полученных запросов?
  2. Почему \n в serverApiKey, а не в clientApiKey?
  3. Почему это происходит только на AKS, а не на моей локальной машине?

Большое спасибо, как всегда.

Некоторые реализации HTTP-сервера не принимают символы подчеркивания в именах заголовков HTTP.

robertklep 08.02.2023 14:03

@robertklep Большое спасибо. Действительно кажется, что это была первая проблема, но ее легко решить. Теперь проблема заключается в том, что ключ API, который я отправляю от клиента, не соответствует ключу API сервера, исходя из закодированного в base64 значения в секрете сервера, поэтому промежуточное ПО отвечает сообщением wrong key. Я обновил тело вопроса, чтобы показать свои тесты. Любое предложение по этому поводу? я хожу кругами

Vincenzo 08.02.2023 14:10

Я ничего не знаю об AKS, но я бы, наверное, просто списал это на платформу и использовал process.env.API_KEY.trim() (или .trimEnd())

robertklep 08.02.2023 14:12

да, я тоже не уверен в причине этого, но /n заставил меня задуматься о добавленных пробелах. Я пробовал .trim(), и это было! Большое спасибо. Не могли бы вы превратить это в ответ, чтобы я мог его принять?

Vincenzo 08.02.2023 14:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я ничего не знаю об AKS, но некоторые реализации HTTP-серверов (например, nginx) не принимают символы подчеркивания в именах заголовков HTTP.

Что касается того, почему ключи API сервера содержат завершающую новую строку: это может быть связано с платформой. Я бы просто прагматично отнесся к этому и удалил его перед использованием: process.API_KEY.trim()

Большое спасибо еще раз. Я буду использовать .trim() повсюду. Я принимаю здесь политику нулевого доверия, лол.

Vincenzo 08.02.2023 14:48

Другие вопросы по теме