node-argon2 icon indicating copy to clipboard operation
node-argon2 copied to clipboard

[0.28.1] Node16 on Alpine suffers SIGSEGV error when using argon2 verify

Open Tallyrald opened this issue 3 years ago • 5 comments

Steps to reproduce

  1. Call the argon2.verify function on Node v16.X.X on alpine linux using argon2 v0.28.1

Click here for reproduction repository

Expected behaviour

Verify function returns either true or false.

Actual behaviour

Node exits with SIGSEGV signal.

Environment

Operating system: Alpine linux (official node docker container)

Node version: 16.X.X

I'm using Typescript v4.3.2

Issue is specific to Node16 & argon2 v0.28.1. Node14 works fine as well as argon2 v0.27.2 (even on Node16). I'm not sure whether the verify function is the actual or only culprit, but I've seen in my own application that Node definitely crashes while using this function.

Tallyrald avatar Jun 07 '21 15:06 Tallyrald

Hello, any update on this ? Got the same problem today on node 16 running on alpine linux with the 0.28.3 package version.

LoicMatters avatar Jan 26 '22 17:01 LoicMatters

Can confirm the issue. Downgrading to v0.27.2 solves it temporarily.

Janl1 avatar Feb 09 '22 22:02 Janl1

I can reproduce it, but I could not isolate the root cause yet

Edit: just found it. Seems to be related to Blake2 secure memory wipe from Argon2. Will try to work around that.

Backtrace for anyone interested:

#0  0x00000000000020d6 in ?? ()
#1  0x00007f49b017c99a in secure_wipe_memory () from /home/node/node_modules/argon2/lib/binding/napi-v3/argon2.node
#2  0x00007f49b017e2fd in blake2b_final () from /home/node/node_modules/argon2/lib/binding/napi-v3/argon2.node
#3  0x00007f49b017d458 in initialize () from /home/node/node_modules/argon2/lib/binding/napi-v3/argon2.node
#4  0x00007f49b017c656 in argon2_ctx () from /home/node/node_modules/argon2/lib/binding/napi-v3/argon2.node
#5  0x00007f49b017bfd7 in Napi::AsyncWorker::OnAsyncWorkExecute(napi_env__*, void*) () from /home/node/node_modules/argon2/lib/binding/napi-v3/argon2.node
#6  0x0000558df991f29d in worker (arg=0x0) at ../deps/uv/src/threadpool.c:122
#7  0x00007f49b3059160 in ?? () from /lib/ld-musl-x86_64.so.1
#8  0x0000000000000000 in ?? ()

ranisalt avatar Feb 10 '22 17:02 ranisalt

This seems to be a problem that occurs when the version of Node.js is different between build and runtime.

Alpine Linux is built with Node 14, so the error occurs with Node 16. https://github.com/ranisalt/node-argon2/blob/master/.github/workflows/release.yml#L71

The solution is to run the build in source with the npm_config_build_from_source flag set to true.

ARG NODE_VERSION=16.2.0

###
# 1. Dependencies
###

# Build with alpine linux
FROM node:${NODE_VERSION}-alpine as dependencies

WORKDIR /home/node/

ENV NODE_ENV development

# Add `build from source` flag
ENV npm_config_build_from_source true

# Install the tools needed for the build
RUN apk add make g++ python3 git
RUN npm i -g node-pre-gyp

COPY tsconfig.json package.json *package-lock.json ./
RUN npm ci

COPY ts ./ts
# "prod-build" on production
RUN npm run setup && \
    npm prune --production

###
# 2. Application
###

FROM node:${NODE_VERSION}-alpine
WORKDIR /home/node/

COPY --from=dependencies /home/node/node_modules node_modules
COPY --from=dependencies /home/node/built built

COPY package.json ./

ENV PATH="$PATH:/home/node/node_modules/.bin"

RUN chown -R node:node /home/node
USER node

ENV NODE_ENV production

# "start" on production
CMD ["npm", "run", "start"]

cateiru avatar Jul 20 '22 04:07 cateiru

This seems to be a problem that occurs when the version of Node.js is different between build and runtime.

Interestingly, this seems to be only happening with Alpine. I can understand why it happens, but I was expecting node-addon-api to get around those differences in versions. In this case, it may be related to musl in a corner case that NAA didn't expect.

ranisalt avatar Jul 20 '22 16:07 ranisalt

Getting same error...tried everything I could think of...alpine image fails...node image fails...version 0.27.2 fails...anyone knows why it fails?

Error: /home/node/api/node_modules/argon2/lib/binding/napi-v3/argon2.node: invalid ELF header
    at Object.Module._extensions..node (node:internal/modules/cjs/loader:1239:18)
    at Module.load (node:internal/modules/cjs/loader:1033:32)
    at Function.Module._load (node:internal/modules/cjs/loader:868:12)
    at Module.require (node:internal/modules/cjs/loader:1057:19)
    at require (node:internal/modules/cjs/helpers:103:18)
    at Object.<anonymous> (/home/node/api/node_modules/argon2/argon2.js:6:25)
    at Module._compile (node:internal/modules/cjs/loader:1155:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10)
    at Module.load (node:internal/modules/cjs/loader:1033:32)
    at Function.Module._load (node:internal/modules/cjs/loader:868:12)
    at Module.require (node:internal/modules/cjs/loader:1057:19)
    at require (node:internal/modules/cjs/helpers:103:18)
    at Object.<anonymous> (/home/node/api/src/app.module.ts:8:1)
    at Module._compile (node:internal/modules/cjs/loader:1155:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1209:10)
    at Module.load (node:internal/modules/cjs/loader:1033:32)
ARG NODE_VERSION=16

###########
# INSTALL #
###########

FROM node:${NODE_VERSION} As install

# 👉 Security: do not use the `root` user.
ENV USER=node

# You can not use `${USER}` here, but reference `/home/node`.
ENV PATH="/home/node/.npm-global/bin:${PATH}"
# 👉 The `--global` install dir
ENV NPM_CONFIG_PREFIX="/home/node/.npm-global"

# All subsequent commands are run as the `node` user.
USER "${USER}"

# Pre-create the target dir for global install.
RUN mkdir -p "${NPM_CONFIG_PREFIX}/lib"

WORKDIR /home/node/api

COPY ./tsconfig*.json ./package*.json ./

# RUN chown -R node:node /home/node

# Install python/pip
# ENV PYTHONUNBUFFERED=1
# RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python
# RUN python3 -m ensurepip
# RUN pip3 install --no-cache --upgrade pip setuptools

# 👉 Configure NPM, so pkg get installed with correct credentials.
# Avoids `chmod u+x $DIR` and other workarounds.
RUN npm --global --quiet --no-progress install \
    && npm cache clean --force

#########
# BUILD #
#########

FROM node:${NODE_VERSION} As build

WORKDIR /home/node/api

COPY ./tsconfig*.json ./package*.json ./

COPY --from=install /home/node/api/node_modules ./node_modules

COPY . .

ENV NODE_ENV=production

RUN npm ci --only=production && npm cache clean --force

RUN npm run setup && npm prune --production

RUN npm run build

##############
# PRODUCTION #
##############

FROM node:${NODE_VERSION} As production

WORKDIR /home/node

COPY --from=build /home/node/api/node_modules ./node_modules
COPY --from=build /home/node/api/dist ./dist

ENV PATH="$PATH:/home/node/api/node_modules/.bin"

RUN chown -R node:node /home/node/api
USER node

ENV NODE_ENV production

CMD [ "node", "dist/src/main.js" ]
version: '3.8'

services:
  db:
    # image: mongo # container image to be used
    # restart: always 
    # ports: # expose ports in “host:container” format
    #   - 27017:27017
    # environment: #env variables to pass into the container
    #    MONGODB_DATABASE: zombies
    image:  postgres
    restart: always 
    ports:
      - "${DATABASE_PORT}:5432"
    environment:
      POSTGRES_DB: ${DATABASE_NAME}
      POSTGRES_USER: ${DATABASE_USERNAME}
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
    volumes:
      - ./pgdata:/var/lib/postgresql/data
  dev:
    container_name: zombies_dev
    image: zombies-dev:1.0.0
    build:
      context: .
      target: install
      dockerfile: ./Dockerfile
    working_dir: /home/node/api
    command: npm run start:dev #:debug
    ports:
      - 3000:3000
      # - 9229:9229
    networks:
      - zombies-network
    volumes:
      - .:/home/node/api
      # - /home/node/api/node_modules
    restart: unless-stopped
  prod:
    container_name: zombies_prod
    image: zombies-prod:1.0.0
    build:
      context: .
      target: production
      dockerfile: ./Dockerfile
    working_dir: /home/node/api
    command: npm run start:prod
    ports:
      - 3000:3000
      - 9229:9229
    networks:
      - zombies-network
    volumes:
      - .:/home/node
      - /home/node/api/node_modules
    restart: unless-stopped

networks:
  zombies-network:

project url https://github.com/testtestingtester/zombies-angular-nestjs

9x3l6 avatar Dec 10 '22 15:12 9x3l6

@testtestingtester the project you sent does not seem to use this library. Additionally, you are not using Alpine, you are using node:16 which uses Debian Buster. There have been reports in the past of people building in an arch and running in another, so check that you are not mixing Debian with Alpine later on.

With that said, can you check the latest version (0.30.2)?

ranisalt avatar Dec 10 '22 18:12 ranisalt

node:16 in the code I pasted but I tried with alpine different versions also, you want me to copy paste those configs? i think not...I'll try 0.30.2 but I'm thinking it's hopeless and probably won't work like the rest I tried...works on mac though

9x3l6 avatar Dec 11 '22 21:12 9x3l6

same error even with 0.30.2, i tried downgrading actually because I saw that was the fix others were saying...the project doesn't have the configs in the repo yet but if you try to add them and run docker-compose up -d you'll see it fails with the same error in docker, works on mac without docker just fine

9x3l6 avatar Dec 11 '22 21:12 9x3l6

using bcryptjs instead fixes my issue

9x3l6 avatar Dec 12 '22 20:12 9x3l6

Possibly getting the same error here:

    /home/runner/work/code-server/code-server/node_modules/argon2/lib/binding/napi-v3/argon2.node: invalid ELF header

      at Runtime._loadModule (test/node_modules/jest-runtime/build/index.js:1180:29)
      at Object.<anonymous> (node_modules/argon2/argon2.js:6:25)

Using ubuntu-20.04 in CI with GitHub Actions and 0.30.2

jsjoeio avatar Dec 19 '22 21:12 jsjoeio

Hi,

Do you know if we have any updates here? I have the same problem with the latest version of argon2 @ 0.30.3 with alpine.3.17

sknight80 avatar Jan 06 '23 17:01 sknight80

I just saw this for the first time I can ever recall using 0.30.3, but we also just upgraded Electron so it could be that? But I wouldn't expect this error from upgrading Electron.

Regrettably my scrollback is too small and the error is already gone so I can't see if it was identical to @jsjoeio.

We're running Ubuntu 22.10.

Nantris avatar Mar 29 '23 01:03 Nantris

Just hit it again. We're downgrading to 0.29.x.

If I don't report the issue anymore, it's probably safe to assume the issue is 0.30.x and not the Electron upgrade - although I'll try to loop back more explicitly. @ranisalt

Nantris avatar Mar 29 '23 23:03 Nantris

@Slapbox I believe you have a different problem than the one in this issue. This one is caused by/within musl so unless it's happening in Alpine or Void it's not the same thing :P

ranisalt avatar Mar 30 '23 12:03 ranisalt

It's been some time since I reported this issue and unfortunately it's still a valid issue. Compared to 2021, we're currently using:

The only reason I'm bumping this issue is because of the security update introduced through 0.31.0 which we cannot update to without changing to another underlying linux distro or replacing argon2 entirely (which I'd really want to avoid if possible).

Is there anything I can do to help resolve this issue in the near future?

Tallyrald avatar Sep 01 '23 12:09 Tallyrald

@Tallyrald it would be good if you can get the Alpine developers' attention on this issue, it's super hard to debug and when I traced the exception, it happens inside musl, so it's not something we can change from node-argon2

A bug report on their tracker should be enough to get the ball rolling.

ranisalt avatar Sep 01 '23 14:09 ranisalt

this happens because the argon2.node plugin is precompiled. you cannot expect to run it on a non-glibc system (where it was originally compiled) and work. that it happened to sometimes work is just a fluke.

there is nothing alpine (or musl) can do to fix this. it works if you rebuild it:

> [email protected] start
> node ./built/index.js

[...]
npm ERR! signal SIGSEGV
[...]

$ npx @mapbox/node-pre-gyp rebuild -C node_modules/argon2
$ npm run start

> [email protected] start
> node ./built/index.js

$argon2id$v=19$m=65536,t=8,p=2$4ZxaYfo3wi4uQQcG5Iu+Jg$E/XmpSxdshGcocvDa/DHdcSpHiqvH7o4ckjY1GTUj2+qmPAxjJVIrsh/rWjEB2WAdn8
true

nekopsykose avatar Sep 04 '23 22:09 nekopsykose

there is nothing alpine (or musl) can do to fix this.

the reason it sometimes happens to work is because musl has some degree of compatibility with glibc (ABI-wise), but it is neither complete nor guaranteed to work (so the older versions just so happened to only use the C api in such a way as to be compatible with either). and when it doesn't match up, having a different ABI than what you compiled against just crashes. there is nothing to do about that (in either alpine, musl, or this repository; though one could argue npm should better handle precompiled binary files and have easy ways to forbid ones that wouldn't work, like how python wheels only install on matching platforms and require building otherwise).

nekopsykose avatar Sep 04 '23 22:09 nekopsykose

this happens because the argon2.node plugin is precompiled

The precompiled binary will be linked against musl if you are running Alpine.

The problem with the Dockerfile provided in this issue is that it installs dependencies under Debian, but then copies to Alpine and does not perform the install step again to refetch the precompiled binary.

ranisalt avatar Sep 04 '23 23:09 ranisalt

The precompiled binary will be linked against musl if you are running Alpine.

in that case it would work fine, indeed this specific way of installing it would be broken :)

nekopsykose avatar Sep 04 '23 23:09 nekopsykose

yeah, can confirm changing FROM node:${NODE_VERSION} to FROM node:${NODE_VERSION}-alpine alone works as expected

nekopsykose avatar Sep 04 '23 23:09 nekopsykose

Thank you @nekopsykose and @ranisalt for your help. For my specific needs the final solution is to run a 2-step pre-build process before copying everything over to the final image.

  • The first step builds most dependencies of the project that need the Debian-based Node image.
  • The second step installs argon2 on an Alpine Node image.
  • The final step is to copy over all dependencies into the target (Alpine-based) image that is used to run the finalized and prepared application.

For me, the problem is resolved now. If this is satisfactory for others too, please close this issue. Thank you again for your help.

Just for reference, I asked for help in the Alpine issues board HERE which helped the issue move forward and be solved.

Tallyrald avatar Sep 05 '23 13:09 Tallyrald

@Tallyrald why do you need to run it on Debian? Doesn't Alpine provide the tools you need to build this project?

ranisalt avatar Sep 05 '23 13:09 ranisalt

We have at least one 3rd party library that doesn't support building on Alpine. Heck it didn't even use to run on Alpine, but that got solved some years ago. I wish I knew why this is, but I'm in no position to make a change in regards to this. Best I can do is to trigger a re-verification if we still need to be so specific with this library. If people are able to run the build(install)-phase on Alpine, that is the most straightforward solution.

Tallyrald avatar Sep 05 '23 14:09 Tallyrald

Alright, since the original problem has been solved, I'm closing the issue

ranisalt avatar Sep 05 '23 14:09 ranisalt