Skip to content

Commit

Permalink
fix: Dockerfile & Buildpacks builder has no permissions, Close #96
Browse files Browse the repository at this point in the history
  • Loading branch information
wangeguo committed Jan 20, 2024
1 parent 3f248ba commit cea7a4f
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 23 deletions.
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[workspace.package]
version = "0.7.4"
version = "0.7.5"
edition = "2021"
license = "Apache-2.0"
repository = "https://github.com/amphitheatre-app/amphitheatre"
Expand Down
7 changes: 4 additions & 3 deletions resources/src/containers/buildpacks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
// limitations under the License.

use amp_common::resource::ActorSpec;
use k8s_openapi::api::core::v1::{Container, EnvVar, VolumeMount};
use k8s_openapi::api::core::v1::{Container, EnvVar, SecurityContext, VolumeMount};

use super::{workspace_mount, WORKSPACE_DIR};
use crate::args;

/// Build and return the container spec for the buildpacks container
pub fn container(spec: &ActorSpec) -> Container {
pub fn container(spec: &ActorSpec, security_context: &Option<SecurityContext>) -> Container {
let build = spec.character.build.clone().unwrap_or_default();

// Parse the arguments for the container
Expand Down Expand Up @@ -47,6 +47,7 @@ pub fn container(spec: &ActorSpec) -> Container {
args: Some(arguments),
env: Some(environment),
volume_mounts: Some(vec![workspace_mount(), docker_config_mount()]),
security_context: security_context.clone(),
..Default::default()
}
}
Expand All @@ -65,7 +66,7 @@ mod tests {
fn test_container() {
let spec = ActorSpec { name: "test".into(), image: "test".into(), ..Default::default() };

let container = container(&spec);
let container = container(&spec, &None);

assert_eq!(container.name, "builder");
assert_eq!(container.image, Some("gcr.io/buildpacks/builder:v1".into()));
Expand Down
1 change: 1 addition & 0 deletions resources/src/containers/git_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::args;
/// Build and return the container spec for the git-sync.
pub fn container(spec: &ActorSpec) -> Container {
let source = spec.source.as_ref().unwrap();

// Parse the arguments for the container
let revision = source.rev();
let arguments = vec![
Expand Down
7 changes: 4 additions & 3 deletions resources/src/containers/syncer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
use std::path::PathBuf;

use amp_common::resource::ActorSpec;
use k8s_openapi::api::core::v1::Container;
use k8s_openapi::api::core::v1::{Container, SecurityContext};

use super::{workspace_mount, DEFAULT_SYNCER_IMAGE, WORKSPACE_DIR};
use crate::args;
use crate::error::Result;

/// Build and return the container spec for the syncer.
pub fn container(playbook: &str, spec: &ActorSpec) -> Result<Container> {
pub fn container(playbook: &str, spec: &ActorSpec, security_context: &Option<SecurityContext>) -> Result<Container> {
// Set the working directory to `workspace` argument.
let build = spec.character.build.clone().unwrap_or_default();
let mut workdir = PathBuf::from(WORKSPACE_DIR);
Expand All @@ -48,6 +48,7 @@ pub fn container(playbook: &str, spec: &ActorSpec) -> Result<Container> {
// image_pull_policy: Some("IfNotPresent".to_string()),
args: Some(args(&arguments, 2)),
volume_mounts: Some(vec![workspace_mount()]),
security_context: security_context.clone(),
..Default::default()
})
}
Expand All @@ -59,7 +60,7 @@ mod tests {
#[test]
fn test_container() {
let spec = ActorSpec { name: "test".into(), image: "test".into(), ..Default::default() };
let container = container("test", &spec).unwrap();
let container = container("test", &spec, &None).unwrap();

assert_eq!(container.name, "syncer");
assert_eq!(container.image, Some(DEFAULT_SYNCER_IMAGE.into()));
Expand Down
47 changes: 37 additions & 10 deletions resources/src/deployer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ use amp_common::config::Credentials;
use amp_common::docker::{self, registry, DockerConfig};
use amp_common::resource::Actor;
use amp_common::schema::BuildMethod;
use k8s_openapi::api::core::v1::{Container, PodSecurityContext, PodSpec};
use k8s_openapi::api::core::v1::{Container, PodSecurityContext, PodSpec, SecurityContext};
use kube::ResourceExt;
use tracing::{debug, error, info};

use crate::containers::{application, buildpacks, docker_config_volume, git_sync, kaniko, syncer, workspace_volume};
use crate::error::{self, Error, Result};
use crate::{deployment, hash, service};

const DEFAULT_RUN_AS_GROUP: i64 = 1000;
const DEFAULT_RUN_AS_USER: i64 = 1001;

pub struct Deployer {
k8s: kube::Client,
credentials: Credentials,
Expand Down Expand Up @@ -89,25 +92,30 @@ impl Deployer {
}

async fn prepare(&mut self) -> Result<()> {
let build = self.actor.spec.character.build.clone().unwrap_or_default();

// Get SecurityContext for the container
let builder = build.buildpacks.clone().unwrap_or_default().builder;
let security_context = security_context(&builder);

// Set the syncer
if self.actor.spec.source.is_some() {
self.syncer = Some(git_sync::container(&self.actor.spec));
} else {
let playbook = owner_reference(&self.actor)?;
self.syncer = Some(syncer::container(&playbook, &self.actor.spec)?);
self.syncer = Some(syncer::container(&playbook, &self.actor.spec, &security_context)?);
}

// Prefer to use Kaniko to build images with Dockerfile,
// else, build the image with Cloud Native Buildpacks
let build = self.actor.spec.character.build.clone().unwrap_or_default();
match build.method() {
BuildMethod::Dockerfile => {
debug!("Found dockerfile, build it with kaniko");
self.builder = Some(kaniko::container(&self.actor.spec));
}
BuildMethod::Buildpacks => {
debug!("Build the image with Cloud Native Buildpacks");
self.builder = Some(buildpacks::container(&self.actor.spec));
self.builder = Some(buildpacks::container(&self.actor.spec, &security_context));
}
};

Expand Down Expand Up @@ -200,12 +208,7 @@ impl Deployer {
pod.volumes = Some(vec![workspace_volume(), docker_config_volume()]);

// Set the security context for the pod
pod.security_context = Some(PodSecurityContext {
run_as_user: Some(1000),
run_as_group: Some(1000),
fs_group: Some(1000),
..Default::default()
});
pod.security_context = Some(PodSecurityContext { fs_group: Some(DEFAULT_RUN_AS_GROUP), ..Default::default() });

Ok(pod)
}
Expand All @@ -220,3 +223,27 @@ fn owner_reference(actor: &Actor) -> Result<String, error::Error> {
.find_map(|owner| (owner.kind == "Playbook").then(|| owner.name.clone()))
.ok_or_else(|| Error::MissingObjectKey(".metadata.ownerReferences"))
}

/// Build SecurityContext for the container by Buildpacks builder mapping.
///
/// |user |group|builder|
/// |-----|-----|-------|
/// |1000 |1000 |heroku*|
/// |1000 |1000 |gcr.io/buildpacks*|
/// |1001 |1000 |paketobuildpacks*|
/// |1001 |1000 |amp-buildpacks*|
/// |1001 |1000 |*|
///
fn security_context(builder: &str) -> Option<SecurityContext> {
let mut run_as_user = DEFAULT_RUN_AS_USER;

if builder.starts_with("heroku") || builder.starts_with("gcr.io/buildpacks") {
run_as_user = 1000;
}

Some(SecurityContext {
run_as_user: Some(run_as_user),
run_as_group: Some(DEFAULT_RUN_AS_GROUP),
..Default::default()
})
}

0 comments on commit cea7a4f

Please sign in to comment.