Skip to content

Commit

Permalink
feat: dockerfile and docker compose example (#390)
Browse files Browse the repository at this point in the history
Co-authored-by: Thomas Aidan Curran <[email protected]>
  • Loading branch information
zepatrik and tacurran authored Jan 26, 2021
1 parent 5eb1a7d commit 10cd0b3
Show file tree
Hide file tree
Showing 27 changed files with 461 additions and 66 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ node_modules/
.idea/
coverage.txt
vendor/
*-packr.go
pkged.go
5 changes: 5 additions & 0 deletions .schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
"namespace": {
"type": "object",
"properties": {
"$schema": {
"type": "string",
"format": "uri-reference",
"description": "Add this to allow defining the schema, useful for IDE integration"
},
"name": {
"type": "string",
"title": "The name of the namespace.",
Expand Down
5 changes: 5 additions & 0 deletions .schema/namespace.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "./config.schema.json#/definitions/namespace",
"description": "This schema is just to work around the limitation of some IDEs to support json schema paths."
}
41 changes: 41 additions & 0 deletions .schema/relation_tuple.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"additionalProperties": false,
"required": ["namespace", "relation", "object", "subject"],
"properties": {
"$schema": {
"type": "string",
"format": "uri-reference",
"description": "Add this to allow defining the schema, useful for IDE integration"
},
"namespace": {
"type": "string",
"description": "The namespace of the object and relation in this tuple."
},
"relation": {
"type": "string",
"description": "The relation of the object and subject."
},
"object": {
"type": "string",
"description": "The object affected by this relation."
},
"subject": {
"oneOf": [
{
"type": "string",
"pattern": "^.*:.*#.*$",
"description": "The subject set affected by this relation. Uses the encoding of \"<namespace>:<object>#<relation>\"."
},
{
"type": "string",
"description": "The subject affected by this relation. Use \"<namespace>:<object>#<relation>\" to describe a subject set.",
"not": {
"pattern": "^.*:.*#.*$"
}
}
]
}
}
}
45 changes: 37 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,45 @@
# To compile this image manually run:
#
# $ packr; GO111MODULE=on GOOS=linux GOARCH=amd64 go build; docker build -t oryd/keto:latest .; rm keto; packr clean
FROM alpine:3.9
FROM golang:1.15-alpine AS builder

RUN apk -U --no-cache add build-base git gcc bash

WORKDIR /go/src/github.com/ory/keto

ADD go.mod go.mod
ADD go.sum go.sum

ENV GO111MODULE on
ENV CGO_ENABLED 1

RUN go mod download

RUN go build -o /usr/bin/pkger github.com/markbates/pkger/cmd/pkger

ADD . .

RUN /usr/bin/pkger && go build -tags sqlite -o /usr/bin/keto

FROM alpine:3.12

RUN addgroup -S ory; \
adduser -S ory -G ory -D -h /home/ory -s /bin/nologin; \
chown -R ory:ory /home/ory

RUN apk add -U --no-cache ca-certificates

FROM scratch
COPY --from=builder /usr/bin/keto /usr/bin/keto

# By creating the sqlite folder as the ory user, the mounted volume will be owned by ory:ory, which
# is required for read/write of SQLite.
RUN mkdir -p /var/lib/sqlite
RUN chown ory:ory /var/lib/sqlite
VOLUME /var/lib/sqlite

# Exposing the ory home directory to simplify passing in the configuration.
VOLUME /home/ory

COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY keto /usr/bin/keto
EXPOSE 4466 4467

USER 1000
USER ory

ENTRYPOINT ["keto"]

Expand Down
22 changes: 10 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ifneq ("$(shell base64 Makefile) $(shell base64 go.mod) $(shell base64 go.sum)",
go build -o .bin/swagger github.com/go-swagger/go-swagger/cmd/swagger
go build -o .bin/goimports golang.org/x/tools/cmd/goimports
go build -o .bin/ory github.com/ory/cli
go build -o .bin/packr github.com/gobuffalo/packr/packr
go build -o .bin/pkger github.com/markbates/pkger/cmd/pkger
go build -o .bin/go-bindata github.com/go-bindata/go-bindata/go-bindata
go build -o .bin/buf github.com/bufbuild/buf/cmd/buf
go build -o .bin/protoc-gen-go google.golang.org/protobuf/cmd/protoc-gen-go
Expand All @@ -33,18 +33,18 @@ format:
install-stable: deps
KETO_LATEST=$$(git describe --abbrev=0 --tags)
git checkout $$KETO_LATEST
packr
pkger
GO111MODULE=on go install \
-ldflags "-X github.com/ory/keto/cmd.Version=$$KETO_LATEST -X github.com/ory/keto/cmd.Date=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/keto/cmd.Commit=`git rev-parse HEAD`" \
.
packr clean
rm pkged.go
git checkout master

.PHONY: install
install: deps
packr
pkger
GO111MODULE=on go install .
packr clean
rm pkged.go

# Generates the SDKs
.PHONY: sdk
Expand All @@ -58,13 +58,11 @@ sdk: deps
swagger generate client -f ./.schema/api.swagger.json -t internal/httpclient -A Ory_Keto
make format

.PHONY: docker
docker: deps
packr
GO111MODULE=on GOOS=linux GOARCH=amd64 go build
docker build -t oryd/keto:latest .
rm keto
packr clean
.PHONY: build
build: deps
pkger
go build -tags sqlite
rm pkged.go

#
# Generate APIs and client stubs from the definitions
Expand Down
30 changes: 14 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<h1 align="center"><img src="https://raw.githubusercontent.com/ory/meta/master/static/banners/keto.svg" alt="ORY Keto - Open Source & Cloud Native Access Control Server"></h1>
<h1 align="center"><img src="https://raw.githubusercontent.com/ory/meta/master/static/banners/keto.svg" alt="ORY Keto - Open Source Cloud Native Access Control Server"></h1>

<h4 align="center">
<a href="https://www.ory.sh/chat">Chat</a> |
Expand All @@ -10,23 +10,21 @@
<a href="https://opencollective.com/ory">Support this project!</a>
</h4>

# This is the next step for ORY Keto :tada:
# Ory Keto keeps evolving; the next step is a highly distributed authorisation and access control system:tada:

Be part of our journey to build the next-gen Keto based on
[Google's Zanzibar paper](https://research.google/pubs/pub48190/).
Creating fluid access to cloud applicaitons demands an efficient access control and authorisation system. In this part of the Ory Open Source ecosystem we tackle an implementation of
[Google's Zanzibar paper](https://research.google/pubs/pub48190/). If you have used Google Mail, Maps, and Youtube in one session, you have had some experience with Zanzibar.

The following is just a high level view on the paper, trying to grasp all the concepts.
The following is a high level view on the paper, where we interpret the main concepts and explain them for our implementation purposes.

## ACL Language

The ACL is represented by `object#relation@user`, while `user` can be a single user,
or a set of users represented by `object#relation` (e.g. users with editing rights on some object).
The Access Control Language (ACL) is represented by `object#relation@user`, while `user` can be a single user,
or a set of users represented by `object#relation` e.g. users with editing rights on some object.

## Managing content update, important for update ordering

1. > A Zanzibar client (e.g. Google Docs) requests an opaque consistency token called zookie for each content version via a `content change` ACL check.
The client has to store the token together with the content change.
1. A Zanzibar client e.g. Google Docs requests an opaque consistency token called "zookie" for each content version via a `content change` ACL check. The client has to store the token together with the content change.
2. "The client sends this zookie in subsequent ACL check requests to ensure that the check snapshot is at least as
fresh as the timestamp for the content version."

Expand All @@ -40,9 +38,9 @@ a client content version, or a read snapshot.
### Namespaces

Zanzibar clients have to configure their namespace:
1. configure relations
1. Configure relations
- optimization via defining relations on relations within the relation definition
2. storage parameters (type of object IDs, sharding)
2. Storage parameters for instance, type of object IDs, or sharding.

## API

Expand All @@ -68,21 +66,21 @@ response. Watching can be resumed with the response zookie and will not miss any

### Check

1. view check
1. View check
The request contains a userset (`object#relation`), authentication token and a zookie corresponding to the object version.
2. content change check
2. Content change check
No zookie in the request, the ACL has to be evaluated at the latest snapshot. Returns the new zookie for the object version.

### Expand

Like read but expands all indirect references.
SImilar to "read" and expands all indirect references.

# Architecture

## Storage

Relation tuples are stored in a database per namespace. Old versions are garbage collected to allow historic evaluation within a certain window.
There is also a global changelog used for the watcher API and optimizations. Changes are commited to both the namespace database and the changelog
There is also a global changelog used for the "Watcher API" and optimizations. Changes are commited to both the namespace database and the changelog
in one transaction.
Namespace configuration is stored in an extra database with two tables, one for the config and one for a changelog to allow hot
reloading.
Expand Down
2 changes: 1 addition & 1 deletion cmd/check/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func newCheckCmd() *cobra.Command {
return err
}

_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%v", resp.Allowed)
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%v\n", resp.Allowed)
return nil
},
}
Expand Down
6 changes: 6 additions & 0 deletions cmd/expand/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package expand
import (
"fmt"

"github.com/ory/x/flagx"

"github.com/ory/x/cmdx"
"github.com/spf13/cobra"

Expand Down Expand Up @@ -48,6 +50,10 @@ func NewExpandCmd() *cobra.Command {
}

cmdx.PrintJSONAble(cmd, expand.TreeFromProto(resp.Tree))
switch flagx.MustGetString(cmd, cmdx.FlagFormat) {
case string(cmdx.FormatDefault), "":
_, _ = fmt.Fprintln(cmd.OutOrStdout())
}
return nil
},
}
Expand Down
86 changes: 63 additions & 23 deletions cmd/relationtuple/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"

acl "github.com/ory/keto/api/keto/acl/v1alpha1"

Expand All @@ -19,52 +21,90 @@ import (

func newCreateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create <relation-tuple.json>",
Args: cobra.ExactArgs(1),
Use: "create <relation-tuple.json> [<relation-tuple-dir>]",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
conn, err := client.GetWriteConn(cmd)
if err != nil {
return err
}

var f io.Reader
if args[0] == "-" {
f = cmd.InOrStdin()
} else {
f, err = os.Open(args[0])
var tuples []*relationtuple.InternalRelationTuple
var deltas []*acl.RelationTupleDelta
for _, fn := range args {
tuple, err := readTuplesFromArg(cmd, fn)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could open %s: %s\n", args[0], err)
return cmdx.FailSilently(cmd)
return err
}
for _, t := range tuple {
tuples = append(tuples, t)
deltas = append(deltas, &acl.RelationTupleDelta{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: t.ToProto(),
})
}
}

var r relationtuple.InternalRelationTuple
err = json.NewDecoder(f).Decode(&r)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not decode: %s\n", err)
return cmdx.FailSilently(cmd)
}

cl := acl.NewWriteServiceClient(conn)

_, err = cl.TransactRelationTuples(cmd.Context(), &acl.TransactRelationTuplesRequest{
RelationTupleDeltas: []*acl.RelationTupleDelta{
{
Action: acl.RelationTupleDelta_INSERT,
RelationTuple: r.ToProto(),
},
},
RelationTupleDeltas: deltas,
})
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error doing the request: %s\n", err)
return cmdx.FailSilently(cmd)
}

cmdx.PrintRow(cmd, &r)
cmdx.PrintTable(cmd, relationtuple.NewRelationCollection(tuples))
return nil
},
}
cmd.Flags().AddFlagSet(packageFlags)

return cmd
}

func readTuplesFromArg(cmd *cobra.Command, arg string) ([]*relationtuple.InternalRelationTuple, error) {
var f io.Reader
if arg == "-" {
f = cmd.InOrStdin()
} else {
stats, err := os.Stat(arg)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error getting stats for %s: %s\n", arg, err)
return nil, cmdx.FailSilently(cmd)
}

if stats.IsDir() {
fi, err := ioutil.ReadDir(arg)
if err != nil {
return nil, err
}

var tuples []*relationtuple.InternalRelationTuple
for _, child := range fi {
t, err := readTuplesFromArg(cmd, filepath.Join(arg, child.Name()))
if err != nil {
return nil, err
}
tuples = append(tuples, t...)
}
return tuples, nil
}

f, err = os.Open(arg)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error processing arg %s: %s\n", arg, err)
return nil, cmdx.FailSilently(cmd)
}
}

var r relationtuple.InternalRelationTuple
err := json.NewDecoder(f).Decode(&r)
if err != nil {
_, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Could not decode: %s\n", err)
return nil, cmdx.FailSilently(cmd)
}

return []*relationtuple.InternalRelationTuple{&r}, nil
}
Loading

0 comments on commit 10cd0b3

Please sign in to comment.