Skip to content

Commit

Permalink
docker: add capabilities via a bazel rule (#3856)
Browse files Browse the repository at this point in the history
  • Loading branch information
sustrik authored Sep 3, 2020
1 parent 2009432 commit ffea3ce
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 61 deletions.
7 changes: 4 additions & 3 deletions docker/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ container_bundle(
"scion_cs:latest": ":cs_prod",
"scion_dispatcher:latest": ":dispatcher_prod",
"scion_sciond:latest": ":sciond_prod",
"scion_sig_nocap:latest": ":sig_prod",
"scion_sig:latest": ":sig_prod",
},
)

Expand All @@ -21,14 +21,14 @@ container_bundle(
"scion_cs_debug:latest": ":cs_debug",
"scion_dispatcher_debug:latest": ":dispatcher_debug",
"scion_sciond_debug:latest": ":sciond_debug",
"scion_sig_nocap_debug:latest": ":sig_debug",
"scion_sig_debug:latest": ":sig_debug",
},
)

container_bundle(
name = "test",
images = {
"scion_sig_acceptance_nocap:latest": ":scion_sig_acceptance_nocap",
"scion_sig_acceptance:latest": ":scion_sig_acceptance",
"scion_tester:latest": ":scion_tester",
},
)
Expand Down Expand Up @@ -91,6 +91,7 @@ scion_app_images(
name = "sig",
appdir = "/app",
binary = "//go/sig:sig",
caps = "cap_net_admin+ei",
entrypoint = [
"/app/sig",
"--config",
Expand Down
6 changes: 0 additions & 6 deletions docker/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,12 @@ apps: prod test

debug:
bazel run -c dbg //docker:debug
docker image build -t "scion_sig_debug:latest" -f dockerfiles/sig.debug .
docker image remove scion_sig_nocap_debug

prod:
bazel run -c opt //docker:prod
docker image build -t "scion_sig:latest" -f dockerfiles/sig.prod .
docker image remove scion_sig_nocap

test:
bazel run //docker:test
docker build -t "scion_sig_acceptance:latest" -f dockerfiles/sig_accept .
docker image remove scion_sig_acceptance_nocap scionproto/docker-caps

clean:
docker image ls --filter reference=scion* -q | xargs docker image rm
145 changes: 145 additions & 0 deletions docker/caps.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Copyright 2020 Anapaya Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file was initially copied from ocker/docker/util/run.bzl and then adapted,
# see https://github.com/bazelbuild/rules_docker/blob/master/docker/util/run.bzl

"""
Rules to set capabilities on a container. The container must have the `setcap`
binary in it.
"""

def _setcap_impl(ctx):
"""Implementation for the setcap rule.
This rule sets capabilities on a binary in an image and stores the image.
Args:
ctx: The bazel rule context.
"""

name = ctx.attr.name
image = ctx.file.image
script = ctx.outputs.build
output_image_tar = ctx.outputs.out

toolchain_info = ctx.toolchains["@io_bazel_rules_docker//toolchains/docker:toolchain_type"].info

# Generate a shell script to execute the reset cmd
image_utils = ctx.actions.declare_file("image_util.sh")
ctx.actions.expand_template(
template = ctx.file._image_utils_tpl,
output = image_utils,
substitutions = {
"%{docker_flags}": " ".join(toolchain_info.docker_flags),
"%{docker_tool_path}": toolchain_info.tool_path,
},
is_executable = True,
)

# Generate a shell script to execute the setcap statement
ctx.actions.expand_template(
template = ctx.file._run_tpl,
output = script,
substitutions = {
"%{caps}": ctx.attr.caps,
"%{binary}": ctx.attr.binary,
"%{docker_flags}": " ".join(toolchain_info.docker_flags),
"%{docker_tool_path}": toolchain_info.tool_path,
"%{image_id_extractor_path}": ctx.executable._extract_image_id.path,
"%{image_tar}": image.path,
"%{output_image}": "bazel/%s:%s" % (
ctx.label.package or "default",
name,
),
"%{output_tar}": output_image_tar.path,
"%{to_json_tool}": ctx.executable._to_json_tool.path,
"%{util_script}": image_utils.path,
},
is_executable = True,
)

runfiles = [image, image_utils]

ctx.actions.run(
outputs = [output_image_tar],
inputs = runfiles,
executable = script,
tools = [ctx.executable._extract_image_id, ctx.executable._to_json_tool],
use_default_shell_env = True,
)

return struct()

_setcap_attrs = {
"image": attr.label(
doc = "The image without caps set.",
mandatory = True,
allow_single_file = True,
cfg = "target",
),
"caps": attr.string(
doc = "The capabilities to add, (example: cap_net_admin+ei)",
mandatory = True,
),
"binary": attr.string(
doc = "The binary to set the capabilities on, (example: /app/sig)",
mandatory = True,
),
"_extract_image_id": attr.label(
default = Label("@io_bazel_rules_docker//contrib:extract_image_id"),
cfg = "host",
executable = True,
allow_files = True,
),
"_image_utils_tpl": attr.label(
default = "@io_bazel_rules_docker//docker/util:image_util.sh.tpl",
allow_single_file = True,
),
"_run_tpl": attr.label(
default = Label("//docker:setcap.sh.tpl"),
allow_single_file = True,
),
"_to_json_tool": attr.label(
default = Label("@io_bazel_rules_docker//docker/util:to_json"),
cfg = "host",
executable = True,
allow_files = True,
),
}
_setcap_outputs = {
"out": "%{name}.tar",
"build": "%{name}.build",
}

setcap = rule(
attrs = _setcap_attrs,
doc = ("This rules setcap a binary in an image"),
executable = False,
outputs = _setcap_outputs,
implementation = _setcap_impl,
toolchains = ["@io_bazel_rules_docker//toolchains/docker:toolchain_type"],
)
9 changes: 0 additions & 9 deletions docker/dockerfiles/sig.debug

This file was deleted.

9 changes: 0 additions & 9 deletions docker/dockerfiles/sig.prod

This file was deleted.

11 changes: 0 additions & 11 deletions docker/dockerfiles/sig_accept

This file was deleted.

78 changes: 55 additions & 23 deletions docker/scion_app.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
load("@rules_pkg//:pkg.bzl", "pkg_tar")
load("@io_bazel_rules_docker//container:container.bzl", "container_image")
load("@package_bundle//file:packages.bzl", "packages")
load(":caps.bzl", "setcap")

# Defines a common base image for all app images.
def scion_app_base():
Expand Down Expand Up @@ -63,33 +64,64 @@ def scion_app_base():
# appdir - the directory to deploy the binary to
# workdir - working directory
# entrypoint - a list of strings that add up to the command line
# stamp - whether to stamp the created images (default=True).
def scion_app_images(name, binary, appdir, workdir, entrypoint, stamp = True):
# caps - capabilities to set on the binary
def scion_app_images(name, binary, appdir, workdir, entrypoint, caps = None):
pkg_tar(
name = name + "_docker_files",
name = "%s_docker_files" % name,
srcs = [binary],
package_dir = appdir,
mode = "0755",
)

container_image(
name = name + "_prod",
repository = "scion",
base = "//docker:app_base",
tars = [":" + name + "_docker_files"],
workdir = workdir,
entrypoint = ["/sbin/su-exec"] + entrypoint,
stamp = stamp,
visibility = ["//visibility:public"],
scion_app_images_internal(
"prod",
"//docker:app_base",
name,
binary,
appdir,
workdir,
["/sbin/su-exec"] + entrypoint,
caps,
)

container_image(
name = name + "_debug",
repository = "scion",
base = "//docker:app_base_debug",
tars = [":" + name + "_docker_files"],
workdir = workdir,
entrypoint = entrypoint,
stamp = stamp,
visibility = ["//visibility:public"],
scion_app_images_internal(
"debug",
"//docker:app_base_debug",
name,
binary,
appdir,
workdir,
entrypoint,
caps,
)

def scion_app_images_internal(suffix, base, name, binary, appdir, workdir, entrypoint, caps):
if not caps:
container_image(
name = "%s_%s" % (name, suffix),
repository = "scion",
base = base,
tars = [":%s_docker_files" % name],
workdir = workdir,
entrypoint = entrypoint,
visibility = ["//visibility:public"],
)
else:
container_image(
name = "%s_%s_nocap" % (name, suffix),
repository = "scion",
base = base,
tars = [":%s_docker_files" % name],
workdir = workdir,
entrypoint = entrypoint,
)
setcap(
name = "%s_%s_withcap" % (name, suffix),
image = "%s_%s_nocap.tar" % (name, suffix),
caps = caps,
binary = "%s/%s" % (appdir, name),
)
container_image(
name = "%s_%s" % (name, suffix),
base = "%s_%s_withcap.tar" % (name, suffix),
entrypoint = entrypoint,
visibility = ["//visibility:public"],
)
63 changes: 63 additions & 0 deletions docker/setcap.sh.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash

# Copyright 2017 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Copyright 2020 Anapaya Systems
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file was initially copied from rules_docker/docker/util/commit.sh.tpl and then adapted,
# see https://github.com/bazelbuild/rules_docker/blob/master/docker/util/commit.sh.tpl

set -ex

# Setup tools and load utils
TO_JSON_TOOL="%{to_json_tool}"
source %{util_script}

# Resolve the docker tool path
DOCKER="%{docker_tool_path}"
DOCKER_FLAGS="%{docker_flags}"

if [[ -z "$DOCKER" ]]; then
echo >&2 "error: docker not found; do you need to manually configure the docker toolchain?"
exit 1
fi

# Load the image and remember its name
image_id=$(%{image_id_extractor_path} %{image_tar})
$DOCKER $DOCKER_FLAGS load -i %{image_tar}

id=$($DOCKER $DOCKER_FLAGS run -d --entrypoint setcap $image_id %{caps} %{binary})
# Actually wait for the container to finish running its commands
retcode=$($DOCKER $DOCKER_FLAGS wait $id)
# Trigger a failure if the run had a non-zero exit status
if [ $retcode != 0 ]; then
$DOCKER $DOCKER_FLAGS logs $id && false
fi

reset_cmd $image_id $id %{output_image}
$DOCKER $DOCKER_FLAGS save %{output_image} -o %{output_tar}
$DOCKER $DOCKER_FLAGS rm $id
Loading

0 comments on commit ffea3ce

Please sign in to comment.