sshdeploymentgithub-actionscapistrano

Inappropriate IOCTL for device when using capistrano in github-actions


I have the following yaml file set up for github-actions:

name: Build, Test, and Deploy to Staging

on:
  push:
    branches:
      - develop
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    env:
      DB_DATABASE: foamfactory_stage
      DB_ROOT_USER: root
      DB_ROOT_PASSWORD: password
      DB_USER: admin
      DB_PASSWORD: ${{ secrets.MYSQL_USER_PASSWORD }}
    steps:
      - name: Set up MySQL
        run: |
          sudo systemctl start mysql.service
          mysql -e 'CREATE DATABASE ${{ env.DB_DATABASE }};' -u${{ env.DB_ROOT_USER }} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "CREATE USER '${{ env.DB_USER }}'@'localhost' IDENTIFIED BY '${{ env.DB_PASSWORD }}';" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "CREATE DATABASE IF NOT EXISTS ${{ env.DB_DATABASE }};" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "GRANT ALL PRIVILEGES ON ${{ env.DB_DATABASE }}.* to '${{ env.DB_USER }}'@'localhost';" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
          mysql -e "FLUSH PRIVILEGES;" -u${{env.DB_ROOT_USER}} -p${{ env.DB_ROOT_PASSWORD }}
      - name: Install SSH key to Server
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.STAGE_API_DEPLOY_KEY }}
          name: github-actions
          known_hosts: ${{ secrets.STAGE_API_HOST_KEY }}
          config: |
            host stage.api.example.com
            IdentityFile ~/.ssh/github-actions
            IdentitiesOnly yes
            ForwardAgent yes
      - uses: actions/checkout@v2
      - name: Set up Ruby Environment
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: 2.6.1
          bundler-cache: true
        env:
          RAILS_ENV: staging
      - name: Setup Database
        env:
          RAILS_ENV: staging
        run: bundle exec rake db:setup
      - name: Perform Database Migrations
        env:
          RAILS_ENV: staging
        run: bundle exec rake db:migrate
      - name: Run specs
        env:
          RAILS_ENV: staging
        run: bundle exec rails spec
  deploy-staging:
    needs: build-and-test
    runs-on: ubuntu-latest
    steps:
      - name: Install SSH Host Key
        uses: shimataro/ssh-key-action@v2
        with:
          key: ${{ secrets.STAGE_API_DEPLOY_KEY }}
          name: github-actions
          known_hosts: ${{ secrets.STAGE_API_HOST_KEY }}
          config: |
            host stage.api.example.com
            IdentityFile ~/.ssh/github-actions
            IdentitiesOnly yes
            ForwardAgent yes
      - uses: actions/checkout@v2
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - name: Install SSH Key
        run: |
          eval "$(ssh-agent -s)"
          ssh-add -D
          ssh-add ~/.ssh/github-actions
      - name: Check SSH Key Viability
        run: |
          echo "ls -al" | ssh deploy@stage.api.example.com
      - name: Deploy to staging
        run: |
          bundle exec cap staging deploy

The last step, 'Deploy to staging' is failing with the following output:

Run bundle exec cap staging deploy
  bundle exec cap staging deploy
  shell: /usr/bin/bash -e {0}
  
#<Thread:0x000055e7af018820@/home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/runners/parallel.rb:10 run> terminated with exception (report_on_exception is true):
/home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as deploy@stage.api.example.com: Inappropriate ioctl for device (SSHKit::Runner::ExecuteError)
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/runners/parallel.rb:11:in `block (2 levels) in execute'
/home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/prompt.rb:45:in `noecho': Inappropriate ioctl for device (Errno::ENOTTY)
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/prompt.rb:45:in `ask'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/authentication/methods/password.rb:68:in `ask_password'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/authentication/methods/password.rb:21:in `authenticate'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/authentication/session.rb:86:in `block in authenticate'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/authentication/session.rb:72:in `each'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh/authentication/session.rb:72:in `authenticate'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/net-ssh-6.1.0/lib/net/ssh.rb:255:in `start'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/connection_pool.rb:63:in `call'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/connection_pool.rb:63:in `with'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/netssh.rb:177:in `with_ssh'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/netssh.rb:130:in `execute_command'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/abstract.rb:148:in `block in create_command_and_execute'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/abstract.rb:148:in `tap'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/abstract.rb:148:in `create_command_and_execute'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/abstract.rb:61:in `test'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/capistrano-passenger-0.2.1/lib/capistrano/tasks/passenger.cap:43:in `block (3 levels) in <top (required)>'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/abstract.rb:31:in `instance_exec'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/backends/abstract.rb:31:in `run'
    from /home/runner/work/api/api/vendor/bundle/ruby/2.6.0/gems/sshkit-1.21.2/lib/sshkit/runners/parallel.rb:12:in `block (2 levels) in execute'
(Backtrace restricted to imported tasks)
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@stage.api.example.com: Inappropriate ioctl for device


Caused by:
Errno::ENOTTY: Inappropriate ioctl for device

Tasks: TOP => rvm:hook => passenger:rvm:hook => passenger:test_which_passenger
(See full trace by running task with --trace)
deploy@stage.api.example.com's password:
Error: Process completed with exit code 1.

It appears that there is some lack of communication between the ssh-agent and the capistrano task, hence the reason it appears to be asking for a password. However, on the previous step, 'Check SSH Key Viability' it's clear that the SSH key is usable and working:

Run echo "ls -al" | ssh deploy@stage.api.example.com
  echo "ls -al" | ssh deploy@stage.api.example.com
  shell: /usr/bin/bash -e {0}
Pseudo-terminal will not be allocated because stdin is not a terminal.
Warning: Permanently added the ECDSA host key for IP address 'XXX.XXX.XXX.XXX' to the list of known hosts.

<output of ls command>

I'm not sure what I'm doing incorrectly here, but was wondering if someone might give me a hint as to why this isn't working to deploy from github-actions.


Solution

  • The reason this was failing was that it was using an incorrect SSH key to authenticate to Github, based on what was being used on the server (in this case, the server being api.stage.example.com). Because, on api.stage.example.com, my ~/.ssh/config file showed:

    Host github.com
     HostName github.com
     IdentityFile ~/.ssh/github-actions
    

    It was using that file - ~/.ssh/github-actions, and not id_rsa as the private key to authenticate to github. As such, I needed to add a deploy key for the appropriate repository that contained the corresponding ~/.ssh/github_actions.pub key.

    Further, I had to change the line in my config/deploy/staging.rb file that previously only used id_rsa to utilize either id_rsa or github_actions:

    set :ssh_options, {
      keys: %w(~/.ssh/id_rsa ~/.ssh/github-actions),
      forward_agent: true,
    }
    

    Re-deploying then alleviated the errors in question.