Skip to content

Commit 384b017

Browse files
authored
Support overriding the user on Processes (#4407)
Issue: #4372
1 parent 12ebacb commit 384b017

34 files changed

+410
-38
lines changed

app/actions/deployment_create.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def clone_existing_web_process(app, revision, process_instances)
113113
state: ProcessModel::STOPPED,
114114
instances: process_instances,
115115
command: command,
116+
user: web_process.user,
116117
memory: web_process.memory,
117118
file_descriptors: web_process.file_descriptors,
118119
disk_quota: web_process.disk_quota,

app/actions/process_update.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def update(process, message, strategy_class)
3131
end
3232

3333
process.command = strategy.updated_command if message.requested?(:command)
34+
process.user = message.user if message.requested?(:user)
3435
process.health_check_timeout = message.health_check_timeout if message.requested?(:health_check_timeout)
3536

3637
process.health_check_invocation_timeout = message.health_check_invocation_timeout if message.requested?(:health_check_invocation_timeout)

app/messages/process_update_message.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
module VCAP::CloudController
55
class ProcessUpdateMessage < MetadataBaseMessage
6-
register_allowed_keys %i[command health_check readiness_health_check]
6+
register_allowed_keys %i[command user health_check readiness_health_check]
77

88
# rubocop:disable Metrics/CyclomaticComplexity
99
def initialize(params={})
@@ -25,6 +25,10 @@ def self.command_requested?
2525
@command_requested ||= proc { |a| a.requested?(:command) }
2626
end
2727

28+
def self.user_requested?
29+
@user_requested ||= proc { |a| a.requested?(:user) }
30+
end
31+
2832
validates_with NoAdditionalKeysValidator
2933

3034
validates :command,
@@ -33,6 +37,12 @@ def self.command_requested?
3337
allow_nil: true,
3438
if: command_requested?
3539

40+
validates :user,
41+
string: true,
42+
length: { in: 1..255, message: 'must be between 1 and 255 characters' },
43+
allow_nil: true,
44+
if: user_requested?
45+
3646
validates :health_check_type,
3747
inclusion: {
3848
in: [HealthCheckTypes::PORT, HealthCheckTypes::PROCESS, HealthCheckTypes::HTTP],

app/models.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
require 'models/runtime/constraints/readiness_health_check_policy'
6464
require 'models/runtime/constraints/docker_policy'
6565
require 'models/runtime/constraints/sidecar_memory_less_than_process_memory_policy'
66+
require 'models/runtime/constraints/process_user_policy'
6667
require 'models/runtime/revision_model'
6768
require 'models/runtime/revision_process_command_model'
6869
require 'models/runtime/revision_sidecar_model'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class ProcessUserPolicy
2+
ERROR_MSG = 'invalid (requested user %<requested_user>s not allowed, permitted users are: %<allowed_users>s)'.freeze
3+
4+
def initialize(process, allowed_users)
5+
@process = process
6+
@allowed_users = allowed_users
7+
@errors = process.errors
8+
end
9+
10+
def validate
11+
return if @process.user.blank?
12+
return if @allowed_users.map(&:downcase).include?(@process.user.downcase)
13+
14+
@errors.add(:user, sprintf(ERROR_MSG, requested_user: quote_user(@process.user), allowed_users: formatted_users_for_error))
15+
end
16+
17+
private
18+
19+
def formatted_users_for_error
20+
@allowed_users.map { |u| quote_user(u) }.join(', ')
21+
end
22+
23+
def quote_user(user)
24+
"'#{user}'"
25+
end
26+
end

app/models/runtime/process_model.rb

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
require_relative 'buildpack'
1414

1515
module VCAP::CloudController
16-
class ProcessModel < Sequel::Model(:processes)
16+
class ProcessModel < Sequel::Model(:processes) # rubocop:disable Metrics/ClassLength
1717
include Serializer
1818

1919
plugin :serialization
@@ -34,7 +34,8 @@ def after_initialize
3434
NO_APP_PORT_SPECIFIED = -1
3535
DEFAULT_HTTP_PORT = 8080
3636
DEFAULT_PORTS = [DEFAULT_HTTP_PORT].freeze
37-
UNLIMITED_LOG_RATE = -1
37+
DEFAULT_USER = 'vcap'.freeze
38+
UNLIMITED_LOG_RATE = -1
3839

3940
many_to_one :app, class: 'VCAP::CloudController::AppModel', key: :app_guid, primary_key: :guid, without_guid_generation: true
4041
many_to_one :revision, class: 'VCAP::CloudController::RevisionModel', key: :revision_guid, primary_key: :guid, without_guid_generation: true
@@ -267,7 +268,8 @@ def validation_policies
267268
ReadinessHealthCheckPolicy.new(self, readiness_health_check_invocation_timeout, readiness_health_check_type, readiness_health_check_http_endpoint,
268269
readiness_health_check_interval),
269270
DockerPolicy.new(self),
270-
PortsPolicy.new(self)
271+
PortsPolicy.new(self),
272+
ProcessUserPolicy.new(self, permitted_users)
271273
]
272274
end
273275

@@ -391,6 +393,12 @@ def started_command
391393
specified_commands[type] || revision.droplet&.process_start_command(type) || ''
392394
end
393395

396+
def run_action_user
397+
return user if user.present?
398+
399+
docker? ? docker_run_action_user : DEFAULT_USER
400+
end
401+
394402
def specified_or_detected_command
395403
command.presence || detected_start_command
396404
end
@@ -446,15 +454,15 @@ def debug
446454

447455
delegate :cnb?, to: :app
448456

457+
delegate :windows_gmsa_credential_refs, to: :app
458+
449459
def database_uri
450460
service_binding_uris = service_bindings.map do |binding|
451461
binding.credentials['uri'] if binding.credentials.present?
452462
end.compact
453463
DatabaseUriGenerator.new(service_binding_uris).database_uri
454464
end
455465

456-
delegate :windows_gmsa_credential_refs, to: :app
457-
458466
def max_app_disk_in_mb
459467
VCAP::CloudController::Config.config.get(:maximum_app_disk_in_mb)
460468
end
@@ -564,6 +572,18 @@ def open_ports
564572

565573
private
566574

575+
def permitted_users
576+
Set.new([DEFAULT_USER]) + Config.config.get(:additional_allowed_process_users)
577+
end
578+
579+
def docker_run_action_user
580+
return DEFAULT_USER unless docker?
581+
582+
docker_exec_metadata = Oj.load(execution_metadata)
583+
container_user = docker_exec_metadata['user']
584+
container_user.presence || 'root'
585+
end
586+
567587
def non_unique_process_types
568588
return [] unless app
569589

app/presenters/v3/process_presenter.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ def to_hash
2828
version: process.version,
2929
type: process.type,
3030
command: redact(process.specified_or_detected_command),
31+
user: process.run_action_user,
3132
instances: process.instances,
3233
memory_in_mb: process.memory,
3334
disk_in_mb: process.disk_quota,

config/cloud_controller.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ maximum_app_disk_in_mb: 2048
6767
max_retained_deployments_per_app: 100
6868
max_retained_builds_per_app: 100
6969
max_retained_revisions_per_app: 100
70+
additional_allowed_process_users: ['ContainerUser']
7071

7172
broker_client_default_async_poll_interval_seconds: 60
7273
broker_client_max_async_poll_duration_minutes: 10080
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Sequel.migration do
2+
up do
3+
alter_table :processes do
4+
add_column :user, String, null: true, default: nil, size: 255 unless @db.schema(:processes).map(&:first).include?(:user)
5+
end
6+
end
7+
8+
down do
9+
alter_table :processes do
10+
drop_column :user if @db.schema(:processes).map(&:first).include?(:user)
11+
end
12+
end
13+
end

docs/v3/source/includes/api_resources/_processes.erb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"guid": "6a901b7c-9417-4dc1-8189-d3234aa0ab82",
44
"type": "web",
55
"command": "rackup",
6+
"user": "vcap",
67
"instances": 5,
78
"memory_in_mb": 256,
89
"disk_in_mb": 1024,
@@ -80,6 +81,7 @@
8081
"guid": "6a901b7c-9417-4dc1-8189-d3234aa0ab82",
8182
"type": "web",
8283
"command": "[PRIVATE DATA HIDDEN IN LISTS]",
84+
"user": "vcap",
8385
"instances": 5,
8486
"memory_in_mb": 256,
8587
"disk_in_mb": 1024,
@@ -141,6 +143,7 @@
141143
"guid": "3fccacd9-4b02-4b96-8d02-8e865865e9eb",
142144
"type": "worker",
143145
"command": "[PRIVATE DATA HIDDEN IN LISTS]",
146+
"user": "vcap",
144147
"instances": 1,
145148
"memory_in_mb": 256,
146149
"disk_in_mb": 1024,

0 commit comments

Comments
 (0)