From 18b3ed9b51bc4dd6a8ac1c352bc6f4faf8d6782a Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Tue, 25 Aug 2020 17:13:40 +0100 Subject: [PATCH 1/4] Add initial core-plans/dex Signed-off-by: Gavin Didrichsen --- config/config.yml | 22 ++++++++++++++++++++ default.toml | 8 ++++++++ hooks/init | 6 ++++++ hooks/run | 8 ++------ plan.sh | 50 ++++++++++++++++++++++++++++++++++++++++++++++ tests/helpers.bash | 24 ++++++++++++++++++++++ tests/test.bats | 20 +++++++++++++++++++ tests/test.sh | 39 ++++++++++++++++++++++++++++++++++++ 8 files changed, 171 insertions(+), 6 deletions(-) create mode 100644 config/config.yml create mode 100644 default.toml create mode 100644 hooks/init create mode 100644 plan.sh create mode 100644 tests/helpers.bash create mode 100644 tests/test.bats create mode 100755 tests/test.sh diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000..972238f --- /dev/null +++ b/config/config.yml @@ -0,0 +1,22 @@ +logger: + level: {{cfg.log.level}} + format: {{cfg.log.format}} + +issuer: https://{{cfg.service.fqdn}}/dex + +storage: + type: sqlite3 + config: + file: {{pkg.svc_var_path}}/dex.db + +frontend: + dir: {{pkg.svc_static_path}}/web + theme: coreos + +web: + http: {{cfg.service.host}}:{{cfg.service.port}} + +connectors: +- type: mockCallback + id: mock + name: Example diff --git a/default.toml b/default.toml new file mode 100644 index 0000000..07b07f6 --- /dev/null +++ b/default.toml @@ -0,0 +1,8 @@ +[log] +level = "INFO" +format = "text" # can also be "json" + +[service] +fqdn = "localhost" +host = "0.0.0.0" +port = 5556 diff --git a/hooks/init b/hooks/init new file mode 100644 index 0000000..f52ba2b --- /dev/null +++ b/hooks/init @@ -0,0 +1,6 @@ +#!/bin/sh + +exec 2>&1 + +# put static files into place +cp -r {{pkg.path}}/web {{pkg.svc_static_path}} diff --git a/hooks/run b/hooks/run index 70e8ecf..9aa66b4 100644 --- a/hooks/run +++ b/hooks/run @@ -1,8 +1,4 @@ -#!/bin/bash +#!/bin/sh exec 2>&1 - -while true; do - echo "Sleeping ..." - sleep 10 -done +exec dex serve {{pkg.svc_config_path}}/config.yml diff --git a/plan.sh b/plan.sh new file mode 100644 index 0000000..9e7192b --- /dev/null +++ b/plan.sh @@ -0,0 +1,50 @@ +gopkg="github.com/dexidp/dex" +pkg_name=dex +pkg_description="OpenID Connect Identity (OIDC) and OAuth 2.0 Provider with Pluggable Connectors" +pkg_origin=core +pkg_version="2.24.0" +pkg_maintainer="Chef Software Inc. " +pkg_license=("Apache-2.0") +pkg_source="https://$gopkg" +pkg_upstream_url=$pkg_source +pkg_exports=( + [port]=service.port + [host]=service.host +) +pkg_deps=(core/glibc) +pkg_build_deps=(core/go core/git core/gcc) +pkg_bin_dirs=(bin) + +do_before() { + GOPATH=$HAB_CACHE_SRC_PATH/$pkg_dirname + export GOPATH +} + +do_prepare() { + export GO_LDFLAGS="-w -X $gopkg/version.Version=v$pkg_version" +} + +do_download() { + return 0 +} + +do_verify() { + return 0 +} + +# Use unpack instead of download, so that plan-build can manage the +# source path. This ensures us a clean checkout every time we build. +do_unpack() { + git clone "$pkg_source" "$GOPATH/src/$gopkg" + ( cd "$GOPATH/src/$gopkg" || exit + git reset --hard "v$pkg_version" + ) +} + +do_build() { + go build --ldflags "${GO_LDFLAGS}" -o "$pkg_prefix/bin/dex" "$gopkg/cmd/dex" +} + +do_install() { + cp -r "$GOPATH/src/$gopkg/web" "$pkg_prefix" +} diff --git a/tests/helpers.bash b/tests/helpers.bash new file mode 100644 index 0000000..360cee0 --- /dev/null +++ b/tests/helpers.bash @@ -0,0 +1,24 @@ +# Usage: test_listen [wait-duration] +# protocol: tcp or udp +# port: int +# wait-duration: time in seconds +test_listen() { + local proto="-z" + if [ "${1}" == "udp" ]; then + proto="-u" + fi + local wait=${3:-3} + nc "${proto}" -w"${wait}" 127.0.0.1 "${2}" + return $? +} + +wait_listen() { + local proto="-z" + if [ "${1}" == "udp" ]; then + proto="-u" + fi + local wait=${3:-1} + while ! nc "${proto}" -w"${wait}" 127.0.0.1 "${2}"; do + sleep 1 + done +} diff --git a/tests/test.bats b/tests/test.bats new file mode 100644 index 0000000..a96834e --- /dev/null +++ b/tests/test.bats @@ -0,0 +1,20 @@ +source "${BATS_TEST_DIRNAME}/../plan.sh" +load helpers + +@test "Port Listen TCP/5556" { + test_listen tcp 5556 + [ "$?" -eq 0 ] +} + +@test "/dex/healthz endpoint returns success" { + curl -f http://127.0.0.1:5556/dex/healthz +} + +@test "OpenID Connect discovery document is returned with issuer set" { + jq -ne --argjson doc "$(curl http://127.0.0.1:5556/dex/.well-known/openid-configuration)" \ + '$doc.issuer | test("https://localhost/dex")' +} + +@test "'dex version' returns the correct version" { + hab pkg exec "${TEST_PKG_IDENT}" dex version | grep -q "dex Version: v${pkg_version}" +} diff --git a/tests/test.sh b/tests/test.sh new file mode 100755 index 0000000..a0f47d5 --- /dev/null +++ b/tests/test.sh @@ -0,0 +1,39 @@ +#!/bin/sh +#/ Usage: test.sh +#/ +#/ Example: test.sh core/dex/2.17.0/20190531065119 +#/ + +set -euo pipefail + +if [[ -z "${1:-}" ]]; then + grep '^#/' < "${0}" | cut -c4- + exit 1 +fi + +TESTDIR="$(dirname "${0}")" +source "${TESTDIR}/helpers.bash" + +export TEST_PKG_IDENT="${1}" +hab pkg install "${TEST_PKG_IDENT}" + +hab pkg install core/busybox-static +hab pkg binlink core/busybox-static nc +hab pkg install core/bats core/curl core/jq-static --binlink + +# if the supervisor isn't running, start it +if ! hab sup status 2>/dev/null; then + hab sup run & +fi + +echo "Waiting for supervisor to start (30s)" +wait_listen tcp 9632 30 +hab svc load "${TEST_PKG_IDENT}" + +# Wait for 5 seconds on first check, to ensure service is up. +echo "Waiting for service to start (5s)" +wait_listen tcp 5556 5 + +bats "${TESTDIR}/test.bats" + +hab svc unload "${TEST_PKG_IDENT}" || true From e4657be873783cbfe46ac1feff7fdbe284c845a0 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Tue, 25 Aug 2020 17:16:48 +0100 Subject: [PATCH 2/4] Add inspec tests Signed-off-by: Gavin Didrichsen --- .gitignore | 3 +- attributes.yml | 2 ++ botanist.yml | 4 +++ controls/dex_exists.rb | 28 +++++++++++++++++++ controls/dex_habservice_works.rb | 48 ++++++++++++++++++++++++++++++++ controls/dex_works.rb | 32 +++++++++++++++++++++ inspec.yml | 8 +++--- 7 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 botanist.yml create mode 100644 controls/dex_exists.rb create mode 100644 controls/dex_habservice_works.rb create mode 100644 controls/dex_works.rb diff --git a/.gitignore b/.gitignore index 1a06816..37d31da 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -results +results/ +inspec.lock \ No newline at end of file diff --git a/attributes.yml b/attributes.yml index e69de29..4bdf868 100644 --- a/attributes.yml +++ b/attributes.yml @@ -0,0 +1,2 @@ +plan_name: 'dex' +listening_port: '5556' diff --git a/botanist.yml b/botanist.yml new file mode 100644 index 0000000..6de45ba --- /dev/null +++ b/botanist.yml @@ -0,0 +1,4 @@ +--- +owners: + - "@smacfarlane" + - "@habitat-sh/habitat-core-plans-maintainers" \ No newline at end of file diff --git a/controls/dex_exists.rb b/controls/dex_exists.rb new file mode 100644 index 0000000..3e102e4 --- /dev/null +++ b/controls/dex_exists.rb @@ -0,0 +1,28 @@ +title 'Tests to confirm dex exists' + +plan_origin = ENV['HAB_ORIGIN'] +plan_name = input('plan_name', value: 'dex') + +control 'core-plans-dex-exists' do + impact 1.0 + title 'Ensure dex exists' + desc ' + Verify dex by ensuring bin/dex + (1) exists and + (2) is executable' + + plan_installation_directory = command("hab pkg path #{plan_origin}/#{plan_name}") + describe plan_installation_directory do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stderr') { should be_empty } + end + + ["dex"].each do |binary_name| + command_full_path = File.join(plan_installation_directory.stdout.strip, "bin", binary_name) + describe file(command_full_path) do + it { should exist } + it { should be_executable } + end + end +end diff --git a/controls/dex_habservice_works.rb b/controls/dex_habservice_works.rb new file mode 100644 index 0000000..79f0796 --- /dev/null +++ b/controls/dex_habservice_works.rb @@ -0,0 +1,48 @@ +title 'Tests to confirm dex habitat service works as expected' + +plan_origin = ENV['HAB_ORIGIN'] +plan_name = input('plan_name', value: 'dex') + +control 'core-plans-dex-habservice-works' do + impact 1.0 + title 'Ensure dex habitat service works as expected' + desc ' + Verify dex habitat service by ensuring that + (1) the default.dex habitat service is "up"; + (2) the dex process is LISTENing on the expected port. Note the + regex that detects the LISTENing works in both a habitat studio environment + and a docker one. In studio the program is displayed as "1234/dex"; + whereas in docker as "-". + ' + + plan_installation_directory = command("hab pkg path #{plan_origin}/#{plan_name}") + describe plan_installation_directory do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stderr') { should be_empty } + end + + plan_pkg_ident = ((plan_installation_directory.stdout.strip).match /(?<=pkgs\/)(.*)/)[1] + describe command("hab svc status") do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stdout') { should match /(?#{plan_pkg_ident})\s+(?standalone)\s+(?up)\s+(?up)/ } + its('stderr') { should be_empty } + end + + netstat_installation_directory = command("hab pkg path core/busybox-static") + describe netstat_installation_directory do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stderr') { should be_empty } + end + + netstat_fullpath = File.join(netstat_installation_directory.stdout.strip, "bin/netstat" ) + listening_port=input('listening_port', value: '5556') + describe command("#{netstat_fullpath} -peanut") do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stdout') { should match /:(?#{listening_port}).*LISTEN\s+(?-|\d+\/dex)/ } + its('stderr') { should be_empty } + end +end diff --git a/controls/dex_works.rb b/controls/dex_works.rb new file mode 100644 index 0000000..5ab8f90 --- /dev/null +++ b/controls/dex_works.rb @@ -0,0 +1,32 @@ +title 'Tests to confirm dex works as expected' + +plan_origin = ENV['HAB_ORIGIN'] +plan_name = input('plan_name', value: 'dex') + +control 'core-plans-dex-works' do + impact 1.0 + title 'Ensure dex works as expected' + desc ' + Verify dex by ensuring that + (1) its installation directory exists + (2) it returns the expected version + ' + + plan_installation_directory = command("hab pkg path #{plan_origin}/#{plan_name}") + describe plan_installation_directory do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stderr') { should be_empty } + end + + plan_pkg_version = plan_installation_directory.stdout.split("/")[5] + ["dex"].each do |binary_name| + command_full_path = File.join(plan_installation_directory.stdout.strip, "bin", binary_name) + describe command("#{command_full_path} version") do + its('exit_status') { should eq 0 } + its('stdout') { should_not be_empty } + its('stdout') { should match /dex Version:\s+v#{plan_pkg_version}/ } + its('stderr') { should be_empty } + end + end +end \ No newline at end of file diff --git a/inspec.yml b/inspec.yml index fd5a74c..a228865 100644 --- a/inspec.yml +++ b/inspec.yml @@ -1,7 +1,7 @@ -name: {plan} -title: Habitat Core Plan {plan} +name: dex +title: Habitat Core Plan dex maintainer: "The Core Planners " -summary: InSpec controls for testing Habitat Core Plan {plan} +summary: InSpec controls for testing Habitat Core Plan dex version: 0.1.0 license: Apache-2.0 -inspec_version: '>= 4.18.108' +inspec_version: '>= 4.18.108' \ No newline at end of file From 5edc806e4905146d53496f2cffc9c11943865c7b Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Tue, 25 Aug 2020 17:56:21 +0100 Subject: [PATCH 3/4] Add documentation Signed-off-by: Gavin Didrichsen --- README.md | 104 +++++++++++++++++++++++++++++++++++++++++++- azure-pipelines.yml | 2 +- 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 58a5d2c..6ee1df6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,102 @@ -# base-plan-skeleton -Template for all new Chef Base Plans to simplify creation of repositories. +[![Build Status](https://dev.azure.com/chefcorp-partnerengineering/Chef%20Base%20Plans/_apis/build/status/chef-base-plans.dex?branchName=master)](https://dev.azure.com/chefcorp-partnerengineering/Chef%20Base%20Plans/_build/latest?definitionId=206&branchName=master) + +# dex + +Dex is an identity service that uses OpenID Connect to drive authentication for other apps. Dex acts as a portal to other identity providers through "connectors." See [documentation](https://github.com/dexidp/dex) + +## Maintainers + +* The Core Planners: + +## Type of Package + +Service package + +### Use as Dependency + +Service packages can be set as runtime (as well as build time) dependencies. See [Defining your dependencies](https://www.habitat.sh/docs/developing-packages/developing-packages/#sts=Define%20Your%20Dependencies) for more information. + +To add core/dex as a runtime dependency, you can add the following to your plan file. + +> pkg_deps=(core/dex) + +If for some reason, this package is only needed as a build time dependency then do not include it in pkg_deps, but only in pkg_build_deps: + +> pkg_build_deps=(core/dex) + +### Using dex as a habitat service + +Simple usage from a hab studio: + +```bash +$ # build and install dex +$ build . +$ source ./results/last_build.env +$ hab pkg install ./results/$pkg_artifact +$ +$ # export and run as a docker container +$ hab pkg install core/docker core/hab-pkg-export-docker +$ hab pkg export docker ./results/$pkg_artifact +$ hab pkg exec core/docker docker run --name core_dex -p 9000:9000 --rm -de HAB_LICENSE=accept core/dex +``` + +For more information see: + +* [Running Chef Habitat Packages](https://www.habitat.sh/docs/using-habitat/using-packages/) for more information. +* [Service Groups](https://www.habitat.sh/docs/using-habitat/service-groups/) +* [Topologies](https://www.habitat.sh/docs/using-habitat/topologies/) +* [Update Strategy](https://www.habitat.sh/docs/using-habitat/using-updates/) +* [Binds and Exports](https://www.habitat.sh/docs/developing-packages/#runtime-binds-and-exports) + +## Further development of the dex plan + +The hab studio provides an excellent way to further develop and or troubleshoot the dex hab service. Since dex provides an http endpoint on port 5556, then enter a hab studio with the following command. + +``HAB_DOCKER_OPTS="-p 5556:5556" hab studio enter -D`` + +Once in the studio, then build and load the service: + +```bash +build . +source ./results/last_build.env +hab pkg install ./results/$pkg_artifact +hab svc load $pkg_ident +``` + +Verify the http endpoint, e.g., open a browser at http://localhost:5556/ + +For example: + +```bash +# build dex +[21][default:/src/dex:0]# build . +building artifact +... +... +'/hab/cache/artifacts/core-dex-2.24.0-20200826103442-x86_64-linux.hart' -> '/src/dex/results/core-dex-2.24.0-20200826103442-x86_64-linux.hart' + dex: hab-plan-build cleanup + dex: + dex: Source Path: /hab/cache/src/dex-2.24.0 + dex: Installed Path: /hab/pkgs/core/dex/2.24.0/20200826103442 + dex: Artifact: /src/dex/results/core-dex-2.24.0-20200826103442-x86_64-linux.hart + dex: Build Report: /src/dex/results/last_build.env + dex: SHA256 Checksum: 86d51165eadc82d9bd29a59dad5f2289830f99b78eb8b4b5bc0754977170c3fd + dex: Blake2b Checksum: 6cbd551048d0e73aa21d7636b2d697740fa14da449242b6a1c5e7bf291afed0b + dex: + dex: I love it when a plan.sh comes together. + dex: + dex: Build time: 0m33s + +# install dex +[22][default:/src/dex:0]# source ./results/last_build.env +[23][default:/src/dex:0]# hab pkg install ./results/$pkg_artifact + +# load as a habitat service +[25][default:/src/dex:0]# hab svc load $pkg_ident +The core/dex/2.24.0/20200826103442 service was successfully loaded + +# verify that the service is running +[26][default:/src/dex:0]# hab svc status +package type desired state elapsed (s) pid group +core/dex/2.24.0/20200826103442 standalone up down 2 dex.default +``` diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b163137..f505c87 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,4 +15,4 @@ resources: # Execute the stages from the main pipeline template stages: - - template: azure-pipelines.yml@plan_builder + - template: azure-pipelines-package-install.yml@plan_builder From 353317ce2c6bb52d279af9d2e6ba31c400c7e504 Mon Sep 17 00:00:00 2001 From: Gavin Didrichsen Date: Wed, 26 Aug 2020 11:48:26 +0100 Subject: [PATCH 4/4] Make azdo build and export this as docker since dex is a habitat service Signed-off-by: Gavin Didrichsen --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f505c87..b163137 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,4 +15,4 @@ resources: # Execute the stages from the main pipeline template stages: - - template: azure-pipelines-package-install.yml@plan_builder + - template: azure-pipelines.yml@plan_builder