Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bsfy 214 #289

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 30 additions & 4 deletions lib/travis/scheduler/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,39 @@
module Travis
module Scheduler
class Config < Travis::Config
define amqp: { username: 'guest', password: 'guest', host: 'localhost', prefetch: 1 },
class << self
def amqp_username
ENV['TRAVIS_AMQP_USERNAME'] || 'guest'
end

def amqp_password
ENV['TRAVIS_AMQP_PASSWORD'] || 'guest'
end

def billing_auth_keys
ENV['TRAVIS_BILLING_AUTH_KEYS'] || 'auth_keys'
end

def travis_site
ENV['TRAVIS_SITE'] || 'org'
end

def job_board_url
ENV['JOB_BOARD_URL'] || 'https://job-board.travis-ci.org'
end

def job_board_auth
ENV['JOB_BOARD_AUTH'] || 'user:pass'
end
end

define amqp: { username: amqp_username, password: amqp_password, host: 'localhost', prefetch: 1 },
database: { adapter: 'postgresql', database: "travis_#{env}", encoding: 'unicode', min_messages: 'warning' },
delegate: { },
encryption: { key: SecureRandom.hex(64) },
enterprise: false,
github: { api_url: 'https://api.github.com', source_host: 'github.com' },
billing: { url: 'http://localhost:9292/', auth_key: 'auth_keys' },
billing: { url: 'http://localhost:9292/', auth_key: billing_auth_keys },
host: 'https://travis-ci.com',
interval: 2,
limit: { public: 5, education: 1, default: 5, by_owner: {}, delegate: {} },
Expand All @@ -24,9 +50,9 @@ class Config < Travis::Config
sentry: { },
sidekiq: { namespace: 'sidekiq', pool_size: 3, log_level: :warn },
ping: { interval: 5 * 60 },
site: ENV['TRAVIS_SITE'] || 'org',
site: travis_site,
ssl: { },
job_board: { url: ENV['JOB_BOARD_URL'] || 'https://job-board.travis-ci.org', auth: ENV['JOB_BOARD_AUTH'] || 'user:pass' },
job_board: { url: job_board_url, auth: job_board_auth },
vcs_proxy_api: { url: 'http://vcs_proxy_api' }

def metrics
Expand Down
2 changes: 2 additions & 0 deletions lib/travis/scheduler/record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
require 'travis/scheduler/record/branch'
require 'travis/scheduler/record/build'
require 'travis/scheduler/record/commit'
require 'travis/scheduler/record/custom_key'
require 'travis/scheduler/record/installation'
require 'travis/scheduler/record/job'
require 'travis/scheduler/record/log'
require 'travis/scheduler/record/membership'
require 'travis/scheduler/record/organization'
require 'travis/scheduler/record/permission'
require 'travis/scheduler/record/pull_request'
Expand Down
7 changes: 7 additions & 0 deletions lib/travis/scheduler/record/custom_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require 'travis/support/encrypted_column'
require 'travis/support/secure_config'

class CustomKey < ActiveRecord::Base
serialize :private_key, Travis::EncryptedColumn.new
belongs_to :owner, polymorphic: true
end
4 changes: 4 additions & 0 deletions lib/travis/scheduler/record/membership.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :organization
end
35 changes: 34 additions & 1 deletion lib/travis/scheduler/serialize/worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def data
vm_size: job.vm_size,
queue: job.queue,
config: job.decrypted_config,
env_vars: job.env_vars,
env_vars: env_vars_with_custom_keys,
job: job_data,
host: Travis::Scheduler.config.host,
source: build_data,
Expand Down Expand Up @@ -238,6 +238,39 @@ def allowed_repositories
def travis_vcs_proxy?
repo.vcs_type == 'TravisproxyRepository'
end

def env_vars_with_custom_keys
job.env_vars + custom_keys
end

def custom_keys
return [] if job.decrypted_config[:keys].blank?

if job.source.event_type == 'pull_request' && job.source.request.pull_request.head_repo_slug != job.source.request.pull_request.base_repo_slug
base_repo_owner_name, base_repo_name = job.source.request.pull_request.base_repo_slug.to_s.split('/')
return [] unless base_repo_owner_name && base_repo_name


base_repo = ::Repository.find_by(owner_name: base_repo_owner_name, name: base_repo_name)
return [] unless base_repo && base_repo.settings.share_ssh_keys_with_forks?

end

job.decrypted_config[:keys].map do |key|
custom_key = CustomKey.where(name: key, owner_id: build.sender_id, owner_type: 'User').first
if custom_key.nil?
org_ids = Membership.where(user_id: build.sender_id).map(&:organization_id)
if !base_repo.nil? && base_repo.owner_type == 'Organization'
org_ids.reject! { |id| id != base_repo.owner_id }
elsif repo.owner_type == 'Organization'
org_ids.reject! { |id| id != repo.owner_id }
end

custom_key = CustomKey.where(name: key, owner_id: org_ids, owner_type: 'Organization').first unless org_ids.empty?
end
custom_key.nil? ? nil : { name: "TRAVIS_#{key}", value: Base64.strict_encode64(custom_key.private_key), public: false, branch: nil }
end.compact
end
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/travis/scheduler/serialize/worker/config/decrypt.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ def apply
config[key] = process_env(config[key]) if config[key]
end

force_vault_to_be_secure!(config)
config[:vault] = decryptor.decrypt(config[:vault]) if config[:vault]
config[:addons] = decryptor.decrypt(config[:addons]) if config[:addons]
config
end

private

def force_vault_to_be_secure!(config)
config[:vault].delete(:token) if config.dig(:vault, :token).is_a?(String)
end

def secure_env?
!!options[:secure_env]
end
Expand Down
2 changes: 1 addition & 1 deletion lib/travis/scheduler/serialize/worker/job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def env_vars
end

def secure_env?
defined?(@secure_env) ? @secure_env : @secure_env = !pull_request? || secure_env_allowed_in_pull_request?
defined?(@secure_env) ? @secure_env : (@secure_env = (!pull_request? || secure_env_allowed_in_pull_request?))
end

def pull_request?
Expand Down
8 changes: 8 additions & 0 deletions lib/travis/scheduler/serialize/worker/repo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ def travis_vcs_proxy?
vcs_type == 'TravisproxyRepository'
end

def owner_type
repo.owner_type
end

def owner_id
repo.owner_id
end

private

# If the repo does not have a custom timeout, look to the repo's
Expand Down
9 changes: 9 additions & 0 deletions spec/support/factories.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,14 @@ def public=(value)
author_name 'Sven Fuchs'
author_email 'me@svenfuchs.com'
end

factory :membership do
association :organization
association :user
end

factory :custom_key, :class => 'CustomKey' do
name 'key'
end
end

10 changes: 10 additions & 0 deletions spec/travis/scheduler/serialize/worker/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,16 @@ def encrypt(string)
let(:env) { [{ FOO: 'foo', BAR: 'bar' }, encrypt('BAZ=baz')] }
it { should eql env: ['FOO=foo', 'BAR=bar', 'SECURE BAZ=baz'], global_env: ['FOO=foo', 'BAR=bar', 'SECURE BAZ=baz'] }
end

describe 'decrypts vault secure token' do
let(:config) { { vault: { token: { secure: encrypt('my_key') } } } }
it { should eql vault: {token: 'my_key'} }
end

describe 'clears vault unsecure token' do
let(:config) { { vault: { token: 'my_key' } } }
it { should eql vault: {} }
end
end

describe 'with secure env disabled' do
Expand Down
41 changes: 41 additions & 0 deletions spec/travis/scheduler/serialize/worker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -449,4 +449,45 @@ def encrypted(value)
it { expect(data[:keep_netrc]).to be false }
end
end

context 'custom_keys' do
let!(:organization1) {FactoryGirl.create(:org, login: "org1", id: 1)}
let!(:organization2) {FactoryGirl.create(:org, login: "org2", id: 2)}
let!(:repo) { FactoryGirl.create(:repo, default_branch: 'main') }
let!(:membership1) {FactoryGirl.create(:membership, user: repo.owner, organization: organization1) }
let!(:membership2) {FactoryGirl.create(:membership, user: repo.owner, organization: organization2) }
let!(:custom_key1) {FactoryGirl.create(:custom_key, name: 'key1', owner_id: organization1.id, owner_type: 'Organization', private_key: 'abc')}
let!(:custom_key2) {FactoryGirl.create(:custom_key, name: 'key1', owner_id: organization2.id, owner_type: 'Organization', private_key: 'def')}

describe 'when two organization have the same key name' do
before {
build.update(sender_id: repo.owner.id)
job.update(config: {:keys => ['key1']})
repo.update_attributes(owner: organization2, owner_name: 'org2')
}

it { expect(data[:env_vars]).to include({:name=>"TRAVIS_key1", :value=>"ZGVm", :public=>false, :branch=>nil})}
end

describe 'when user has no access to organization' do
let!(:organization3) {FactoryGirl.create(:org, login: "org3", id: 3)}
let!(:custom_key3) {FactoryGirl.create(:custom_key, name: 'key1', owner_id: organization3.id, owner_type: 'Organization', private_key: 'ghi')}
let(:raw_settings) do
{
env_vars: nil,
timeout_hard_limit: 180,
timeout_log_silence: 20,
share_ssh_keys_with_forks: false
}
end

before {
build.update(sender_id: repo.owner.id)
job.update(config: {:keys => ['key1']})
repo.update_attributes(owner: organization3, owner_name: 'org3')
}

it { expect(data[:env_vars]).to eq([])}
end
end
end