Skip to content

Commit

Permalink
Security: switch to using image's digest sha256 value on promotion
Browse files Browse the repository at this point in the history
  • Loading branch information
zzaakiirr committed Dec 13, 2024
1 parent f8b544d commit fc4d4a6
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 5 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ _Please add entries here for your pull requests that have not yet been released.

- Fixed issue where app cannot be deleted because one of the workloads has a volumeset in-use. [PR 245](https://github.com/shakacode/control-plane-flow/pull/245) by [Zakir Dzhamaliddinov](https://github.com/zzaakiirr).

### Changed

- Providing digest (SHA256 value) for image link on promotion from upstream. [PR 247](https://github.com/shakacode/control-plane-flow/pull/247) by [Zakir Dzhamaliddinov](https://github.com/zzaakiirr).

## [4.0.0] - 2024-08-21

### Fixed
Expand Down
11 changes: 11 additions & 0 deletions lib/command/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,17 @@ def self.add_app_identity_option(required: false)
}
}
end

def self.use_digest_ref_option(required: false)
{
name: :use_digest_ref,
params: {
desc: "Uses the Docker image's digest as its reference.",
type: :boolean,
required: required
}
}
end
# rubocop:enable Metrics/MethodLength

def self.all_options
Expand Down
12 changes: 8 additions & 4 deletions lib/command/deploy_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ class DeployImage < Base
NAME = "deploy-image"
OPTIONS = [
app_option(required: true),
run_release_phase_option
run_release_phase_option,
use_digest_ref_option
].freeze
DESCRIPTION = "Deploys the latest image to app workloads, and runs a release script (optional)"
LONG_DESCRIPTION = <<~DESC
Expand All @@ -15,22 +16,25 @@ class DeployImage < Base
- If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
DESC

def call # rubocop:disable Metrics/MethodLength
def call # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
run_release_script if config.options[:run_release_phase]

deployed_endpoints = {}

image = cp.latest_image
if cp.fetch_image_details(image).nil?
image_details = cp.fetch_image_details(image)
if image_details.nil?
raise "Image '#{image}' does not exist in the Docker repository on Control Plane " \
"(see https://console.cpln.io/console/org/#{config.org}/repository/#{config.app}). " \
"Use `cpflow build-image` first."
end

image = "#{image_details['name']}@#{image_details['digest']}" if config.options[:use_digest_ref]

config[:app_workloads].each do |workload|
workload_data = cp.fetch_workload!(workload)
workload_data.dig("spec", "containers").each do |container|
next unless container["image"].match?(%r{^/org/#{config.org}/image/#{config.app}:})
next unless container["image"].match?(%r{^/org/#{config.org}/image/#{config.app}})

container_name = container["name"]
step("Deploying image '#{image}' for workload '#{container_name}'") do
Expand Down
2 changes: 1 addition & 1 deletion lib/command/promote_app_from_upstream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def copy_image_from_upstream
def deploy_image
args = []
args.push("--run-release-phase") if config.current[:release_script]
run_cpflow_command("deploy-image", "-a", config.app, *args)
run_cpflow_command("deploy-image", "-a", config.app, "--use-digest-ref", *args)
end
end
end
11 changes: 11 additions & 0 deletions spec/command/deploy_image_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@
end
end

context "with --use-digest-ref option" do
let!(:app) { dummy_test_app("rails-non-app-image", create_if_not_exists: true) }

it "deploys latest image with digest reference", :slow do
result = run_cpflow_command("deploy-image", "-a", app, "--use-digest-ref")

expect(result[:status]).to eq(0)
expect(result[:stderr]).to match(/Deploying image '#{app}:\d+@sha256:[a-fA-F0-9]{64}'/)
end
end

context "when 'release_script' is not defined" do
let!(:app) { dummy_test_app("nothing") }

Expand Down
3 changes: 3 additions & 0 deletions spec/command/promote_app_from_upstream_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
expect(result[:stderr]).to match(%r{Pulling image from '.+?/#{upstream_app}:1'})
expect(result[:stderr]).to match(%r{Pushing image to '.+?/#{app}:1'})
expect(result[:stderr]).not_to include("Running release script")
expect(result[:stderr]).to match(/Deploying image '#{app}:1@sha256:[a-fA-F0-9]{64}'/)
expect(result[:stderr]).to match(%r{rails: https://rails-.+?.cpln.app})
end
end
Expand Down Expand Up @@ -64,6 +65,7 @@
expect(result[:stderr]).to match(%r{Pushing image to '.+?/#{app}:1'})
expect(result[:stderr]).to include("Running release script")
expect(result[:stderr]).to include("Failed to run release script")
expect(result[:stderr]).not_to include("Deploying image")
expect(result[:stderr]).not_to match(%r{rails: https://rails-.+?.cpln.app})
end
end
Expand Down Expand Up @@ -98,6 +100,7 @@
expect(result[:stderr]).to match(%r{Pushing image to '.+?/#{app}:1'})
expect(result[:stderr]).to include("Running release script")
expect(result[:stderr]).to include("Finished running release script")
expect(result[:stderr]).to match(/Deploying image '#{app}:1@sha256:[a-fA-F0-9]{64}'/)
expect(result[:stderr]).to match(%r{rails: https://rails-.+?.cpln.app})
end
end
Expand Down

0 comments on commit fc4d4a6

Please sign in to comment.