Why is `Rails.application.key_generator.generate_key` generating different output? - ruby-on-rails

Lately a piece of code broke and I realised that my calls to OpenSSL::HMAC.hexdigest('SHA1', Rails.application.key_generator.generate_key('abcdef123456'), 'something') generated different output on different machine running the function.
I originally though this was due to a change in OpenSSL, but running OpenSSL::HMAC.hexdigest('SHA1', 'abcdef123', 'something') lead to the same result on all machines.
Turns out that Rails.application.key_generator.generate_key('abcdef123456') returns different values on different machines.
Running locally, I obtain a different results that on the server.
Same Ruby version, same Rails version, the only difference is the platform (x86_64-darwin21 vs x86_64-linux).
Shouldn't Rails.application.key_generator.generate_key always return the same results?
Otherwise the code will break if it migrates to another machine.

I have tested this in three different Linux distributions, each using a different version of OpenSSL. I am unable to reproduce the issue that you have described.
You said that there were two commands that you ran:
OpenSSL::HMAC.hexdigest('SHA1', 'abcdef123', 'something') which you said returned the same value on every machine
OpenSSL::HMAC.hexdigest('SHA1', Rails.application.key_generator.generate_key('abcdef123456'), 'something') which you said returned different values on each machine
I believe that the cause of the latter returning different values is that each machine is using a different secret_key_base value. The only unique element of generate_key is #secret which is the value of Rails.application.secret_key_base.
I recommend that you double-check:
That every machine is running in the same environment (RAILS_ENV)
That every machine is using the same secret_key_base value for that environment
The only way that I've been able to get different values for each is to change the secret_key_env value. This value is set automatically in test and development environments and can be set in a variety of ways for other environments. You can check the value in the Rails console with Rails.application.secret_key_base.
Below you can find the steps to validate that different versions of OpenSSL have no impact on the value generated.
Alpine 3.17.0 and OpenSSL 3.0.7
docker run -it alpine:3.17.0 sh
apk add build-base libc6-compat openssl openssl-dev readline readline-dev tzdata zlib zlib-dev
wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.3.tar.gz && tar xvf ruby-3.1.3.tar.gz && cd ruby-3.1.3 && ./configure --with-openssl-dir=/usr && make && make install
openssl version
# OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION; puts OpenSSL::OPENSSL_VERSION_NUMBER'
# OpenSSL 3.0.7 1 Nov 2022
# 805306480
cd /
gem install rails
rails new foo --skip-git --skip-keeps --skip-mailer --skip-mailbox --skip-text --skip-active-record --skip-active-job --skip-active-storage --skip-action-cable --skip-asset-pipeline --skip-javascript --skip-hotwire --skip-jbuilder --skip-test --skip-system-test --api --minimal
cd foo
export RAILS_ENV=production
export SECRET_KEY_BASE="eedfd5f234fbca535134ae551981d319df449c096c9bcd041b01ef77b7810eb4e480a7671c2b8788adb8782f950e9684ff582c5135d76ff088795d284f5917ff"
rails console
OpenSSL::HMAC.hexdigest('SHA1', Rails.application.key_generator.generate_key('abcdef123456'), 'something')
=> "f1ba53b65a27d443ae8bc60fcc248230c651032f"
Alpine 3.16.3 and OpenSSL 1.1.1s
docker run -it alpine:3.16.3 sh
apk add build-base libc6-compat openssl openssl-dev readline readline-dev tzdata zlib zlib-dev
wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.3.tar.gz && tar xvf ruby-3.1.3.tar.gz && cd ruby-3.1.3 && ./configure --with-openssl-dir=/usr && make && make install
openssl version
# OpenSSL 1.1.1s 1 Nov 2022
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION; puts OpenSSL::OPENSSL_VERSION_NUMBER'
# OpenSSL 1.1.1s 1 Nov 2022
# 269488447
cd /
gem install rails
rails new foo --skip-git --skip-keeps --skip-mailer --skip-mailbox --skip-text --skip-active-record --skip-active-job --skip-active-storage --skip-action-cable --skip-asset-pipeline --skip-javascript --skip-hotwire --skip-jbuilder --skip-test --skip-system-test --api --minimal
cd foo
export RAILS_ENV=production
export SECRET_KEY_BASE="eedfd5f234fbca535134ae551981d319df449c096c9bcd041b01ef77b7810eb4e480a7671c2b8788adb8782f950e9684ff582c5135d76ff088795d284f5917ff"
rails console
OpenSSL::HMAC.hexdigest('SHA1', Rails.application.key_generator.generate_key('abcdef123456'), 'something')
=> "f1ba53b65a27d443ae8bc60fcc248230c651032f"
Debian 11.5 and OpenSSL 1.1.1n
docker run -it debian:11.5 sh
apt update && apt install -y build-essential libreadline-dev libreadline8 openssl libssl-dev tzdata wget zlib1g zlib1g-dev
wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.3.tar.gz && tar xvf ruby-3.1.3.tar.gz && cd ruby-3.1.3 && ./configure --with-openssl-dir=/usr && make && make install
openssl version
# OpenSSL 1.1.1n 15 Mar 2022
ruby -ropenssl -e 'puts OpenSSL::OPENSSL_VERSION; puts OpenSSL::OPENSSL_VERSION_NUMBER'
# OpenSSL 1.1.1n 15 Mar 2022
# 269488367
cd /
gem install rails
rails new foo --skip-git --skip-keeps --skip-mailer --skip-mailbox --skip-text --skip-active-record --skip-active-job --skip-active-storage --skip-action-cable --skip-asset-pipeline --skip-javascript --skip-hotwire --skip-jbuilder --skip-test --skip-system-test --api --minimal
cd foo
export RAILS_ENV=production
export SECRET_KEY_BASE="eedfd5f234fbca535134ae551981d319df449c096c9bcd041b01ef77b7810eb4e480a7671c2b8788adb8782f950e9684ff582c5135d76ff088795d284f5917ff"
rails console
OpenSSL::HMAC.hexdigest('SHA1', Rails.application.key_generator.generate_key('abcdef123456'), 'something')
=> "f1ba53b65a27d443ae8bc60fcc248230c651032f"

Related

Ruby on Rails daylight-saving detection does not work in recent `ruby:2.7.1-alpine` container

Ruby on rails with recent ruby:2.7.1-alpine container, breaks timezone daylight-saving support:
irb(main):004:0> Time.zone.to_s
=> "(GMT+01:00) Europe/Berlin"
irb(main):005:0> Time.zone.parse("2020-08-27").zone
=> "CEST"
irb(main):006:0> Time.zone.parse("2020-12-27").zone
=> "CEST"
2020-12-27 should actually be CET because of daylight saving. On local ruby installation and previous container builds it used to work and would returns:
irb(main):006:0> Time.zone.to_s
=> "(GMT+01:00) Europe/Berlin"
irb(main):007:0> Time.zone.parse("2020-08-27").zone
=> "CEST"
irb(main):008:0> Time.zone.parse("2020-12-27").zone
=> "CET"
The Dockerfile looks as follows:
FROM ruby:2.7.1-alpine
# Add basic packages
RUN apk add --update --no-cache \
build-base \
python2 \
postgresql-dev \
git \
nodejs \
yarn \
tzdata \
file \
# Image processing resources
gobject-introspection-dev \
poppler-dev \
# Test Resources
chromium-chromedriver \
chromium \
harfbuzz \
nss
WORKDIR /app
# Install standard gems
COPY Gemfile* /app/
RUN gem install bundler && \
bundle config --global frozen 1 && \
bundle config --local build.sassc --disable-march-tune-native && \
bundle install -j4 --retry 3
# Install standard Node modules
COPY package.json yarn.lock /app/
RUN yarn install
ONBUILD COPY . ./
Does anybody knows more about the internals?
Is this a rails or ruby or alpine package problem.
Any help would be great!
UPDATE
I could reproduce the behavior with a clean docker like:
docker run -it ruby:2.7.1-alpine /bin/sh
0 apk add --update --no-cache build-base sqlite-dev nano nodejs yarn tzdata
1 gem install rails
2 rails new timzone_test
3 rails webpacker:install
4 nano config/application.rb # add config.time_zone = 'Europe/Berlin'
5 rails console
I have the same issue on this clean install
Loading development environment (Rails 6.0.3.4)
irb(main):001:0> Time.zone.parse("2020-12-27").zone
=> "CEST"
After adding gem 'tzinfo-data' to the Gemfile, everything worked as expected:
Loading development environment (Rails 6.0.3.4)
irb(main):001:0> Time.zone.parse("2020-12-27").zone
=> "CET"

Docker image working on Ubuntu 16.04 but failing on Ubuntu 18.04

I thought docker images worked independent of the OS and did not need any dependencies installed on the OS itself.
I have a docker image built using the dockerfile:
FROM ubuntu:16.04
# Prepare to install Java for 'rjb' gem
RUN apt-get update && apt-get -y install software-properties-common && add-apt-repository ppa:webupd8team/java -y && apt-get update
# Install Java 8 and accept the license by default (https://www.digitalocean.com/community/tutorials/how-to-install-java-with-apt-get-on-ubuntu-16-04)
RUN echo debconf shared/accepted-oracle-license-v1-1 select true | debconf-set-selections && echo debconf shared/accepted-oracle-license-v1-1 seen true | debconf-set-selections && apt-get -y install oracle-java8-installer && java -version
# Set JAVA_HOME (should add in the docker startup script)
RUN echo 'export JAVA_HOME="/usr/lib/jvm/java-8-oracle"' >> /etc/environment && . /etc/environment && echo $JAVA_HOME
ENV JAVA_HOME /usr/lib/jvm/java-8-oracle
# Install curl, rvm, ruby 2.1.5p273, rails 4.2.6
RUN apt-get -y update && apt-get -y install build-essential zlib1g-dev libssl-dev libreadline6-dev libyaml-dev && apt-get -y install wget && apt-get install -y ruby-full && ruby -v && gem install rails -v 4.2.6 && rails -v
# Install bundler, git
RUN gem install bundler && apt-get -y install git
ENV DEBIAN_FRONTEND noninteractive
# Make sure 'bundle install' run successfully and set the git pre-commit hooks
RUN ["/bin/bash", "-c", "cd home && mkdir expertiza_developer && cd expertiza_developer && git clone https://github.com/expertiza/expertiza.git && cd expertiza && apt-get -y install ruby-dev && apt-get -y install make && apt-get install -y gcc make && apt-get install -y libmysqlclient-dev && apt-get install -y libpq-dev && bundle install && debconf-set-selections <<< 'mysql-server mysql-server/root_password password ' && debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password ' && apt-get -y install mysql-server && /etc/init.d/mysql start && cp config/database.yml.example config/database.yml && cp config/secrets.yml.example config/secrets.yml && mv ./hooks/pre-commit ./.git/hooks/pre-commit && chmod 755 ./.git/hooks/pre-commit"]
and using the following docker-compose file to run the image:
version: '3'
services:
expertiza:
image: winbobob/expertiza:test
ports:
- '3000:3000'
volumes:
- '.:/root/expertiza'
depends_on:
- scrubbed_db
- redis
links:
- scrubbed_db
- redis
working_dir: /root/expertiza
command: bundle exec thin start -p 3000
environment:
REDIS_HOST: redis
scrubbed_db:
image: mysql:5.7
volumes:
# https://stackoverflow.com/questions/25920029/setting-up-mysql-and-importing-dump-within-dockerfile
- './docker/scrubbed_db:/docker-entrypoint-initdb.d'
environment:
MYSQL_ROOT_PASSWORD: expertiza
redis:
image: redis:alpine
This runs perfectly fine on an Ubuntu 16.04 box, but when I run this same image on an Ubuntu 18.04 box, I get the following error:
expertiza_1 | /var/lib/gems/2.3.0/gems/bundler-1.16.4/lib/bundler/spec_set.rb:91:in `block in materialize': Could not find rubyzip-1.2.2 in any of the sources (Bundler::GemNotFound)
Does anyone know why this is happening and how to fix it?
You are not specifying the version of the packages that you want installed with apt-get install. As a result, you're stuck with whatever version is available for those packages for the distribution that you're using.
In this case, you think you're installing Ruby 2.1.5 but on 16.04 you're actually installing Ruby 2.3.0 and on 18.04 you're installing Ruby 2.5.1. Your RUN command includes ruby -v so check your build logs to confirm that you're not getting the version you expect.
Also, make sure gem install rubyzip -v 1.2.2 works as a RUN command, that rubyzip is defined in your Gemfile and the correct version is defined in Gemfile.lock, and that whatever gem server bundler is pointing to has a copy of the gem at that version.
Next, you're trying to use Rails 4.2.6 but Rails 4.2.6 is not compatible with Ruby 2.5.1. It's not compatible with 2.4 either. The first version of Rails 4 to support Ruby 2.4 was 4.2.8rc1 and the current version of Rails 4 is 4.2.10.
Ensure that you are installing the correct versions of the packages that you need and that your applications and libraries are using compatible versions. Additionally, consider upgrading your version of Ruby as support of Ruby 2.1 has ended. (and it ended over a year ago)
Finally, consider using a Docker image that has Ruby already, rather than installing Ruby via apt-get. For example, the 2.3-stretch image gives you Debian stretch with Ruby 2.3's latest revision. Other versions are also available. The upside of using one of these Dockerfiles is that you know exactly what version of Ruby you're getting and it doesn't change.

Specifying the version of bundle

I am building a website using bundle and im building it through docker containers. Apparently when i install ruby-bundler, its giving me bundler 1.15.1 instead of 1.16.4 and it throws a warning that my bundler is outdated. This is leading to some other issues with some files im trying to update.
Here is my Dockerfile:
FROM ubuntu:17.10
RUN apt-get update && apt-get install -y \
git \
gcc \
make \
ruby \
ruby-dev \
locales \
ruby-bundler \
zlib1g-dev \
curl
RUN gem install bundler
RUN rm -rf /var/lib/apt/lists/*
# Add support for UTF-8.
RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG en_US.utf8
The line RUN gem install bundler is fixing the issue for me locally, but not on the docker image. So I ran which -a bundle and it gave me two directories: /usr/bin and /usr/local/bin. The new bundler seems to be installed on the local/bin and when i run bundle install it doesnt look for the local/bin therefore it throws me the warning.
What would be the best way to overcome this? Just keep in mind it has to be automated.
If I understood correctly you want only the newest version provided by gem install bundler
In that case all you need is to remove the ruby-bundler from the apt-get install remaining with this final Dockerfile
FROM ubuntu:17.10
RUN apt-get update && apt-get install -y \
git \
gcc \
make \
ruby \
ruby-dev \
locales \
zlib1g-dev \
curl
RUN gem install bundler
RUN rm -rf /var/lib/apt/lists/*
# Add support for UTF-8.
RUN localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
ENV LANG en_US.utf8

How to install Ruby on docker?

I am trying to install ruby on docker. I could install the 1.9 versions but it is not possible to install the latest version such as 2.2.0 and above. I am actually trying to set up calabash on docker. Have tried this. Whenever I try to install calabash-android in it getting the error
ERROR: Error installing calabash-android:
luffa requires Ruby version >= 2.0.
If you're starting FROM a different base Docker instance, you can simply RUN commands that install Ruby from your base instance's package management system. For example, this GitHub Gist shows how to use apt-get to install Ruby on a Ubuntu instance:
# Pull base image.
FROM dockerfile/ubuntu
# Install Ruby.
RUN \
apt-get update && \
apt-get install -y ruby
And this Gist shows a Dockerfile that's configured to install RVM and Ruby on a Ubuntu instance:
FROM ubuntu
RUN apt-get update
# basics
RUN apt-get install -y openssl
# install RVM, Ruby, and Bundler
RUN \curl -L https://get.rvm.io | bash -s stable
RUN /bin/bash -l -c "rvm requirements"
RUN /bin/bash -l -c "rvm install 2.0"
RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
This makes ruby available for any future RUN command and not just bash:
FROM debian:stretch-slim
RUN \
apt-get update && apt-get install -y --no-install-recommends --no-install-suggests curl bzip2 build-essential libssl-dev libreadline-dev zlib1g-dev && \
rm -rf /var/lib/apt/lists/* && \
curl -L https://github.com/sstephenson/ruby-build/archive/v20180329.tar.gz | tar -zxvf - -C /tmp/ && \
cd /tmp/ruby-build-* && ./install.sh && cd / && \
ruby-build -v 2.5.1 /usr/local && rm -rfv /tmp/ruby-build-* && \
gem install bundler --no-rdoc --no-ri
You could start view a dockerfile starting with:
# 2016
FROM ruby:2.3.0
# 2020
# Import your ruby version
FROM ruby:2.7.1
# Install bundler gem
RUN gem install bundler
# Assign a work directory
WORKDIR /work
That would use the docker image ruby, with ruby already installed.
The 2020 version comes from "Ruby version management with docker" from Arjun Das, mentioned by ArMD in the comments.
Low reputation so I can't comment inline (all those years of lurking, sigh), but in case anyone else happens across this while searching for ways to install old ruby versions to docker, I found #grosser's answer very helpful - it worked where trying to install via RVM simply wouldn't, at least for me.
I would, however, recommend using the recommended approach for installing ruby-build - the following worked for me:
<prior steps>
RUN git clone https://github.com/rbenv/ruby-build.git && \
PREFIX=/usr/local ./ruby-build/install.sh && \
ruby-build -v 2.4.1 /usr/local && \
gem install bundler -v <VERSION HERE> --no-ri --no-rdoc && bundle install
<following steps>
Key point here is that this keeps you up to date with ruby-build instead of being hard-coded to the 2018-03-29 version as in a previous #grosser's comment.
If you want to use things like bundle install and don't use a base image with pre-installed devtools like Ubuntu, you need to install these packages:
RUN apt-get update && apt-get install -y ruby ruby-dev ruby-bundler build-essential
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
Thanks to #Jacob and #grosser, I've managed to set up mine in a similar, if a bit more unpacked way:
# Install Local ruby
RUN git clone https://github.com/rbenv/rbenv.git ~/.rbenv \
&& echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc \
&& echo 'eval "$(rbenv init -)"' >> ~/.bashrc
ENV HOME /home/jenkins # Change this dir as needed.
ENV PATH "$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH"
ENV RUBY_VERSION 2.6.3
RUN mkdir -p "$(rbenv root)"/plugins \
&& git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
RUN rbenv install $RUBY_VERSION
RUN rbenv global $RUBY_VERSION && rbenv versions && ruby -v
# RUN curl -fsSL https://github.com/rbenv/rbenv-installer/raw/master/bin/rbenv-doctor | bash # Uncomment this to get rbenv to validate your setup.

How to check ruby version inside docker container

I have build the docker container by creating below docker file
# Select ubuntu as the base image
FROM ubuntu
# Install nginx, nodejs and curl
RUN apt-get update -q
RUN apt-get install -qy nginx
RUN apt-get install -qy curl
RUN apt-get install -qy nodejs
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
# Install rvm, ruby, bundler
RUN curl -sSL https://get.rvm.io | bash -s stable
RUN /bin/bash -l -c "rvm requirements"
RUN /bin/bash -l -c "rvm install 2.1.0"
RUN /bin/bash -l -c "gem install bundler --no-ri --no-rdoc"
# Add configuration files in repository to filesystem
ADD config/container/nginx-sites.conf /etc/nginx/sites-enabled/default
ADD config/container/start-server.sh /usr/bin/start-server
RUN chmod +x /usr/bin/start-server
# Add rails project to project directory
ADD ./ /rails
# set WORKDIR
WORKDIR /rails
# bundle install
RUN /bin/bash -l -c "bundle install"
# Publish port 80
EXPOSE 80
# Startup commands
ENTRYPOINT /usr/bin/start-server
When i go inside the container and give the command ruby -v it throws bash: ruby: command not found
Could any one help me in doing this
I've spent a bit of time messing with RVM, Ruby and Docker recently. This answer might not be what you're looking for, but it needs to be said anyway: if you don't absolutely need RVM, then don't use it in your docker file. You've already noticed one downside: having to pre-empt your commands with /bin/bash -lc. You'll run into another downside if you ever want to have a non-root user run a ruby program in your Docker container. Also, your problem is most likely related to Docker not loading .bashrc or .bash_profile (I forgot which one RVM modifies) when you run a bash shell.
Instead use this to compile Ruby from source:
RUN apt-get update
RUN apt-get install -yq build-essential openssl libreadline6 libreadline6-dev curl git-core \
zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev \
autoconf libc6-dev ncurses-dev automake libtool bison subversion libmysqlclient-dev
ADD http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.2.tar.gz /tmp/
RUN cd /tmp && tar -xzf /tmp/ruby-2.1.2.tar.gz
RUN cd /tmp/ruby-2.1.2/ && ./configure --disable-install-doc && make && make install
RUN rm -rf /tmp/*
ADD http://production.cf.rubygems.org/rubygems/rubygems-2.4.1.tgz /tmp/
RUN cd /tmp && tar -xzf /tmp/rubygems-2.4.1.tgz
RUN cd /tmp/rubygems-2.4.1 && ruby setup.rb
RUN rm -rf /tmp/*
RUN echo "gem: --no-ri --no-rdoc" > ~/.gemrc
RUN gem install bundler --no-rdoc --no-ri
You are not setting the default ruby after installing RVM. Trying setting the default ruby after installing it.
RUN /bin/bash -l -c "rvm install 2.1.0"
RUN /bin/bash -l -c "rvm use 2.1.0 --default"

Resources