Skip to content

Commit

Permalink
feat: improve dex server (#1440)
Browse files Browse the repository at this point in the history
Signed-off-by: Dillen Padhiar <[email protected]>
  • Loading branch information
dpadhiar authored Jan 16, 2024
1 parent 3b5a560 commit 870d86b
Show file tree
Hide file tree
Showing 14 changed files with 498 additions and 57 deletions.
203 changes: 203 additions & 0 deletions cmd/commands/dex_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*
Copyright 2022 The Numaproj Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package commands

import (
"fmt"
"net/url"
"os"
"time"

"github.com/spf13/cobra"
"sigs.k8s.io/yaml"

sharedtls "github.com/numaproj/numaflow/pkg/shared/tls"
sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
)

// generate TLS and config for Dex server
func NewDexServerInitCommand() *cobra.Command {
var (
disableTls bool
hostname string
baseHref string
)

command := &cobra.Command{
Use: "dex-server-init",
Short: "Generate dex config and TLS certificates for Dex server",
RunE: func(c *cobra.Command, args []string) error {

config, err := generateDexConfigYAML(hostname, baseHref, disableTls)
if err != nil {
return err
}

if err := os.WriteFile("/tmp/config.yaml", []byte(config), 0644); err != nil {
return fmt.Errorf("failed to create config.yaml: %w", err)
}

if !disableTls {
err = createTLSCerts()
if err != nil {
return fmt.Errorf("failed to create TLS certificates: %w", err)
}
}

return nil
},
}
command.Flags().BoolVar(&disableTls, "disable-tls", sharedutil.LookupEnvBoolOr("NUMAFLOW_DISABLE_DEX_SERVER_TLS", false), "Whether to disable authentication and authorization, defaults to false.")
command.Flags().StringVar(&hostname, "hostname", sharedutil.LookupEnvStringOr("NUMAFLOW_SERVER_ADDRESS", "https://localhost:8443"), "The external address of the Numaflow server.")
command.Flags().StringVar(&baseHref, "base-href", sharedutil.LookupEnvStringOr("NUMAFLOW_SERVER_BASE_HREF", "/"), "Base href for Numaflow server, defaults to '/'.")
return command
}

func generateDexConfigYAML(hostname, baseHref string, disableTls bool) ([]byte, error) {

// check if type of connector needs redirect URI
// <HOSTNAME>/<base_href>/login
redirectURL, err := url.JoinPath(hostname, baseHref, "/login")
if err != nil {
return nil, fmt.Errorf("failed to infer redirect url from config: %v", err)
}

var (
config []byte
dexCfg map[string]interface{}
)

config, err = os.ReadFile("/cfg/config.yaml")
if err != nil {
return nil, err
}

err = yaml.Unmarshal(config, &dexCfg)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal dex.config from configmap: %v", err)
}
// issuer URL is <HOSTNAME>/dex
issuerURL, err := url.JoinPath(hostname, "/dex")
if err != nil {
return nil, fmt.Errorf("failed to infer issuer url: %v", err)
}
dexCfg["issuer"] = issuerURL
dexCfg["storage"] = map[string]interface{}{
"type": "memory",
}
// if TLS is disabled otherwise include path to certs
if disableTls {
dexCfg["web"] = map[string]interface{}{
"http": "0.0.0.0:5556",
}
} else {
dexCfg["web"] = map[string]interface{}{
"https": "0.0.0.0:5556",
"tlsCert": "/etc/numaflow/dex/tls/tls.crt",
"tlsKey": "/etc/numaflow/dex/tls/tls.key",
}
}

if oauth2Cfg, found := dexCfg["oauth2"].(map[string]interface{}); found {
if _, found := oauth2Cfg["skipApprovalScreen"].(bool); !found {
oauth2Cfg["skipApprovalScreen"] = true
}
} else {
dexCfg["oauth2"] = map[string]interface{}{
"skipApprovalScreen": true,
}
}

numaflowStaticClient := map[string]interface{}{
"id": "numaflow-server-app",
"name": "Numaflow Server App",
"public": true,
"redirectURIs": []string{
redirectURL,
},
}

staticClients, ok := dexCfg["staticClients"].([]interface{})
if ok {
dexCfg["staticClients"] = append([]interface{}{numaflowStaticClient}, staticClients...)
} else {
dexCfg["staticClients"] = []interface{}{numaflowStaticClient}
}

// <HOSTNAME>/dex/callback
dexRedirectURL, err := url.JoinPath(hostname, "/dex/callback")
if err != nil {
return nil, fmt.Errorf("failed to infer dex redirect url: %v", err)
}
connectors, ok := dexCfg["connectors"].([]interface{})
if !ok {
return nil, fmt.Errorf("malformed Dex configuration found")
}
for i, connectorIf := range connectors {
connector := connectorIf.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("malformed Dex configuration found")
}
connectorType := connector["type"].(string)
if !needsRedirectURI(connectorType) {
continue
}
connectorCfg, ok := connector["config"].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("malformed Dex configuration found")
}
connectorCfg["redirectURI"] = dexRedirectURL
connector["config"] = connectorCfg
connectors[i] = connector
}
dexCfg["connectors"] = connectors
return yaml.Marshal(dexCfg)
}

// needsRedirectURI returns whether or not the given connector type needs a redirectURI
// Update this list as necessary, as new connectors are added
// https://dexidp.io/docs/connectors/
func needsRedirectURI(connectorType string) bool {
switch connectorType {
case "oidc", "saml", "microsoft", "linkedin", "gitlab", "github", "bitbucket-cloud", "openshift", "gitea", "google", "oauth":
return true
}
return false
}

func createTLSCerts() error {

serverKey, serverCert, caCert, err := sharedtls.CreateCerts("numaflow", []string{"localhost", "dexserver"}, time.Now(), true, false)
if err != nil {
return err
}

err = os.WriteFile("/tls/tls.crt", serverCert, 0644)
if err != nil {
return err
}
err = os.WriteFile("/tls/tls.key", serverKey, 0644)
if err != nil {
return err
}
err = os.WriteFile("/tls/ca.crt", caCert, 0644)
if err != nil {
return err
}

return nil
}
1 change: 1 addition & 0 deletions cmd/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ func init() {
rootCmd.AddCommand(NewSideInputsInitCommand())
rootCmd.AddCommand(NewSideInputsManagerCommand())
rootCmd.AddCommand(NewSideInputsSynchronizerCommand())
rootCmd.AddCommand(NewDexServerInitCommand())
}
5 changes: 2 additions & 3 deletions cmd/commands/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/numaproj/numaflow/pkg/shared/logging"
sharedutil "github.com/numaproj/numaflow/pkg/shared/util"
svrcmd "github.com/numaproj/numaflow/server/cmd/server"
"github.com/numaproj/numaflow/server/common"
)

func NewServerCommand() *cobra.Command {
Expand All @@ -35,7 +36,6 @@ func NewServerCommand() *cobra.Command {
managedNamespace string
baseHref string
disableAuth bool
dexServerAddr string
serverAddr string
)

Expand All @@ -56,7 +56,7 @@ func NewServerCommand() *cobra.Command {
ManagedNamespace: managedNamespace,
BaseHref: baseHref,
DisableAuth: disableAuth,
DexServerAddr: dexServerAddr,
DexServerAddr: common.NumaflowDexServerAddr,
ServerAddr: serverAddr,
}
server := svrcmd.NewServer(opts)
Expand All @@ -70,7 +70,6 @@ func NewServerCommand() *cobra.Command {
command.Flags().StringVar(&managedNamespace, "managed-namespace", sharedutil.LookupEnvStringOr("NUMAFLOW_SERVER_MANAGED_NAMESPACE", sharedutil.LookupEnvStringOr("NAMESPACE", "numaflow-system")), "The namespace that the server watches when \"--namespaced\" is \"true\".")
command.Flags().StringVar(&baseHref, "base-href", sharedutil.LookupEnvStringOr("NUMAFLOW_SERVER_BASE_HREF", "/"), "Base href for Numaflow server, defaults to '/'.")
command.Flags().BoolVar(&disableAuth, "disable-auth", sharedutil.LookupEnvBoolOr("NUMAFLOW_SERVER_DISABLE_AUTH", false), "Whether to disable authentication and authorization, defaults to false.")
command.Flags().StringVar(&dexServerAddr, "dex-server-addr", sharedutil.LookupEnvStringOr("NUMAFLOW_SERVER_DEX_SERVER_ADDR", "http://numaflow-dex-server:5556/dex"), "The address of the Dex server.")
command.Flags().StringVar(&serverAddr, "server-addr", sharedutil.LookupEnvStringOr("NUMAFLOW_SERVER_ADDRESS", "https://localhost:8443"), "The external address of the Numaflow server.")
return command
}
65 changes: 54 additions & 11 deletions config/advanced-install/numaflow-dex-server.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,20 @@ subjects:
---
apiVersion: v1
data:
config.yaml: "issuer: <HOSTNAME>/dex\nstorage:\n type: memory\nweb:\n http: 0.0.0.0:5556\nstaticClients:\n
\ - id: numaflow-server-app\n redirectURIs: \n - <HOSTNAME>/<base_href>/login\n
\ name: 'Numaflow Server App'\n public: true\nconnectors:\n- type: github\n
\ # https://dexidp.io/docs/connectors/github/\n id: github\n name: GitHub\n
\ config:\n clientID: $GITHUB_CLIENT_ID\n clientSecret: $GITHUB_CLIENT_SECRET\n
\ redirectURI: <HOSTNAME>/dex/callback\n orgs:\n - name: <ORG_NAME>\n
\ teams:\n - admin\n - readonly\noauth2:\n skipApprovalScreen:
true\n"
config.yaml: |-
connectors:
- type: github
# https://dexidp.io/docs/connectors/github/
id: github
name: GitHub
config:
clientID: $GITHUB_CLIENT_ID
clientSecret: $GITHUB_CLIENT_SECRET
orgs:
- name: <ORG_NAME>
teams:
- admin
- readonly
kind: ConfigMap
metadata:
name: numaflow-dex-server-config
Expand Down Expand Up @@ -112,13 +118,50 @@ spec:
ports:
- containerPort: 5556
volumeMounts:
- mountPath: /etc/numaflow/dex/cfg
name: config
- mountPath: /etc/numaflow/dex/cfg/config.yaml
name: generated-dex-config
subPath: config.yaml
- mountPath: /etc/numaflow/dex/tls/tls.crt
name: tls
subPath: tls.crt
- mountPath: /etc/numaflow/dex/tls/tls.key
name: tls
subPath: tls.key
initContainers:
- args:
- dex-server-init
env:
- name: NUMAFLOW_SERVER_ADDRESS
valueFrom:
configMapKeyRef:
key: server.address
name: numaflow-cmd-params-config
optional: true
- name: NUMAFLOW_SERVER_BASE_HREF
valueFrom:
configMapKeyRef:
key: server.base.href
name: numaflow-cmd-params-config
optional: true
image: quay.io/numaproj/numaflow:latest
imagePullPolicy: Always
name: dex-init
volumeMounts:
- mountPath: /cfg
name: connector-config
- mountPath: /tls
name: tls
- mountPath: /tmp
name: generated-dex-config
serviceAccountName: numaflow-dex-server
volumes:
- configMap:
items:
- key: config.yaml
path: config.yaml
name: numaflow-dex-server-config
name: config
name: connector-config
- emptyDir: {}
name: tls
- emptyDir: {}
name: generated-dex-config
16 changes: 1 addition & 15 deletions config/base/dex/numaflow-dex-server-configmap.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ metadata:
name: numaflow-dex-server-config
data:
config.yaml: |
issuer: <HOSTNAME>/dex
storage:
type: memory
web:
http: 0.0.0.0:5556
staticClients:
- id: numaflow-server-app
redirectURIs:
- <HOSTNAME>/<base_href>/login
name: 'Numaflow Server App'
public: true
connectors:
- type: github
# https://dexidp.io/docs/connectors/github/
Expand All @@ -23,11 +12,8 @@ data:
config:
clientID: $GITHUB_CLIENT_ID
clientSecret: $GITHUB_CLIENT_SECRET
redirectURI: <HOSTNAME>/dex/callback
orgs:
- name: <ORG_NAME>
teams:
- admin
- readonly
oauth2:
skipApprovalScreen: true
- readonly
Loading

0 comments on commit 870d86b

Please sign in to comment.