# Как обойти ограничение окружения в 4 КБ на Vercel

By [klif](https://paragraph.com/@klifentro) · 2022-12-30

---

В Layer3, мы запускаем всю нашу платформу в прекрасно организованном full-stack окружении на Vercel. Весь серверный и клиентский код написан на TypeScript и использует многие модули и типы.

Все шло хорошо, пока однажды…

![](https://storage.googleapis.com/papyrus_images/377c310ebbe70d82ef138af297fef75a7cae767af8d7b28bbd1b90111429783b.png)

У Vercel есть ограничение на переменную окружения в 4 КБ. Это вызвано базовой инфраструктурой AWS Lambda, но, хотя у AWS есть некоторые решения для правильного управления секретами, Vercel в основном говорит, что вам нужно [создать собственное управление секретами](https://vercel.com/support/articles/how-do-i-workaround-vercel-s-4-kb-environment-variables-limit).

В это время, у платформы Vercel, есть так много плюсов, и мы сохраняем так много времени, поскольку нам не нужно настраивать сложную облачную инфраструктуру на AWS.

Поэтому мы решили исправить это.

**Наше решение сфокусировано на двух вещах:**

*   Безопасное управление и развертывание секретов при просмотре и производстве
    
*   Сохраняйте отличный опыт разработчика перенося ключи разработки в локальные окружения
    

Посмотрите пример репозитория здесь: [larskarbo/next-env-encrypt-decrypt](https://github.com/larskarbo/next-env-encrypt-decrypt/blob/main/README.md)

Познакомьтесь с Doppler - менеджером по окружению
-------------------------------------------------

**_Doppler_** - это сервис, который специализируется на управлении переменными окружения. Это звучало идеально для нашего варианта использования, у них даже есть интеграция с Vercel!

![](https://storage.googleapis.com/papyrus_images/5bff9a015f526939a6ce5674120fdcf7e4b71014b33d2334cdc43c92372a89b0.png)

Однако мы быстро осознали, что хотя у Doppler есть интеграция с Vercel, это вовсе не решает проблему 4 КБ. На самом деле, он просто - вроде как _способствует_ этому... Добавляя больше переменных `DOPPLER_`.

Однако, интерфейс Doppler и API удивительные, и мы подумали, что могли бы создать работающее решение с некоторыми взломами.

Извлечение секретов из Doppler вместо Vercel
--------------------------------------------

Как только вы добавите все свои переменные окружения в Doppler вместо Vercel, вы сможете довольно легко обойти ограничение в 4 КБ, извлекая секреты из Doppler вместо Vercel.

![](https://storage.googleapis.com/papyrus_images/212e91502fe376d43c67d807fd589c56816c361353bcb65877be9212a8b65a6a.png)

Единственная переменная окружения, которая вам нужна в Vercel, - это _токен Doppler_. Самый простой способ добавить токены для `development`и `preview` - `production` это установить Vercel CLI и Doppler CLI и сгенерировать три разных ключа из терминала:

    echo -n "$(doppler configs tokens create vercel-gitops --config dev --plain)" | vercel env add DOPPLER_TOKEN development
    echo -n "$(doppler configs tokens create vercel-gitops --config stg --plain)" | vercel env add DOPPLER_TOKEN preview
    echo -n "$(doppler configs tokens create vercel-gitops --config prd --plain)" | vercel env add DOPPLER_TOKEN production
    

Затем мы создадим скрипт `fetchSecrets.ts`, который извлекает эти переменные во время создания и записывает их в `.env`.

    import fs from "fs/promises";
    import secrets from "@larskarbo/gitops-secrets";
    
    async function main() {
      const payload = await secrets.providers.doppler.fetch();
    
      let envFile = "";
    
      Object.entries({
        ...payload,
      }).forEach(([key, value]) => {
        envFile += `${key}=${value}\n`;
      });
    
      envFile += `DOPPLER_TOKEN=${process.env.DOPPLER_TOKEN}\n`;
    
      await fs.writeFile(".env", envFile);
    }
    
    void main();
    

Изменения в package.json:

    "scripts": {
        ...
        "build": "npm run fetch-secrets && nextjs build",
        "fetch-secrets": "ts-node fetchSecrets.ts"
    }
    

Да, это все, что вам нужно.

В разработке, вы просто запустите `npm run fetch-env`. Этот процесс не добавляет много движущихся частей и ощущается очень схоже на рабочий процесс `vercel env pull`.

Делаем шаг вперед с зашифрованными секретами
--------------------------------------------

Теперь, когда мы создаем собственное управление секретами, почему бы не сделать шаг вперед и улучшить безопасность?

Текущая настройка переменной окружения [может представлять угрозу безопасности](https://diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/). Мошеннический npm пакет мог бы [сбросить все свободно доступные](https://youtu.be/jUhiPj6h_L8?t=795) переменные `process.env` и отправить их на отдаленный сервер. И помните, это также могло бы быть зависимостью одной из ваших зависимостей. У большинства npm приложений есть куча зависимостей, когда вы смотрите на дерево зависимостей, поэтому область поверхностного риска может быть больше, чем вы думаете.

**Нашей целью будет создание системы, где:**

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

У многих платформ для этого есть сложные решения такие, как [AWS KMS](https://aws.amazon.com/kms/) и [Docker Secrets](https://docs.docker.com/engine/swarm/secrets/). Идея в том, что эти инструменты хранят секрет в зашифрованной форме и предоставляют его приложению во время выполнения.

Мы решим это простым и индивидуальным способом с некоторыми уникальными соображениями:

*   Нам нужно, чтобы переменные `NEXT_PUBLIC_` были доступны в окружении.
    
*   Мы хотим быть в состоянии переопределять секреты с `.env.local` для наших локальных окружений разработки.
    

![](https://storage.googleapis.com/papyrus_images/2ff54d22ad1c74c3c792e3521b6f3a195ce79b4af797fa6425938bb27edf434b.png)

Основываясь на настройке Doppler, мы добавим в Vercel другую переменную окружения, `SECRETS_KEY`.

     gen_key () { openssl rand -base64 32 }
    
    gen_key | vercel env add SECRETS_KEY development
    gen_key | vercel env add SECRETS_KEY preview
    gen_key | vercel env add SECRETS_KEY production
    

Теперь мы сделаем некоторые изменения в наш скрипт `fetch-secrets.ts`.

**Это нужно, чтобы:**

1.  Извлечь секреты из Doppler
    
2.  Записать все переменные `NEXT_PUBLIC_` в `.env`
    
3.  Записать все остальные секреты в специальный файл `.encrypted-secrets`
    

Зафиксируйте этот файл в **git** вот так, а _затем_ добавьте его в `.gitignore`. Это позволяет нам запускать приложение независимо от сгенерированного файла.

Наши супер-заряженные `fetch-secrets.ts` выглядят так:

    import Cryptr from "cryptr";
    import fs from "fs/promises";
    
    import gitopsSecrets from "@larskarbo/gitops-secrets";
    import { ENCRYPTED_SECRETS_FILE } from "../src/utils";
    
    async function main() {
      const payload = await gitopsSecrets.providers.doppler.fetch();
    
      if (!process.env.SECRETS_KEY) {
        throw new Error("SECRETS_KEY is not set");
      }
    
      const cryptr = new Cryptr(process.env.SECRETS_KEY);
    
      const encryptedText = cryptr.encrypt(JSON.stringify(payload));
    
      await fs.writeFile(ENCRYPTED_SECRETS_FILE, encryptedText);
    
      let envFile = "";
    
      Object.entries({
        ...payload,
      })
        .filter(([key]) => key.startsWith("NEXT_PUBLIC_"))
        .forEach(([key, value]) => {
          envFile += `${key}=${value}\n`;
        });
    
      envFile += `DOPPLER_TOKEN=${process.env.DOPPLER_TOKEN}\n`;
      envFile += `SECRETS_KEY=${process.env.SECRETS_KEY}\n`;
    
      await fs.writeFile(".env", envFile);
    }
    
    void main();
    

Затем нам нужно расшифровать секреты в коде времени выполнения. Мы создадим вспомогательную функцию для этого.

    let decryptedSecrets: null | {
      [key: string]: string;
    } = null;
    
    import { readFileSync } from "fs";
    
    import Cryptr from "cryptr";
    import path from "path";
    
    export const ENCRYPTED_SECRETS_FILE = ".encrypted-secrets";
    
    export const getSecret = (key: string) => {
      // in case you have some overrides in `.env.local`
      if (process.env.NODE_ENV === "development" && process.env[key]) {
        return process.env[key];
      }
    
      // only decrypt secrets the first time
      if (!decryptedSecrets) {
        if (!process.env.SECRETS_KEY) {
          return undefined;
        }
    
        const encryptedSecrets = readFileSync(
          path.join(process.cwd(), ENCRYPTED_SECRETS_FILE),
          "utf8"
        );
        const cryptr = new Cryptr(process.env.SECRETS_KEY);
        decryptedSecrets = JSON.parse(cryptr.decrypt(encryptedSecrets));
      }
    
      return decryptedSecrets?.[key];
    };
    

Вуаля! Теперь вы можете использовать секреты везде в вашем приложении следующим образом:

    // back-end
    const apiKey = getSecret("API_KEY")
    
    // front-end
    const somePublicKey = process.env.NEXT_PUBLIC_KEY
    

Проверьте рабочую демонстрацию здесь: ([ссылка](https://next-env-encrypt-decrypt.vercel.app/), репозиторий в [github](https://github.com/larskarbo/next-env-encrypt-decrypt)).

Заключение
----------

У Vercel может быть ограничение окружения в 4 КБ, но с некоторой творческой инженерией, вы можете столкнуться с системой, которая будет более удобной для разработчиков и безопасной, чем раньше.

Такой подход может быть правильным, если вы являетесь стартапом на ранней стадии. Когда вы станете больше и у вас будут строже требования к управлению конфиденциальными данными, вы, вероятно, столкнетесь с более сложной облачной инфраструктурой.

В Layer3, мы используем Vercel и Doppler для быстрого перемещения. Если вам понравился этот пост и вам понравилась идея создания новых типов приложений, которые используют преимущества децентрализованной сети, вам следует  [присоединиться к нашей команде](https://www.notion.so/Open-Roles-c7edc26d9c314721a058b2ffc7b711b9)!

---

*Originally published on [klif](https://paragraph.com/@klifentro/4-vercel)*
