Skip to content

Commit

Permalink
Add IAM runtime implementation (#1)
Browse files Browse the repository at this point in the history
This commit adds a proper implementation of iam-runtime-infratographer
to the repository. As written, this makes use of identity-api for
validating credentials and permissions-api for checking access to
resources.

Signed-off-by: John Schaeffer <[email protected]>
  • Loading branch information
jnschaeffer authored Feb 14, 2024
1 parent ed51625 commit e9a832e
Show file tree
Hide file tree
Showing 32 changed files with 1,251 additions and 23 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @infratographer/the_committee @infratographer/identity_maintainers
38 changes: 38 additions & 0 deletions .github/workflows/image-main-latest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Publish main-latest image
on:
push:
branches:
- main

jobs:
goreleaser:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Log in to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin

- uses: actions/checkout@v4
with:
fetch-depth: 0

- run: git fetch --force --tags

- uses: actions/setup-go@v4
with:
go-version-file: go.mod

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser-pro
version: latest
args: release --nightly --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
38 changes: 38 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Release
on:
push:
tags:
- v**

jobs:
goreleaser:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Log in to GHCR
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin

- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- run: git fetch --force --tags

- uses: actions/setup-go@v4
with:
go-version-file: go.mod

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser-pro
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
33 changes: 33 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: Lint and test

# Run for all pushes to main and pull requests
on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
lint-and-test:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: "1.20"

- name: Run go tests and generate coverage report
run: make test

- name: Upload coverage report
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.out
flags: unittests
name: codecov-umbrella
24 changes: 3 additions & 21 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

# Go workspace file
go.work
*~
coverage.out
bin/*
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM gcr.io/distroless/static:nonroot

# `nonroot` coming from distroless
USER 65532:65532

# pass in name as --build-arg
ARG NAME

COPY ./bin/${NAME} /app

# Run the web service on container startup.
ENTRYPOINT ["/app"]
CMD ["serve"]
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2024 The Infratographer Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
all: lint test
PHONY: test coverage lint golint clean vendor docker-up docker-down unit-test
GOOS=linux
# use the working dir as the app name, this should be the repo name
APP_NAME=$(shell basename $(CURDIR))

test: | lint
@echo Running tests...
@go test -mod=readonly -race -coverprofile=coverage.out -covermode=atomic ./...

lint:
@echo Linting Go files...
@golangci-lint run --modules-download-mode=readonly

build:
@CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o bin/${APP_NAME}

go-dependencies:
@go mod download
@go mod tidy
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,76 @@
# iam-runtime-infratographer
# iam-runtime-infratographer - An IAM runtime using the Infratographer IAM stack

iam-runtime-infratographer is an implementation of [IAM runtime][iam-runtime] that uses [identity-api][identity-api] for authenticating subjects and [permissions-api][permissions-api] for checking access to resources. This allows applications to make use of Infratographer IAM functionality without needing to include dependencies directly in application code or mock services in development.

[iam-runtime]: https://github.com/metal-toolbox/iam-runtime
[identity-api]: https://github.com/infratographer/identity-api
[permissions-api]: https://github.com/infratographer/permissions-api

## Usage

iam-runtime-infratographer can be run as a standalone binary or a sidecar in a Kubernetes deployment.

To run it as a standalone binary using the provided example config, use the following commands:

```
$ go build -mod=readonly -o bin/ .
$ ./bin/iam-runtime-infratographer serve --config config.example.yaml
```

## Configuration

iam-runtime-infratographer can be configured using either a config file, command line arguments, or environment variables. An example config file is located at config.example.yaml.

## Example Kubernetes deployment

Below provides an example of adding the IAM runtime as a sidecar to your app deployment.

```yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: iam-runtime-config
data:
config.yaml: |
server:
socketpath: /var/iam-runtime/runtime.sock
permissions:
host: permissions-api.internal.enterprise.net
jwt:
jwksuri: https://iam.example.com/jwks.json
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: app
image: example/my-app:latest
volumeMounts:
- name: iam-runtime-socket
mountPath: /var/iam-runtime/
- name: iam-runtime
image: ghcr.io/infratographer/iam-runtime-infratographer:v0.1.0
volumeMounts:
- name: iam-runtime-config
mountPath: /etc/iam-runtime-infratographer/
- name: iam-runtime-socket
mountPath: /var/iam-runtime/
volumes:
- name: iam-runtime-config
configMap:
name: iam-runtime-config
- name: iam-runtime-socket
emptyDir: {}
```
105 changes: 105 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Package cmd is our cobra/viper cli implementation
package cmd

import (
"strings"

"go.infratographer.com/iam-runtime-infratographer/internal/config"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.uber.org/zap"
)

const appName = "iam-runtime-equinix"

var (
cfgFile string
appConfig config.Config
logger *zap.SugaredLogger
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: appName,
Short: "Infratographer IAM runtime",
Long: "iam-runtime-infratographer is an IAM runtime implementation that uses the Infratographer IAM stack for authentication and authorization.",
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
cobra.CheckErr(rootCmd.Execute())
}

func init() {
cobra.OnInitialize(initConfig)

rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is /etc/"+appName+"/config.yaml)")

rootCmd.PersistentFlags().Bool("debug", false, "enable debug logging")
viperBindFlag("logging.debug", rootCmd.PersistentFlags().Lookup("debug"))

rootCmd.PersistentFlags().Bool("pretty", false, "enable pretty (human readable) logging output")
viperBindFlag("logging.pretty", rootCmd.PersistentFlags().Lookup("pretty"))
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath("/etc/" + appName + "/")
viper.SetConfigName("config")
}

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))

viper.SetEnvPrefix("iamruntime")

viper.AutomaticEnv() // read in environment variables that match

setupLogging()

// If a config file is found, read it in.
err := viper.ReadInConfig()
if err == nil {
logger.Infow("using config file",
"file", viper.ConfigFileUsed(),
)
}

if err := viper.Unmarshal(&appConfig); err != nil {
logger.Fatalw("unable to process app config", "error", err.Error())
}
}

func setupLogging() {
cfg := zap.NewProductionConfig()
if viper.GetBool("logging.pretty") {
cfg = zap.NewDevelopmentConfig()
}

if viper.GetBool("logging.debug") {
cfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
} else {
cfg.Level = zap.NewAtomicLevelAt(zap.InfoLevel)
}

l, err := cfg.Build()
if err != nil {
panic(err)
}

logger = l.Sugar().With("app", appName)
defer logger.Sync() //nolint:errcheck
}

// viperBindFlag provides a wrapper around the viper bindings that handles error checks
func viperBindFlag(name string, flag *pflag.Flag) {
if err := viper.BindPFlag(name, flag); err != nil {
panic(err)
}
}
Loading

0 comments on commit e9a832e

Please sign in to comment.