ory / hydra

The most scalable and customizable OpenID Certified™ OpenID Connect and OAuth Provider on the market. Become an OpenID Connect and OAuth2 Provider over night. Broad support for related RFCs. Written in Go, cloud native, headless, API-first. Available as a service on Ory Network and for self-hosters.
https://www.ory.sh/?utm_source=github&utm_medium=banner&utm_campaign=hydra
Apache License 2.0
15.65k stars 1.5k forks source link

Allow people to configure the Hydra service using a config file. #561

Closed Natim closed 7 years ago

Natim commented 7 years ago

When looking at hydra host --help it says that a config file can be specified using --config

However I couldn't find any documentation or example file about it.

aeneasr commented 7 years ago

The config file is only for the client cli, not the host process

Am 20.07.2017 um 14:41 schrieb Rémy HUBSCHER notifications@github.com:

When looking at hydra host --help it says that a config file can be specified using --config

However I couldn't find any documentation or example file about it.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or mute the thread.

Natim commented 7 years ago

So hydra host --config config.yml is not really a thing?

Natim commented 7 years ago

It would be nice to be able to set all the system_secret, database_url in a file rather than in env variable especially for production deployments.

aeneasr commented 7 years ago

This has security and deployment implications and violates 12 factor principles, it's thus disabled by design and highly unlikely to be implemented.

Am 20.07.2017 um 15:16 schrieb Rémy HUBSCHER notifications@github.com:

It would be nice to be able to set all the system_secret, database_url in a file rather than in env variable especially for production deployments.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

Natim commented 7 years ago

You will need to elaborate a little bit on this because most service I know are configurable by files.

aeneasr commented 7 years ago

12 factor principles are design guidelines for cloud native services that run on docker or via buildpacks. Those rarely support file configs, especially because some configs require dynamic settings (eg ip ranges) which are hard to automate via files in prepackaged docker images. Also, leaving highly classified info (system secret) unencrypted on the filesystem is really bad practice that requires either additional business logic (eg file owner check) or trusting sysadmins to properly set up the systems, which is also called "insecure by default".

Am 20.07.2017 um 17:29 schrieb Rémy HUBSCHER notifications@github.com:

You will need to elaborate a little bit on this because most service I know are configurable by files.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

Natim commented 7 years ago

Ok so one should use Dockerflow to deploy hydra?

aeneasr commented 7 years ago

Preferably

Am 20.07.2017 um 17:37 schrieb Rémy HUBSCHER notifications@github.com:

Ok so one should use Dockerflow to deploy hydra?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

aeneasr commented 7 years ago

Closing because this will not be changed

glerchundi commented 6 years ago

I love 12 factor apps guidelines but I'll vote for this feature to be implemented, mainly because one doesn't hurt the other and it adds value as well as ease the deployments in several cases.

For example, I want to deploy Envoy + Hydra + Consent App by using a unique Kubernetes deployment + a unique Kubernetes secret/configmap. Each entry in this secret is going to be the configuration file for one of those containers:

apiVersion: v1
kind: Secret
metadata:
  name: authn
type: Opaque
data:
  hydra.yml: |
     port: 4444
     public-url: "https://"
     admin-url: "https://...."
  consent-app.yml: |
    port: 4445
    hydra-url: "https:/...."
  envoy.yml: |
    cluster-1: "/oauth2"
      ip: "127.0.0.1:4444"
    cluster-2: "/"
      ip: "127.0.0.1:4445"
apiVersion: apps/v1
kind: Deployment
metadata:
  name: authn
  labels:
    app: authn
spec:
  replicas: 3
  selector:
    matchLabels:
      app: authn
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: hydra
        image: oryd/hydra:v1.0.0-beta.8-alpine
        args: ["--config", "/etc/authn/hydra.yml"]
        ports:
        - containerPort: 4444
        - containerPort: 4445
        volumeMounts:
        - name: authn-hydra
          mountPath: "/etc/authn"
          readOnly: true
      - name: consent-app
        image: mycompany/consent-app:v1.0.0
        args: ["--config", "/etc/authn/consent-app.yml"]
        ports:
        - containerPort: 3000
        volumeMounts:
        - name: authn-consent-app
          mountPath: "/etc/authn"
          readOnly: true
      - name: envoy
        image: envoyproxy/envoy:v1.7.0
        args: ["--config", "/etc/authn/envoy.yml"]
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: authn-envoy
          mountPath: "/etc/authn"
          readOnly: true
  volumes:
  - name: authn-hydra
    secret:
      secretName: authn
      items:
      - key: hydra.yml
        path: hydra.yml
  - name: authn-consent-app
    secret:
      secretName: authn
      items:
      - key: consent-app.yml
        path: consent-app.yml
  - name: authn-envoy
    secret:
      secretName: authn
      items:
      - key: envoy.yml
        path: envoy.yml

Btw, It has the benefit of deployment consent and hydra at once thus avoiding API incompatiblities between them.

Would you even consider this? I don't mind creating a PR.

Another references:

aeneasr commented 6 years ago

The issue with config files here is that we have two sensitive secrets (SYSTEM_SECRET and COOKIE_SECRET). Since we'll store them in plaintext this can get a bit tricky to secure. While there certainly are ways to get the env vars of a process, they typically require root privileges like cat /proc/<pid>/environ (afaik). Your particular set up does not seem to have an issue with that as it's all mounted volumes within kubernetes. But I'm not only thinking about your use case, but also about people that might do this on a baremetal server.

On a side note, I do see that you save a bit of space in your definition from

  hydra.yml: |
     port: 4444
     public-url: "https://"
     admin-url: "https://...."

to

    env:
    - name: PORT
      value: "4444"

but I'm not yet convinced that it's worth it.

glerchundi commented 6 years ago

Let me do a complete test and will come back here. Kubernetes seems to be implementing an envFrom key in the specs and referencing this one to a complete ConfigMap/Secret by using configMapRef or secretKeyRef. So it seems fairly easy to deploy this configuration parameters in the way i'm thinking.

Will tell you something once I have a working example.

glerchundi commented 6 years ago

@aeneasr TLTR, it works and I don't need a config file.

We're going to deploy Hydra, a front/reverse proxy and our conset application all in the same Kubernetes Deployment. This means:

The most important bits for this deployment are the statically defined Envoy configuration for the front/reverse proxy, which is this one:

apiVersion: v1
kind: ConfigMap
metadata:
  name: envoy
data:
  envoy.yaml: |
    static_resources:
      listeners:
      - address:
          socket_address:
            address: 0.0.0.0
            port_value: 8080
        filter_chains:
        - filters:
          - name: envoy.http_connection_manager
            config:
              codec_type: auto
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                - name: backend
                  domains:
                  - "*"
                  routes:
                  - match:
                      prefix: "/login"
                    route:
                      cluster: authn
                  - match:
                      prefix: "/consent"
                    route:
                      cluster: authn
                  - match:
                      prefix: "/"
                    route:
                      cluster: hydra
              http_filters:
              - name: envoy.router
                config: {}
      clusters:
      - name: hydra
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        hosts:
        - socket_address:
            address: 127.0.0.1
            port_value: 8080
      - name: authn
        connect_timeout: 0.25s
        type: strict_dns
        lb_policy: round_robin
        hosts:
        - socket_address:
            address: 127.0.0.1
            port_value: 8081
    admin:
      access_log_path: "/dev/null"
      address:
        socket_address:
          address: 0.0.0.0
          port_value: 9000

And how to provide the required configuration to Hydra where parts of them are secrets and the others are not.

configmaps/hydra.yaml:

apiVersion: v1
kind: ConfigMap
metadata:
  name: hydra
data:
  LOG_LEVEL: debug
  PUBLIC_PORT: "8080"
  ADMIN_PORT: "9000"
  OAUTH2_ISSUER_URL: http://192.168.99.100.nip.io
  OAUTH2_LOGIN_URL: http://192.168.99.100.nip.io/login
  OAUTH2_CONSENT_URL: http://192.168.99.100.nip.io/consent
  OAUTH2_ERROR_URL: http://192.168.99.100.nip.io/error
  OIDC_SUBJECT_TYPES_SUPPORTED: public,pairwise

secrets/hydra.yaml

apiVersion: v1
kind: Secret
metadata:
  name: hydra
type: Opaque
data:
  # postgres://postgres:mysecretpassword@postgres.default.svc.cluster.local:5432/hydra?sslmode=disable
  DATABASE_URL: cG9zdGdyZXM6Ly9wb3N0Z3JlczpteXNlY3JldHBhc3N3b3JkQHBvc3RncmVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWw6NTQzMi9oeWRyYT9zc2xtb2RlPWRpc2FibGU=
  # youReallyNeedToChangeThis
  OIDC_SUBJECT_TYPE_PAIRWISE_SALT: eW91UmVhbGx5TmVlZFRvQ2hhbmdlVGhpcw==
  # youReallyNeedToChangeThis
  SYSTEM_SECRET: eW91UmVhbGx5TmVlZFRvQ2hhbmdlVGhpcw==

So that everything can get merged in the deployment by leveraging this to Kubernetes using the envFrom:

[...]
      containers:
      - image: docker.io/oryd/hydra:v1.0.0-beta.9-alpine
        name: hydra
        args: ["serve", "all", "--dangerous-force-http"]
        envFrom:
        - configMapRef:
            name: hydra
        - secretRef:
            name: hydra
        ports:
        - containerPort: 8080
          name: public
[...]

That's all.

aeneasr commented 6 years ago

Perfect!

On 5. Sep 2018, at 14:58, Gorka Lerchundi Osa notifications@github.com wrote:

@aeneasr TLTR, it works and I don't need a config file.

We're going to deploy Hydra, a front/reverse proxy and our conset application all in the same Kubernetes Deployment. This means:

Envoy: Which reverse proxies /login and /consent to our own consent application and everything else to the public Hydra port so that we don't need to explicitly define which paths is Hydra publishing (/.well-known/..., /oauth2, ...) Hydra: A concrete version of hydra, for now v1.0.0-beta.9. Consent App: A concrete version of consent application written in Go with a concrete vendored version of Hydra SDK in order to deploy everything at the same time and try to avoid any future incompatiblities (I know that you're probable aware of this but we feel more comfortable doing it in this way). The most important bits for this deployment are the statically defined Envoy configuration for the front/reverse proxy, which is this one:

apiVersion: v1 kind: ConfigMap metadata: name: envoy data: envoy.yaml: | static_resources: listeners:

  • address: socket_address: address: 0.0.0.0 port_value: 8080 filter_chains:
    • filters:
      • name: envoy.http_connection_manager config: codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts:
        • name: backend domains:
          • "*" routes:
          • match: prefix: "/login" route: cluster: authn
          • match: prefix: "/consent" route: cluster: authn
          • match: prefix: "/" route: cluster: hydra http_filters:
            • name: envoy.router config: {} clusters:
  • name: hydra connect_timeout: 0.25s type: strict_dns lb_policy: round_robin hosts:
    • socket_address: address: 127.0.0.1 port_value: 8080
  • name: authn connect_timeout: 0.25s type: strict_dns lb_policy: round_robin hosts:
    • socket_address: address: 127.0.0.1 port_value: 8081 admin: access_log_path: "/dev/null" address: socket_address: address: 0.0.0.0 port_value: 9000 And how to provide the required configuration to Hydra where parts of them are secrets and the others are not.

configmaps/hydra.yaml:

apiVersion: v1 kind: ConfigMap metadata: name: hydra data: LOG_LEVEL: debug PUBLIC_PORT: "8080" ADMIN_PORT: "9000" OAUTH2_ISSUER_URL: http://192.168.99.100.nip.io OAUTH2_LOGIN_URL: http://192.168.99.100.nip.io/login OAUTH2_CONSENT_URL: http://192.168.99.100.nip.io/consent OAUTH2_ERROR_URL: http://192.168.99.100.nip.io/error OIDC_SUBJECT_TYPES_SUPPORTED: public,pairwise secrets/hydra.yaml

apiVersion: v1 kind: Secret metadata: name: hydra type: Opaque data:

postgres://postgres:mysecretpassword@postgres.default.svc.cluster.local:5432/hydra?sslmode=disable

DATABASE_URL: cG9zdGdyZXM6Ly9wb3N0Z3JlczpteXNlY3JldHBhc3N3b3JkQHBvc3RncmVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWw6NTQzMi9oeWRyYT9zc2xtb2RlPWRpc2FibGU=

youReallyNeedToChangeThis

OIDC_SUBJECT_TYPE_PAIRWISE_SALT: eW91UmVhbGx5TmVlZFRvQ2hhbmdlVGhpcw==

youReallyNeedToChangeThis

SYSTEM_SECRET: eW91UmVhbGx5TmVlZFRvQ2hhbmdlVGhpcw== So that everything can get merged in the deployment by leveraging this to Kubernetes using the envFrom:

[...] containers:

  • image: docker.io/oryd/hydra:v1.0.0-beta.9-alpine name: hydra args: ["serve", "all", "--dangerous-force-http"] envFrom:
    • configMapRef: name: hydra
    • secretRef: name: hydra ports:
    • containerPort: 8080 name: public [...] That's all.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

baboons commented 6 years ago

I'm trying to do the same thing but I don't understand how to get the envoy.yml in the configmap to a volume, is it possible to share a full example on how you did that?

glerchundi commented 6 years ago
apiVersion: v1
kind: ConfigMap
metadata:
  name: envoy
data:
  envoy.yaml: |
    static_resources:
      listeners:
      - address:
[...]

And the deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: envoy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: envoy
  template:
    metadata:
      labels:
        app: envoy
    spec:
      containers:
      - image: docker.io/envoyproxy/envoy-alpine:v1.7.0
        name: envoy
        ports:
        - containerPort: 8080
          name: http
        command: ["envoy"]
        args:
        - --config-path /etc/envoy/envoy.yaml
        - --log-level debug
        - --v2-config-only
        volumeMounts:
        - name: envoy
          mountPath: /etc/envoy
          readOnly: true
      volumes:
      - name: envoy
        configMap:
          name: envoy

That's all.