Skip to content
This repository has been archived by the owner on Feb 22, 2022. It is now read-only.

Hydra #1022

Closed
wants to merge 22 commits into from
Closed

Hydra #1022

Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions incubator/hydra/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: v1
description: A security-first open source OAuth2 and OpenID Connect server for existing and new enterprise infrastructures.
name: hydra
version: 0.7.10
keywords:
- hdyra
- authentication
- authorization
- auth
- oauth
- jwt
home: https://www.ory.am/products/hydra
icon: https://github.com/ory/hydra/blob/master/docs/images/logo.png
sources:
- https://github.com/ory/hydra
maintainers:
- name: Kevin Minehart
email: kminehart@wehco.com
53 changes: 53 additions & 0 deletions incubator/hydra/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Hydra
[Hydra](https://github/ory/hdya) offers OAuth 2.0 and OpenID Connect Core 1.0 capabilities as a service. Hydra is different, because it works with any existing authentication infrastructure, not just LDAP or SAML. By implementing a consent app (works with any programming language) you build a bridge between Hydra and your authentication infrastructure.

Hydra is able to securely manage JSON Web Keys, and has a sophisticated policy-based access control you can use if you want to.

This chart comes bundled with an installation of Postgres.

# Prerequesites

* Kubernetes 1.4+ with Beta APIs enabled
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason you still want to support 1.4?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason. 1.4 + Beta APIs is the minimum requirement, though. Honestly, I'd prefer to just remove this line anyways.

* PV provisioner to support in the underlying infrastructure

# Install

To install the chart:

```
helm install --name=my-release-name stable/hydra
```

This will create a deployment, a secret, a service, and optionally a PersistentVolumeClaim, prefixed with `my-release-name`.

# Uninstall

```
helm delete my-release-name [--purge]
```

Adding the `--purge` flag will completely remove any trace of your release. (No values will be remembered.)

# Configuring


| Parameter | Description | Default |
|-------------------------------|----------------------------------------------------------------------------------------------------------------------------|--------------------------------|
| `image` | The path for pulling the docker image (without tag) | `"oryd/hydra"` |
| `imageTag` | The version of the docker image to pull | `"0.7.10"` |
| `imagePullPolicy` | When to pull the docker image | `"Always"` |
| `replicas` | How many replicas of Hydra to create | `2` |
| `mountPath` | Where in the container to mount the storage volume | `"/root"` |
| `persistence.enabled` | If false, then use `emptyDir: {}`, otherwise, issue a PVC | `true` |
| `persistence.accessMode` | [ReadWriteOnce/ReadOnlyMany/ReadWriteMany](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes-1) | `ReadWriteOnce` |
| `persistence.size` | How much storage to allocate to the volume | `1Gi` |
| `postgresql.postgresPassword` | The password of the Postgres User managing Hydra | `hydra` |
| `postgresql.persistence.size` | How much storage to allocate to the postgres server behind Hydra | `1Gi` |
| `config.system.secret` | The secret password for encrypting authorization tokens. You should change this. | `"changeme_changeme_changeme"` |
| `config.consentUrl` | The URL for the consent app | `"https://consent.example.com"`|
| `config.logLevel` | How detailed should the log output be | `"debug"` |
| `accessTokenLifespan` | How long should an access token be valid for | `"1h"` |
| `idTokenLifespan` | How long should an id token be valid for | `"1h"` |
| `authorizeCodeLifespan` | How long should an authorize code be valid for | `"1h"` |

You can check out the [values.yaml](values.yaml) for any other configuration items not listed here.
10 changes: 10 additions & 0 deletions incubator/hydra/requirements.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
dependencies:
- condition: ""
enabled: false
import-values: null
name: postgresql
repository: https://kubernetes-charts.storage.googleapis.com/
tags: null
version: 0.6.0
digest: sha256:111f81da579687c7d392e5faf90dee60abcb036acbdbe5d2b31ff813a0aee54c
generated: 2017-04-19T15:46:03.298854078-05:00
4 changes: 4 additions & 0 deletions incubator/hydra/requirements.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
dependencies:
- name: postgresql
version: 0.6.0
repository: https://kubernetes-charts.storage.googleapis.com/
16 changes: 16 additions & 0 deletions incubator/hydra/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{{- if eq .Values.config.system.secret "this_is_a_secret_change_me" }}
######################################################################
# WARNING #
# You are using the default secret provided with the helm chart when #
# issuing authorization tokens. This is very insecure. #
######################################################################
{{- end }}

Hydra should not be facing the internet itself, so you can access hydra via.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of Hydra's authorization flow requires a redirect to Hydra. It definitely does need to be publicly accessible.

Overview of the OAuth2 Authorization Code flow
Basically, the user is initially redirected by the client to Hydra, which then redirects to the consent app with a signed challenge token. The consent app does necessary authentication and consent before redirecting the user back to Hydra (passing a signed challenge response). Hydra validates the response and generates the tokens before redirecting the user back to the client with the proper tokens.

This is incredibly simplified, but the key thing is that the user does need to be able to access Hydra from outside of the kubernetes cluster.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotya. Sorry about that! Part of the reason for me making this helm chart was so that I could set up Hydra on our cluster and give it a test run :P

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. Let me know if I can help :) I too need to set up Hydra. Figured I'd go big or go home.

the service from other deployments (including the consent app) using the hostname:

{{ template "fullname" . }}-service.{{ $.Release.Namespace }}.svc.cluster.local

Or on the same namespace:

{{ template "fullname" . }}-service
24 changes: 24 additions & 0 deletions incubator/hydra/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified postgresql name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "postgresql.fullname" -}}
{{- printf "%s-%s" .Release.Name "postgresql" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
69 changes: 69 additions & 0 deletions incubator/hydra/templates/hydra_deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
apiVersion: extensions/v1beta1
kind: Deployment
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make more sense to use a Statefulset since you are using persistent storage?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@unguiculus Statefulset is still beta, not sure we should be pushing it yet

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if anything @kminehart I would consider removing postgres entirely from here, most people still aren't putting db's on k8s for a variety of reasons. Best method I have seen so far is the operator method, but still I think Hydra should just deploy agnostic of postgres, or use the postgres chart as a dependency

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grillz Postgres chart as a dependency sounds good. It's probably best to have Hydra's chart set up its own database one way or the other because if left to their own devices, inexperienced system admins will likely configure Hydra to use the same database as the application, which is a major security hole. Maintaining database isolation is very important here, so it should be emphasized one way or another.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@20zinnm yeah I agree with that, but at the same time most of the k8s deployments were seeing right now are utilizing RDS or some hosted datastore, so I would hate to marry it either. Kind of a rock and a hard place

Copy link
Author

@kminehart kminehart Jun 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We've seen before that beta Kubernetes resources are fairly stable, I have no problem with making Hydra a StatefulSet. I don't fully understand what the benefit of using StatefulSets would be in this case, though.

If I understand correctly, the only thing you really need persistent storage for is the SSL certificates and CA files created by Hydra, right?

  1. As for Postgres, it is currently a dependency, as @20zinnm said. I followed in Gitlab's example, which may not have been the best idea considering Gitlab has the "omnibus" methodology of installing their software, but they also have a hell of a lot more than just Postgres as requirements.

There's a solution to this problem in Helm already.

By default, we will install the bundled postgres. In requirements.yaml, I'll add a condition: postgres.bundled, and if that value is true in values.yaml, then the bundled postgres will be installed.

This way, users that will prefer using RDS can opt-in to using RDS, and those of us that don't care or prefer keeping everything in Kubernetes can keep it the way it is.

It makes sense to me to make external database connections opt-in because it's always going to require configuration anyways. Keeping it bundled means the application will work out of the box without as much configuration.

https://github.com/kubernetes/helm/blob/master/docs/charts.md#tags-and-condition-fields-in-requirementsyaml

Bear with me, I'm still fairly new to Helm and Hydra.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think that should work, Hydra should not be a statefulset, only postgres would be, but that should be a separate chart as mentioned.

metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "fullname" . }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use {{ template "name" . }} for the app label. Apply everywhere.

chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
replicas: {{ .Values.replicas }}
selector:
matchLabels:
app: {{ template "fullname" . }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update selector for the label change:

app: {{ template "name" . }}
release: "{{ .Release.Name }}"

Apply everywhere.

template:
metadata:
name: {{ template "fullname" . }}
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
volumes:
- name: hydra-data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ template "fullname" . }}
{{- else }}
emptyDir: {}
{{- end }}
- name: hydra-secret
secret:
secretName: {{ template "fullname" . }}-secret
containers:
- name: hydra
image: {{ .Values.image }}:{{ .Values.imageTag }}
imagePullPolicy: {{ .Values.imagePullPolicy }}
command: ["hydra", "host"]
volumeMounts:
- name: hydra-data
mountPath: {{ .Values.mountPath }}
ports:
- name: service
containerPort: 4444
- name: service2
containerPort: 4445
env:
- name: SYSTEM_SECRET
valueFrom:
secretKeyRef:
name: {{ template "fullname" . }}-secret
key: system.secret
- name: DATABASE_URL
value: postgres://{{ .Values.postgresql.postgresUser }}:{{ .Values.postgresql.postgresPassword }}@{{ template "postgresql.fullname" . }}:5432/{{ .Values.postgresql.postgresDatabase }}?sslmode=disable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use secrets for passwords. Also, is there any other way to specify the password rather than in the URL?

Copy link

@pbarker pbarker Jun 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@unguiculus I'm not sure using secrets for passwords should be a requirement, secrets in kubernetes are still not secure and wont be for some time

Copy link

@meyerzinn meyerzinn Jun 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@grillz if K8s is properly configured (TLS, don't let just anybody access the REST API) I don't see why a secret is any less secure than the deployment file.

If an attacker gains access to the k8s API, the secret is compromised either way, no?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@20zinnm For one this is a major compliance issue kubernetes/kubernetes#12742 . Not to mention the partitioning problem they are working are attempting to solve at the moment, but its not going to happen in the near future. Secrets just really aren't secret in k8s, and that no secret in the community. Sorry no pun intended 😉

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regardless, we should still put the password in a secret, especially now that RBAC is a thing, you can give someone access to deployments, and not secrets, and they won't be able to grab the database password.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can its just not actually secret, and RBAC is very limited in its ability to secure secrets at the moment. You can join sig-auth every other Wednesday where this is a hot topic.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we get around this by throwing these credentials into the environment from a secret? I.e. values.yaml --> secret --> environment?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't help much, our current setup is just using helm install --set my.secret=foo and pushing those to env vars. If we really want to use secrets its not the end of the world, they will be secure at some point here in the next year I imagine 😄

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go ahead and set ourselves up for success :). I'll add a commit to kminehart/charts#1 with it as soon as I test it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess... kubernetes is a funny exercise in branding, call something a secret even if it isn't at all and people will use it as such ¯_(ツ)_/¯

- name: HTTPS_ALLOW_TERMINATION_FROM
value: {{ .Values.config.httpsAllowTerminationFrom }}
- name: LOG_LEVEL
value: {{ .Values.config.logLevel }}
- name: CONSENT_URL
value: {{ .Values.config.consentUrl }}
- name: ACCESS_TOKEN_LIFESPAN
value: {{ .Values.config.accessTokenLifespan }}
- name: ID_TOKEN_LIFESPAN
value: {{ .Values.config.idTokenLifespan }}
- name: AUTHORIZE_CODE_LIFESPAN
value: {{ .Values.config.authorizeCodeLifespan }}
resources:
{{ toYaml .Values.resources | indent 12 }}
19 changes: 19 additions & 0 deletions incubator/hydra/templates/hydra_pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{{- if .Values.persistence.enabled }}
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: {{ template "fullname" . }}
annotations:
{{- if .Values.persistence.storageClass }}
volume.beta.kubernetes.io/storage-class: {{ .Values.persistence.storageClass | quote }}
{{- else }}
volume.alpha.kubernetes.io/storage-class: default
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.accessMode | quote }}
resources:
requests:
storage: {{ .Values.persistence.size | quote }}
{{- end }}

12 changes: 12 additions & 0 deletions incubator/hydra/templates/hydra_secret.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Secret
metadata:
name: {{ template "fullname" . }}-secret
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
type: Opaque
data:
system.secret: {{ .Values.config.system.secret | b64enc }}
18 changes: 18 additions & 0 deletions incubator/hydra/templates/hydra_service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
kind: Service
apiVersion: v1
metadata:
name: {{ template "fullname" . }}-service
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
type: {{ .Values.service.type }}
selector:
app: {{ template "fullname" . }}
ports:
- name: service
port: {{ .Values.service.port }}
targetPort: 4444
protocol: TCP
49 changes: 49 additions & 0 deletions incubator/hydra/values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
image: "oryd/hydra"
imageTag: "v0.7.10"
imagePullPolicy: "Always"

replicas: 2
mountPath: "/root"

# Persistent storage.
persistence:
## If this is false, then emptyDir: {} will be used.
## Setting this to true is highly recommended for production use.
## If this is false, you will lose your data when your pod is destroyed.
enabled: true
## If defined, volume.beta.kubernetes.io/storage-class: <storageClass>
## Default: volume.alpha.kubernetes.io/storage-class: default
#
# storageClass: <storageClass>
accessMode: ReadWriteOnce
size: 1Gi

postgresql:
imageTag: "9.6"
memory: 256Mi
cpu: 250m
postgresUser: hydra
postgresPassword: hydra
postgresDatabase: hydra
persistence:
size: 10Gi

config:
system:
secret: "this_is_a_secret_change_me"
consentUrl: "https://consent.example.com"
logLevel: "debug"
accessTokenLifespan: "1h"
idTokenLifespan: "1h"
authorizeCodeLifespan: "10m"
httpsAllowTerminationFrom: 0.0.0.0/0

service:
type: ClusterIP
port: 4444

# http://kubernetes.io/docs/user-guide/compute-resources/
resources:
requests:
memory: 128Mi
cpu: 100m