Heroku 上的 NPM 私有 git 模块

Posted

技术标签:

【中文标题】Heroku 上的 NPM 私有 git 模块【英文标题】:NPM private git module on Heroku 【发布时间】:2012-06-07 20:19:27 【问题描述】:

我正在尝试将我的应用程序部署到 Heroku,但是我依赖于使用一些私有 git 存储库作为模块。我这样做是为了项目之间的代码重用,例如我有一个在多个应用程序中使用的自定义记录器。

"logger":"git+ssh://git@bitbucket.org..............#master"

问题是 Heroku 显然没有 ssh 访问此代码的权限。我在这个问题上找不到任何东西。理想情况下,Heroku 有一个公钥,我可以将其添加到模块中。

【问题讨论】:

模块应该安装在node_modules目录下吗?您可以将应用程序归档,然后在将其发送到 heroku 后将其安装到 heroku 上? 我不完全理解,但我想你是说我可以将代码存储在 node_modules 文件夹和主 repo 中,这可以工作,但它有点 hack。 当您在本地 PC 上执行 npm install 时,这是自 npm 1.0 以来的标准行为? 交叉线。我希望不要在英雄部署之间添加任何其他过程,开始破坏目的。 我也想知道答案。您可以将您的 Github/Bitbucket SSH 密钥绑定到您的 Heroku 帐户:heroku keys:add ~/.ssh/id_rsa.pub。理论上这应该可以解决问题,但git push heroku master 仍然会导致“主机密钥验证失败。”。你在六月问过这个问题,从那以后你找到答案了吗? 【参考方案1】:

基本认证

GitHub 支持基本认证:

"dependencies" : 
    "my-module" : "git+https://my_username:my_password@github.com/my_github_account/my_repo.git"

BitBucket 也是如此:

"dependencies" : 
    "my-module": "git+https://my_username:my_password@bitbucket.org/my_bitbucket_account/my_repo.git"

但可能不希望在您的 package.json 中使用纯密码。

个人访问令牌 (GitHub)

为了使这个答案更加最新,我现在建议在 GitHub 上使用using a personal access token,而不是用户名/密码组合。

你现在应该使用:

"dependencies" : 
    "my-module" : "git+https://<username>:<token>@github.com/my_github_account/my_repo.git"

对于 Github,您可以在此处生成新令牌:

https://github.com/settings/tokens

应用密码(Bitbucket)

应用密码主要用于提供与不支持双重身份验证的应用的兼容性,您也可以将其用于此目的。首先,create an app password,然后像这样指定你的依赖:

"dependencies" : 
    "my-module": "git+https://<username>:<app-password>@bitbucket.org/my_bitbucket_account/my_repo.git"

[已弃用] 团队 API 密钥 (Bitbucket)

对于 BitBucket,您可以在管理团队页面上生成一个 API 密钥,然后使用此 URL:

"dependencies" : 
    "my-module" : "git+https://<teamname>:<api-key>@bitbucket.org/team_name/repo_name.git"

【讨论】:

这会导致 package.json 解析错误,因为格式规范是 module_name: location_or_semver,而不仅仅是 location_or_semver:“使用 npm npm ERR 安装依赖项!无法读取依赖项。npm ERR!解析 json npm ERR 失败!意外的令牌 " 知道了:"dependencies" : "my-module" : "git+my_username:my_password@bitbucket.org/my_bitbucket_account/…" (注意 SO 正在解析这个 url,参见 gist.github.com/4073148) 使用授权 API,您可以通过向自己颁发 OAuth 令牌并使用它而不是您的帐户的用户名和密码来更安全地执行此操作:help.github.com/articles/git-over-https-using-oauth-token 只是对此的快速更新,我试图使用为 bitbucket 提到的 API 方法,但显示的语法不正确。你需要做git+https://&lt;team-name&gt;:&lt;api-key&gt;@bitbucket.org/&lt;team-name&gt;/&lt;repo_name&gt;.git 我已更新答案以引用较新的 Bitbucket 技术,在此之前的 cmets 引用的是此答案的旧版本。【参考方案2】:

2016 年 3 月 26 日更新

如果您使用的是 npm3,则所描述的方法将不再有效,因为 npm3 在运行 preinstall 脚本之前会获取 package.json 中描述的所有模块。这个has been confirmed as a bug。

官方的node.js Heroku buildpack 现在包含heroku-prebuildheroku-postbuild,它们将分别在npm install 之前和之后运行。在所有情况下,您都应该使用这些脚本而不是 preinstallpostinstall,以同时支持 npm2 和 npm3。

换句话说,您的package.json 应该类似于:

 "scripts": 
      "heroku-prebuild": "bash preinstall.sh",
      "heroku-postbuild": "bash postinstall.sh"
    

我想出了 Michael 的答案的替代方案,保留了 (IMO) 有利的要求,即使您的凭据不受源代码控制,同时不需要自定义 buildpack。这是因为buildpack linked by Michael 已经过时了。

解决方案是在 npm 的 preinstallpostinstall 脚本中设置和拆除 SSH 环境,而不是在 buildpack 中。

按照以下说明进行操作:

在您的存储库中创建两个脚本,我们称它们为 preinstall.shpostinstall.sh。 使它们可执行 (chmod +x *.sh)。 将以下内容添加到preinstall.sh
    #!/bin/bash
    # Generates an SSH config file for connections if a config var exists.

    if [ "$GIT_SSH_KEY" != "" ]; then
      echo "Detected SSH key for git. Adding SSH config" >&1
      echo "" >&1

      # Ensure we have an ssh folder
      if [ ! -d ~/.ssh ]; then
        mkdir -p ~/.ssh
        chmod 700 ~/.ssh
      fi

      # Load the private key into a file.
      echo $GIT_SSH_KEY | base64 --decode > ~/.ssh/deploy_key

      # Change the permissions on the file to
      # be read-only for this user.
      chmod 400 ~/.ssh/deploy_key

      # Setup the ssh config file.
      echo -e "Host github.com\n"\
              " IdentityFile ~/.ssh/deploy_key\n"\
              " IdentitiesOnly yes\n"\
              " UserKnownHostsFile=/dev/null\n"\
              " StrictHostKeyChecking no"\
              > ~/.ssh/config
    fi
将以下内容添加到postinstall.sh
    #!/bin/bash

    if [ "$GIT_SSH_KEY" != "" ]; then
      echo "Cleaning up SSH config" >&1
      echo "" >&1

      # Now that npm has finished running, we shouldn't need the ssh key/config anymore.
      # Remove the files that we created.
      rm -f ~/.ssh/config
      rm -f ~/.ssh/deploy_key

      # Clear that sensitive key data from the environment
      export GIT_SSH_KEY=0
    fi

将以下内容添加到您的package.json

"scripts": 
  "preinstall": "bash preinstall.sh",
  "postinstall": "bash postinstall.sh"

使用ssh-agent 生成私钥/公钥对。

将公钥添加为 Github 上的部署密钥。 为您的私钥创建一个 base64 编码版本,并将其设置为 Heroku 配置变量 GIT_SSH_KEY。 提交您的应用并将其推送到 Github。

当 Heroku 构建您的应用程序时,在 npm 安装您的依赖项之前,preinstall.sh 脚本会运行。这将根据GIT_SSH_KEY 环境变量的解码内容创建一个私钥文件,并创建一个 SSH 配置文件来告诉 SSH 在连接到github.com 时使用该文件。 (如果您改为连接到 Bitbucket,则将 preinstall.sh 中的 Host 条目更新为 bitbucket.org)。然后 npm 使用这个 SSH 配置安装模块。安装后,私钥被删除,配置被擦除。

这允许 Heroku 通过 SSH 拉下您的私有模块,同时将私钥保留在代码库之外。如果您的私钥被泄露,因为它只是部署密钥的一半,您可以在 GitHub 中撤销公钥并重新生成密钥对。

顺便说一句,由于 GitHub 部署密钥具有读/写权限,如果您在 GitHub 组织中托管模块,则可以改为创建只读团队并为其分配“部署”用户。然后可以使用密钥对的公共部分配置部署用户。这为您的模块增加了一层额外的安全性。

【讨论】:

对我来说rm -f ~/.ssh/config 中的cleanup-ssh.script 是个问题。因为我已经将它用于其他一些设置。所以我不能删除那个文件。很高兴在这里添加一个概念。 是的,这很好。我想你可以复制你原来的 config 文件,然后恢复它,而不是仅仅删除它。 干得好!我从来不喜欢为了做到这一点而不得不维护一个 buildpack 的分支 @roboli 是的,这种技术只能在没有密码的情况下工作,因为它是无人值守的,我建议为这个操作创建一个单独的 SSH 密钥,这样如果以后受到损害,就可以很容易地撤销日期。 @Pier 名称根本不重要,您可以轻松使用GIT_SSH_01GIT_SSH_02 等 - 只需记住相应地修改脚本以使用正确的环境变量。【参考方案3】:

在你的 git repo 中使用纯文本密码是一个非常糟糕的主意,使用访问令牌更好,但你仍然需要非常小心。

"my_module": "git+https://ACCESS_TOKEN:x-oauth-basic@github.com/me/my_module.git"

【讨论】:

这是最好的答案!在此处创建令牌:github.com/settings/applications 我正在尝试使用 bitbucket,当 Heroku 尝试克隆私有仓库时,它会导致“主机密钥验证失败”:/ 另外,不要忘记限定您的令牌范围,使其只有“repo”访问权限 @bpaul 是的,范围界定真的很重要,否则,就像检查您的计划文本密码一样【参考方案4】:

我创建了一个自定义 nodeJS buildpack,它将允许您指定一个 SSH 密钥,该密钥已在 ssh-agent 中注册并在首次设置 dynos 时由 npm 使用。它允许您无缝地将您的模块指定为package.json 中的 ssh url,如下所示:

"private_module": "git+ssh://git@github.com:me/my_module.git"

设置您的应用以使用您的私钥:

生成密钥:ssh-keygen -t rsa -C "your_email@example.com"(不输入密码。buildpack 不支持带密码的密钥) 将公钥添加到 github:pbcopy &lt; ~/.ssh/id_rsa.pub(在 OS X 中)并将结果粘贴到 github 管理员中 将私钥添加到您的 Heroku 应用程序的配置中:cat id_rsa | base64 | pbcopy,然后是 heroku config:set GIT_SSH_KEY=&lt;paste_here&gt; --app your-app-name 设置您的应用程序以使用项目中包含的 heroku nodeJS buildpack 自述文件中所述的 buildpack。总之,最简单的方法是使用 heroku config:set 设置一个特殊的配置值到包含所需构建包的存储库的 github url。我建议分叉我的版本并链接到您自己的 github 分叉,因为我不保证不会更改我的 buildpack。

我的自定义 buildpack 可以在这里找到:https://github.com/thirdiron/heroku-buildpack-nodejs 它适用于我的系统。非常欢迎评论和拉取请求。

【讨论】:

【参考方案5】:

根据@fiznool 的回答,我创建了一个 buildpack 来解决这个问题,它使用存储为环境变量的自定义 ssh 密钥。由于 buildpack 与技术无关,因此可以使用任何工具下载依赖项,例如 php 的 composer、ruby 的 bundler、javascript 的 npm 等:https://github.com/simon0191/custom-ssh-key-buildpack

    将 buildpack 添加到您的应用中:

    $ heroku buildpacks:add --index 1 https://github.com/simon0191/custom-ssh-key-buildpack
    

    生成一个不带密码的新 SSH 密钥(假设您将其命名为 deploy_key)

    将公钥添加到您的私有存储库帐户。例如:

    Github:https://help.github.com/articles/adding-a-new-ssh-key-to-your-github-account/

    比特桶:https://confluence.atlassian.com/bitbucket/add-an-ssh-key-to-an-account-302811853.html

    将私钥编码为base64字符串,并将其添加为heroku应用的CUSTOM_SSH_KEY环境变量。

    制作应使用 ssh 密钥的主机的逗号分隔列表,并将其添加为 heroku 应用程序的 CUSTOM_SSH_KEY_HOSTS 环境变量。

    # MacOS
    $ heroku config:set CUSTOM_SSH_KEY=$(base64 --input ~/.ssh/deploy_key) CUSTOM_SSH_KEY_HOSTS=bitbucket.org,github.com
    # Ubuntu
    $ heroku config:set CUSTOM_SSH_KEY=$(base64 ~/.ssh/deploy_key) CUSTOM_SSH_KEY_HOSTS=bitbucket.org,github.com
    
    部署您的应用并享受 :)

【讨论】:

脚本的输入部分应该是--input ~/.ssh/deploy_key。您已在脚本中解码了 base64 的私钥。请更正。 @gmuraleekrishna 我发现使用 --input 标志取决于您使用的 base64 版本。例如,在 macOS 中需要它,但在 ubuntu 14.04 中无法识别该标志。 @SimonSoriano 我的意思是你应该使用私钥而不是公钥。 @gmuraleekrishna 感谢您指出这一点,您是对的。我更正了回购的答案和自述文件。 @roboli,是的,ssh 密钥中的密码短语通常不用于自动化,因为要解密私钥,您需要手动输入密码短语,在这种情况下它不起作用,因为 buildpack脚本由 Heroku 自动运行,或者您还需要向自动化工具(在本例中为 buildpack)提供密码,因此会破坏密码的目的,即添加额外的安全层。感谢您的反馈,我更新了答案以澄清必须在没有密码的情况下生成 SSH 密钥。【参考方案6】:

我能够通过个人访问令牌在 Heroku 构建中设置 Github 私有存储库的解析。

在此处生成 Github 访问令牌:https://github.com/settings/tokens 将访问令牌设置为 Heroku 配置变量:heroku config:set GITHUB_TOKEN=&lt;paste_here&gt; --app your-app-name 或通过 Heroku 仪表板

添加heroku-prebuild.sh脚本:

#!/bin/bash
if [ "$GITHUB_TOKEN" != "" ]; then
    echo "Detected GITHUB_TOKEN. Setting git config to use the security token" >&1
    git config --global url."https://$GITHUB_TOKEN@github.com/".insteadOf git@github.com:
fi

将预编译脚本添加到package.json:

"scripts": 
    "heroku-prebuild": "bash heroku-prebuild.sh"

对于本地环境,我们也可以使用git config ... 或者我们可以将访问令牌添加到~/.netrc 文件中:

machine github.com
  login PASTE_GITHUB_USERNAME_HERE
  password PASTE_GITHUB_TOKEN_HERE

并且安装私有 github 存储库应该可以工作。

npm install OWNER/REPO --save 将在 package.json 中显示为:"REPO": "github:OWNER/REPO"

在 Heroku 构建中解决私有 repos 也应该可以工作。 您可以选择设置一个构建后脚本来取消设置GITHUB_TOKEN

【讨论】:

【参考方案7】:

这个答案很好https://***.com/a/29677091/6135922,但我改变了一点预安装脚本。希望这会对某人有所帮助。

#!/bin/bash
# Generates an SSH config file for connections if a config var exists.

echo "Preinstall"

if [ "$GIT_SSH_KEY" != "" ]; then
  echo "Detected SSH key for git. Adding SSH config" >&1
  echo "" >&1

  # Ensure we have an ssh folder
  if [ ! -d ~/.ssh ]; then
    mkdir -p ~/.ssh
    chmod 700 ~/.ssh
  fi

  # Load the private key into a file.
  echo $GIT_SSH_KEY | base64 --decode > ~/.ssh/deploy_key

  # Change the permissions on the file to
  # be read-only for this user.
  chmod o-w ~/
  chmod 700 ~/.ssh
  chmod 600 ~/.ssh/deploy_key

  # Setup the ssh config file.
  echo -e "Host bitbucket.org\n"\
          " IdentityFile ~/.ssh/deploy_key\n"\
          " HostName bitbucket.org\n" \
          " IdentitiesOnly yes\n"\
          " UserKnownHostsFile=/dev/null\n"\
          " StrictHostKeyChecking no"\
          > ~/.ssh/config

  echo "eval `ssh-agent -s`"
  eval `ssh-agent -s`

  echo "ssh-add -l"
  ssh-add -l

  echo "ssh-add ~/.ssh/deploy_key"
  ssh-add ~/.ssh/deploy_key

  # uncomment to check that everything works just fine
  # ssh -v git@bitbucket.org
fi

【讨论】:

【参考方案8】:

您可以在 package.json 私有存储库中使用以下身份验证示例:

https://usernamegit:passwordgit@github.com/reponame/web/tarball/branchname

【讨论】:

将凭据存储在您的 repo 和 package.json 中是个坏主意。【参考方案9】:

简而言之,这是不可能的。我想出的这个问题的最佳解决方案是使用新的git subtree's。在撰写本文时,它们不在官方 git 源中,因此需要手动安装,但它们将包含在 v1.7.11 中。目前它可以在 homebrew 和 apt-get 上使用。然后就是这样做的情况

git subtree add -P /node_modules/someprivatemodue git@github.......someprivatemodule master|tag|commit

这会扩大 repo 的大小,但通过使用 gitsubtree pull 执行上面的命令可以轻松更新。

【讨论】:

【参考方案10】:

我以前使用来自 github 的模块完成此操作。 Npm 当前接受包的名称或指向包含该包的 tar.gz 文件的链接。

例如,如果您想直接从 Github 使用 express.js(通过下载部分获取链接),您可以这样做:

"dependencies" : 
  "express"   :  "https://github.com/visionmedia/express/tarball/2.5.9"

因此,您需要找到一种方法,通过 http(s) 以 tar.gz 文件的形式访问您的存储库。

【讨论】:

谢谢,但这并不能解决 repo 对世界私有的问题。 如前所述,无论是 npm 还是 tar.gz,可能都有某种方法可以将您的模块导出为存档。如果没有,总有可能为此使用 git 子模块。 但是因为该模块是私有的,并且一旦 heroku 尝试获取它就需要 ssh 访问,无论代码如何传输,它们都会被拒绝。 使用 cronjob、post commit hook 或其他任何方式将 tag.gz 转储到可以访问的地方。 (基本的 http auth 可以保护它免受其他人的影响。)如前所述,据我所知,没有什么神奇的方法可以解决你在 package.json 中写一些简单的东西的问题。你需要在这里更有创意。 @TheHippo 老兄把它删掉。

以上是关于Heroku 上的 NPM 私有 git 模块的主要内容,如果未能解决你的问题,请参考以下文章

通过GIT安装私有NPM模块,并让它更新?

在 Heroku 上每次部署后避免 npm refresh

导入 git npm 模块导致未定义

使用cnpm搭建私有NPM仓库 发布npm包

您可以使用 Heroku 部署由 json-server NPM 模块运行的假后端吗?

从特定版本的 Git 安装 npm