#!/usr/bin/env bash # Recompile OpenSSL 1.1.1w on RHEL9+ and install old Rubies # Mateusz GruszczyƄski @linuxiarz.pl set -eo pipefail OPENSSL_VERSION="1.1.1w" OPENSSL_PREFIX="/opt/openssl111" SRC_DIR="/usr/local/src" FORCE_OPENSSL=0 WEB_PROXY="" TARGET_RUBY_VERSION="" usage() { cat <&2 exit 1 } log() { echo "$*" } command_exists() { command -v "$1" >/dev/null 2>&1 } package_installed() { local pkg="$1" rpm -q "${pkg}" >/dev/null 2>&1 && return 0 rpm -q --whatprovides "${pkg}" >/dev/null 2>&1 && return 0 return 1 } parse_args() { while [ $# -gt 0 ]; do case "$1" in --version) shift [ $# -gt 0 ] || die "--version requires a value" [ -z "${TARGET_RUBY_VERSION}" ] || die "Ruby version already provided: ${TARGET_RUBY_VERSION}" TARGET_RUBY_VERSION="$1" ;; --force-openssl) FORCE_OPENSSL=1 ;; --webproxy) shift [ $# -gt 0 ] || die "--webproxy requires a value" WEB_PROXY="$1" ;; -h|--help) usage exit 0 ;; -*) die "unknown option: $1" ;; *) [ -z "${TARGET_RUBY_VERSION}" ] || die "Ruby version already provided: ${TARGET_RUBY_VERSION}" TARGET_RUBY_VERSION="$1" ;; esac shift done [ -n "${TARGET_RUBY_VERSION}" ] || die "missing Ruby version, use --version 2.7.X" if [[ ! "${TARGET_RUBY_VERSION}" =~ ^2\.7\.[0-9]+$ ]]; then die "only Ruby 2.7.X is allowed, e.g. 2.7.9" fi } apply_proxy_env() { if [ -n "${WEB_PROXY}" ]; then export http_proxy="${WEB_PROXY}" export https_proxy="${WEB_PROXY}" export ftp_proxy="${WEB_PROXY}" export all_proxy="${WEB_PROXY}" export HTTP_PROXY="${WEB_PROXY}" export HTTPS_PROXY="${WEB_PROXY}" export FTP_PROXY="${WEB_PROXY}" export ALL_PROXY="${WEB_PROXY}" log "Using web proxy: ${WEB_PROXY}" fi } load_rvm() { export rvm_silence_path_mismatch_check_flag=1 if [ -s /etc/profile.d/rvm.sh ]; then . /etc/profile.d/rvm.sh elif [ -s /usr/local/rvm/scripts/rvm ]; then . /usr/local/rvm/scripts/rvm elif [ -s /root/.rvm/scripts/rvm ]; then . /root/.rvm/scripts/rvm else die "RVM was not found" fi if ! command -v rvm >/dev/null 2>&1; then die "RVM is not available in this shell" fi } download_file() { local url="$1" local output="$2" if command_exists wget; then if [ -n "${WEB_PROXY}" ]; then wget \ -e use_proxy=yes \ -e "http_proxy=${WEB_PROXY}" \ -e "https_proxy=${WEB_PROXY}" \ -O "${output}" \ "${url}" else wget -O "${output}" "${url}" fi return fi if command_exists curl; then if [ -n "${WEB_PROXY}" ]; then curl --proxy "${WEB_PROXY}" -fL -o "${output}" "${url}" else curl -fL -o "${output}" "${url}" fi return fi die "neither wget nor curl is available" } openssl_installation_ok() { local openssl_bin="${OPENSSL_PREFIX}/bin/openssl" local version_output [ -x "${openssl_bin}" ] || return 1 [ -d "${OPENSSL_PREFIX}/include/openssl" ] || return 1 [ -f "${OPENSSL_PREFIX}/lib/libssl.so" ] || [ -f "${OPENSSL_PREFIX}/lib64/libssl.so" ] || return 1 [ -f "${OPENSSL_PREFIX}/lib/libcrypto.so" ] || [ -f "${OPENSSL_PREFIX}/lib64/libcrypto.so" ] || return 1 version_output="$( LD_LIBRARY_PATH="${OPENSSL_PREFIX}/lib:${OPENSSL_PREFIX}/lib64:${LD_LIBRARY_PATH:-}" \ "${openssl_bin}" version 2>/dev/null || true )" [[ "${version_output}" == OpenSSL\ ${OPENSSL_VERSION}* ]] } ruby_in_path_ok() { local ruby_version openssl_version command_exists ruby || return 1 ruby_version="$(ruby -e 'print RUBY_VERSION' 2>/dev/null || true)" [ "${ruby_version}" = "${TARGET_RUBY_VERSION}" ] || return 1 openssl_version="$(ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION' 2>/dev/null || true)" [[ "${openssl_version}" == OpenSSL\ ${OPENSSL_VERSION}* ]] } ruby_in_rvm_present() { rvm list strings | grep -Fxq "ruby-${TARGET_RUBY_VERSION}" } ruby_in_rvm_ok() { local ruby_version openssl_version ruby_in_rvm_present || return 1 ruby_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -e 'print RUBY_VERSION' 2>/dev/null || true)" [ "${ruby_version}" = "${TARGET_RUBY_VERSION}" ] || return 1 openssl_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION' 2>/dev/null || true)" [[ "${openssl_version}" == OpenSSL\ ${OPENSSL_VERSION}* ]] } ruby_already_installed() { if ruby_in_rvm_ok; then log "Ruby ${TARGET_RUBY_VERSION} is already installed in RVM and linked with OpenSSL ${OPENSSL_VERSION}" return 0 fi if ruby_in_path_ok; then log "Ruby ${TARGET_RUBY_VERSION} is already available in PATH and linked with OpenSSL ${OPENSSL_VERSION}" return 0 fi return 1 } install_build_dependencies() { log "[1/6] Checking build dependencies" local missing_packages=() local packages=( curl wget tar xz bzip2 git perl-core pkgconfig zlib-devel readline-devel libyaml-devel libffi-devel gdbm-devel ncurses-devel gmp-devel autoconf automake libtool bison ) local pkg for pkg in "${packages[@]}"; do if ! package_installed "${pkg}"; then missing_packages+=("${pkg}") fi done local need_devtools=0 if ! command_exists gcc || ! command_exists g++ || ! command_exists make; then need_devtools=1 fi if [ "${need_devtools}" -eq 0 ] && [ "${#missing_packages[@]}" -eq 0 ]; then log "All required build dependencies are already installed" return 0 fi if [ "${need_devtools}" -eq 1 ]; then log "Installing missing group: Development Tools" dnf groupinstall -y "Development Tools" fi if [ "${#missing_packages[@]}" -gt 0 ]; then log "Installing missing packages: ${missing_packages[*]}" dnf install -y "${missing_packages[@]}" fi } install_openssl() { local tarball="openssl-${OPENSSL_VERSION}.tar.gz" local srcdir="openssl-${OPENSSL_VERSION}" local url="https://www.openssl.org/source/old/1.1.1/${tarball}" if [ "${FORCE_OPENSSL}" -eq 0 ] && openssl_installation_ok; then log "[2/6] OpenSSL ${OPENSSL_VERSION} already exists and works in ${OPENSSL_PREFIX}, skipping" return 0 fi log "[2/6] Preparing OpenSSL ${OPENSSL_VERSION}" mkdir -p "${SRC_DIR}" mkdir -p "${OPENSSL_PREFIX}" cd "${SRC_DIR}" if [ ! -f "${tarball}" ] || [ "${FORCE_OPENSSL}" -eq 1 ]; then rm -f "${tarball}" download_file "${url}" "${tarball}" else log "Using existing source archive: ${SRC_DIR}/${tarball}" fi rm -rf "${srcdir}" tar xf "${tarball}" cd "${srcdir}" log "[3/6] Building OpenSSL into ${OPENSSL_PREFIX}" ./config \ --prefix="${OPENSSL_PREFIX}" \ --openssldir="${OPENSSL_PREFIX}" \ shared zlib make -j"$(nproc)" rm -rf "${OPENSSL_PREFIX:?}/"* make install_sw openssl_installation_ok || die "OpenSSL ${OPENSSL_VERSION} verification failed after build" } install_ruby_with_rvm() { local -a rvm_args local action="install" log "[4/6] Configuring RVM build environment" rvm autolibs disable export CPPFLAGS="-I${OPENSSL_PREFIX}/include" export CFLAGS="-I${OPENSSL_PREFIX}/include" export LDFLAGS="-L${OPENSSL_PREFIX}/lib -L${OPENSSL_PREFIX}/lib64 -Wl,-rpath,${OPENSSL_PREFIX}/lib -Wl,-rpath,${OPENSSL_PREFIX}/lib64" export PKG_CONFIG_PATH="${OPENSSL_PREFIX}/lib/pkgconfig:${OPENSSL_PREFIX}/lib64/pkgconfig" export RUBY_CONFIGURE_OPTS="--with-openssl-dir=${OPENSSL_PREFIX}" if ruby_in_rvm_present; then action="reinstall" fi rvm_args=("${action}" "${TARGET_RUBY_VERSION}" "--with-openssl-dir=${OPENSSL_PREFIX}") if [ -n "${WEB_PROXY}" ]; then rvm_args+=("--proxy" "${WEB_PROXY}") fi log "[5/6] ${action^}ing Ruby ${TARGET_RUBY_VERSION} with RVM" rvm "${rvm_args[@]}" } verify_installation() { log "[6/6] Verifying installation" local installed_ruby_version installed_openssl_version if ruby_in_rvm_ok; then installed_ruby_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -e 'print RUBY_VERSION')" installed_openssl_version="$(rvm "ruby-${TARGET_RUBY_VERSION}" do ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION')" elif ruby_in_path_ok; then installed_ruby_version="$(ruby -e 'print RUBY_VERSION')" installed_openssl_version="$(ruby -ropenssl -e 'print OpenSSL::OPENSSL_VERSION')" else die "Ruby ${TARGET_RUBY_VERSION} verification failed" fi log "Installed Ruby version: ${installed_ruby_version}" log "Linked OpenSSL version: ${installed_openssl_version}" } main() { [ "${EUID}" -eq 0 ] || die "run this script as root" parse_args "$@" log "Requested Ruby version: ${TARGET_RUBY_VERSION}" apply_proxy_env load_rvm if ruby_already_installed; then log "Nothing to do" exit 0 fi install_build_dependencies install_openssl if ruby_already_installed; then log "Ruby is already installed after OpenSSL verification/build, skipping RVM build" verify_installation exit 0 fi install_ruby_with_rvm verify_installation } main "$@"