Skip to content

Commit

Permalink
Add support for signaling an external process via pid file
Browse files Browse the repository at this point in the history
Signed-off-by: Kevin Fox <[email protected]>
  • Loading branch information
kfox1111 authored and faisal-memon committed Sep 24, 2024
1 parent e7fbc8e commit cbe340b
Show file tree
Hide file tree
Showing 23 changed files with 412 additions and 113 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/nightly_build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Nightly Build
on:
schedule:
# Random minute number to avoid GH scheduler stampede
- cron: '37 21 * * *'
workflow_dispatch: {}

jobs:
build-and-publish-images:
runs-on: ubuntu-22.04

permissions:
contents: read
id-token: write
packages: write

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup go
uses: actions/setup-go@v5
with:
go-version: 1.21.5
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Install regctl
uses: regclient/actions/regctl-installer@main
- name: Build image
run: make docker-build
- name: Log in to GHCR
uses: docker/[email protected]
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push images
run: ./.github/workflows/scripts/push-images.sh nightly
68 changes: 68 additions & 0 deletions .github/workflows/scripts/load-oci-archives.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# shellcheck shell=bash
##
## USAGE: __PROG__
##
## "__PROG__" loads oci tarballs created with xbuild into docker.
##
## Usage example(s):
## ./__PROG__
## PLATFORM=linux/arm64 ./__PROG__
##
## Commands
## - ./__PROG__ loads the oci tarball into Docker.

function usage {
grep '^##' "$0" | sed -e 's/^##//' -e "s/__PROG__/$me/" >&2
}

function normalize_path {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
local npath
# Remove first dir/.. sequence.
npath="${path//[^\/][^\/]*\/\.\.\//}"
# Remove remaining dir/.. sequence.
while [[ $npath != "$path" ]] ; do
path=$npath
npath="${path//[^\/][^\/]*\/\.\.\//}"
done
echo "$path"
}

me=$(basename "$0")
BASEDIR=$(dirname "$0")
ROOTDIR="$(normalize_path "$BASEDIR/../../../")"

command -v regctl >/dev/null 2>&1 || { usage; echo -e "\n * The regctl cli is required to run this script." >&2 ; exit 1; }
command -v docker >/dev/null 2>&1 || { usage; echo -e "\n * The docker cli is required to run this script." >&2 ; exit 1; }

# Takes the current platform architecture or plaftorm as defined externally in a platform variable.
# e.g.:
# linux/amd64
# linux/arm64
PLATFORM="${PLATFORM:-local}"
OCI_IMAGES=(
spiffe-helper
)

org_name=$(echo "$GITHUB_REPOSITORY" | tr '/' "\n" | head -1 | tr -d "\n")
org_name="${org_name:-spiffe}" # default to spiffe in case ran on local
registry=ghcr.io/${org_name}

echo "Importing ${OCI_IMAGES[*]} into docker".
for img in "${OCI_IMAGES[@]}"; do
oci_dir="ocidir://${ROOTDIR}oci/${img}"
platform_tar="${img}-${PLATFORM}-image.tar"
image_to_load="${registry}/${img}:devel"

# regclient works with directories rather than tars, so import the OCI tar to a directory
regctl image import "$oci_dir" "${img}-image.tar"
dig="$(regctl image digest --platform "$PLATFORM" "$oci_dir")"
# export the single platform image using the digest
regctl image export "$oci_dir@${dig}" "${platform_tar}"

docker load < "${platform_tar}"
docker image tag "localhost/oci/${img}:latest" "${image_to_load}"
docker image rm "localhost/oci/${img}:latest"
done
62 changes: 62 additions & 0 deletions .github/workflows/scripts/push-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# shellcheck shell=bash
##
## USAGE: __PROG__
##
## "__PROG__" publishes images to a registry.
##
## Usage example(s):
## ./__PROG__ 1.5.2
## ./__PROG__ v1.5.2
## ./__PROG__ refs/tags/v1.5.2
##
## Commands
## - ./__PROG__ <version> pushes images to the registry using given version.

set -e

function usage {
grep '^##' "$0" | sed -e 's/^##//' -e "s/__PROG__/$me/" >&2
}

function normalize_path {
# Remove all /./ sequences.
local path=${1//\/.\//\/}
local npath
# Remove first dir/.. sequence.
npath="${path//[^\/][^\/]*\/\.\.\//}"
# Remove remaining dir/.. sequence.
while [[ $npath != "$path" ]] ; do
path=$npath
npath="${path//[^\/][^\/]*\/\.\.\//}"
done
echo "$path"
}

me=$(basename "$0")
BASEDIR=$(dirname "$0")
ROOTDIR="$(normalize_path "$BASEDIR/../../../")"

version="$1"
# remove the git tag prefix
# Push the images using the version tag (without the "v" prefix).
# Also strips the refs/tags part if the GITHUB_REF variable is used.
version="${version#refs/tags/v}"
version="${version#v}"

if [ -z "${version}" ]; then
usage
echo "version not provided!" 1>&2
exit 1
fi

image=spiffe-helper
org_name=$(echo "$GITHUB_REPOSITORY" | tr '/' "\n" | head -1 | tr -d "\n")
org_name="${org_name:-spiffe}" # default to spiffe in case ran outside of GitHub actions
registry=ghcr.io/${org_name}
image_to_push="${registry}/${image}:${version}"
oci_dir="ocidir://${ROOTDIR}oci/${image}"

echo "Pushing ${image_to_push}."
regctl image import "${oci_dir}" "${image}-image.tar"
regctl image copy "${oci_dir}" "${image_to_push}"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ rpm/*.rpm
*.swp
*.swo
bootstrap.crt

# oci image builds
oci/
*-image.tar
40 changes: 40 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Build the spiffe-helper binary
ARG go_version
FROM --platform=$BUILDPLATFORM golang:${go_version}-alpine as base
WORKDIR /workspace

# Cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
COPY go.* ./
RUN --mount=type=cache,target=/go/pkg/mod go mod download

# Copy the go source
COPY cmd/spiffe-helper/main.go cmd/spiffe-helper/main.go
COPY pkg/ pkg/

# xx is a helper for cross-compilation
# when bumping to a new version analyze the new version for security issues
# then use crane to lookup the digest of that version so we are immutable
# crane digest tonistiigi/xx:1.3.0
FROM --platform=${BUILDPLATFORM} tonistiigi/xx@sha256:904fe94f236d36d65aeb5a2462f88f2c537b8360475f6342e7599194f291fb7e AS xx

FROM --platform=${BUILDPLATFORM} base as builder
ARG TARGETPLATFORM
ARG TARGETARCH

ENV CGO_ENABLED=0
COPY --link --from=xx / /
RUN xx-go --wrap
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go build -o bin/spiffe-helper cmd/spiffe-helper/main.go

# Use distroless as minimal base image to package the manager binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
#FROM gcr.io/distroless/static:nonroot
FROM gcr.io/distroless/static AS spiffe-helper
WORKDIR /
COPY --link --from=builder /workspace/bin/spiffe-helper /spiffe-helper

ENTRYPOINT ["/spiffe-helper"]
CMD []
32 changes: 29 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export GO111MODULE=on
DIR := ${CURDIR}
PLATFORMS ?= linux/amd64,linux/arm64

E:=@
ifeq ($(V),1)
Expand Down Expand Up @@ -41,6 +42,15 @@ help:
@echo "For verbose output set V=1"
@echo " for example: $(cyan)make V=1 build$(reset)"

# Used to force some rules to run every time
.PHONY: FORCE
FORCE: ;

# CONTAINER_TOOL defines the container tool to be used for building images.
# Be aware that the target commands are only tested with Docker which is
# scaffolded by default. However, you might want to replace it to use other
# tools. (i.e. podman)
CONTAINER_TOOL ?= docker

############################################################################
# OS/ARCH detection
Expand Down Expand Up @@ -90,7 +100,7 @@ else
endif
go_path := PATH="$(go_bin_dir):$(PATH)"

golangci_lint_version = v1.51.1
golangci_lint_version = v1.52.2
golangci_lint_dir = $(build_dir)/golangci_lint/$(golangci_lint_version)
golangci_lint_bin = $(golangci_lint_dir)/golangci-lint
golangci_lint_cache = $(golangci_lint_dir)/cache
Expand Down Expand Up @@ -167,10 +177,26 @@ lint-code: $(golangci_lint_bin) | go-check
# Build targets
############################################################################

.PHONY: build test clean distclean artifact tarball rpm
.PHONY: build test clean distclean artifact tarball rpm docker-build container-builder load-images

build: | go-check
go build -o spiffe-helper${exe} ./cmd/spiffe-helper
CGO_ENABLED=0 go build -o spiffe-helper${exe} ./cmd/spiffe-helper

docker-build: $(addsuffix -image.tar, spiffe-helper) ## Build docker image with spiffe-helper.

container-builder: ## Create a buildx node to create crossplatform images.
$(CONTAINER_TOOL) buildx create --platform $(PLATFORMS) --name container-builder --node container-builder0 --use

spiffe-helper-image.tar: Dockerfile FORCE | container-builder
$(CONTAINER_TOOL) buildx build \
--platform $(PLATFORMS) \
--build-arg go_version=$(go_version) \
--target spiffe-helper \
-o type=oci,dest=$@ \
.

load-images: $(addsuffix -image.tar,$(BINARIES)) ## Load the image for your current PLATFORM into docker from the cross-platform oci tar.
./.github/workflows/scripts/load-oci-archives.sh

artifact: tarball rpm

Expand Down
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,29 @@ The SPIFFE Helper is a simple utility for fetching X.509 SVID certificates from

If `-config` is not specified, the default value `helper.conf` is assumed.

The flag `-exitWhenReady` is also supported.

## Configuration
The configuration file is an [HCL](https://github.com/hashicorp/hcl) formatted file that defines the following configurations:

| Configuration | Description | Example Value |
|-----------------------------|----------------------------------------------------------------------------------------------------------------| -------------------- |
|`agent_address` | Socket address of SPIRE Agent. | `"/tmp/agent.sock"` |
|`cmd` | The path to the process to launch. | `"ghostunnel"` |
|`cmd_args` | The arguments of the process to launch. | `"server --listen localhost:8002 --target localhost:8001--keystore certs/svid_key.pem --cacert certs/svid_bundle.pem --allow-uri-san spiffe://example.org/Database"` |
|`cert_dir` | Directory name to store the fetched certificates. This directory must be created previously. | `"certs"` |
|`add_intermediates_to_bundle`| Add intermediate certificates into Bundle file instead of SVID file. | `true` |
|`renew_signal` | The signal that the process to be launched expects to reload the certificates. It is not supported on Windows. | `"SIGUSR1"` |
|`svid_file_name` | File name to be used to store the X.509 SVID public certificate in PEM format. | `"svid.pem"` |
|`svid_key_file_name` | File name to be used to store the X.509 SVID private key and public certificate in PEM format. | `"svid_key.pem"` |
|`svid_bundle_file_name` | File name to be used to store the X.509 SVID Bundle in PEM format. | `"svid_bundle.pem"` |
|`jwt_audience` | JWT SVID audience. | `"your-audience"` |
|`jwt_svid_file_name` | File name to be used to store JWT SVID in Base64-encoded string. | `"jwt_svid.token"` |
|`jwt_bundle_file_name` | File name to be used to store JWT Bundle in JSON format. | `"jwt_bundle.json"` |
| Configuration | Description | Example Value |
|-------------------------------|----------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `agent_address` | Socket address of SPIRE Agent. | `"/tmp/agent.sock"` |
| `cmd` | The path to the process to launch. | `"ghostunnel"` |
| `cmd_args` | The arguments of the process to launch. | `"server --listen localhost:8002 --target localhost:8001--keystore certs/svid_key.pem --cacert certs/svid_bundle.pem --allow-uri-san spiffe://example.org/Database"` |
| `cert_dir` | Directory name to store the fetched certificates. This directory must be created previously. | `"certs"` |
| `exit_when_ready` | Fetch x509 certificate and then exit(0) | `true` |
| `add_intermediates_to_bundle` | Add intermediate certificates into Bundle file instead of SVID file. | `true` |
| `renew_signal` | The signal that the process to be launched expects to reload the certificates. It is not supported on Windows. | `"SIGUSR1"` |
| `svid_file_name` | File name to be used to store the X.509 SVID public certificate in PEM format. | `"svid.pem"` |
| `svid_key_file_name` | File name to be used to store the X.509 SVID private key and public certificate in PEM format. | `"svid_key.pem"` |
| `svid_bundle_file_name` | File name to be used to store the X.509 SVID Bundle in PEM format. | `"svid_bundle.pem"` |
| `jwt_svids` | An array with the audience and file name to store the JWT SVIDs. File is Base64-encoded string). | `[{jwt_audience="your-audience", jwt_svid_file_name="jwt_svid.token"}]` |
| `jwt_bundle_file_name` | File name to be used to store JWT Bundle in JSON format. | `"jwt_bundle.json"` |

### Configuration example
```
agent_address = "/tmp/agent.sock"
agent_address = "/tmp/spire-agent/public/api.sock"
cmd = "ghostunnel"
cmd_args = "server --listen localhost:8002 --target localhost:8001 --keystore certs/svid_key.pem --cacert certs/svid_bundle.pem --allow-uri-san spiffe://example.org/Database"
cert_dir = "certs"
Expand All @@ -56,4 +58,4 @@ svid_bundle_file_name = "svid_bundle.pem"
jwt_audience = "your-audience"
jwt_svid_file_name = "jwt.token"
jwt_bundle_file_name = "bundle.json"
```
```
7 changes: 4 additions & 3 deletions cmd/spiffe-helper/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,25 @@ func main() {
// 2. Run Sidecar's Daemon

configFile := flag.String("config", "helper.conf", "<configFile> Configuration file path")
exitWhenReady := flag.Bool("exitWhenReady", false, "Exit once the requested objects are retrieved")
flag.Parse()

log := logrus.WithField("system", "spiffe-helper")
log.Infof("Using configuration file: %q\n", *configFile)

if err := startSidecar(*configFile, log); err != nil {
if err := startSidecar(*configFile, *exitWhenReady, log); err != nil {
log.WithError(err).Error("Exiting due this error")
os.Exit(1)
}

log.Infof("Exiting")
}

func startSidecar(configPath string, log logrus.FieldLogger) error {
func startSidecar(configPath string, exitWhenReady bool, log logrus.FieldLogger) error {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()

spiffeSidecar, err := sidecar.New(configPath, log)
spiffeSidecar, err := sidecar.New(configPath, exitWhenReady, log)
if err != nil {
return fmt.Errorf("Failed to create sidecar: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/mysql/helper.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# SPIRE agent unix socket path
agent_address = "/tmp/agent.sock"
agent_address = "/tmp/spire-agent/public/api.sock"

# mysql binary path
cmd = "/usr/bin/mysql"
Expand Down
2 changes: 1 addition & 1 deletion examples/mysql/spire-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ agent {
log_level = "DEBUG"
server_address = "127.0.0.1"
server_port = "8081"
socket_path ="/tmp/agent.sock"
socket_path ="/tmp/spire-agent/public/api.sock"
trust_bundle_path = "./conf/agent/dummy_root_ca.crt"
trust_domain = "example.org"
}
Expand Down
2 changes: 1 addition & 1 deletion examples/postgresql/helper.conf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#

# SPIRE agent unix socket path
agent_address = "/tmp/agent.sock"
agent_address = "/tmp/spire-agent/public/api.sock"

# psql binary path
cmd = "/usr/bin/psql"
Expand Down
2 changes: 1 addition & 1 deletion examples/postgresql/spire-agent.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ agent {
log_level = "DEBUG"
server_address = "127.0.0.1"
server_port = "8081"
socket_path ="/tmp/agent.sock"
socket_path ="/tmp/spire-agent/public/api.sock"
trust_bundle_path = "./conf/agent/dummy_root_ca.crt"
trust_domain = "example.org"
}
Expand Down
Loading

0 comments on commit cbe340b

Please sign in to comment.