Skip to content
This repository has been archived by the owner on Apr 3, 2024. It is now read-only.

allow web UI to be configured when using mTLS in API #138

Merged
merged 4 commits into from
Sep 24, 2022
Merged

allow web UI to be configured when using mTLS in API #138

merged 4 commits into from
Sep 24, 2022

Conversation

tomcz
Copy link
Contributor

@tomcz tomcz commented Sep 22, 2022

What changed?

Two changes:

  1. Allow temporal web/ui to be configured from a yaml file so that the web UI does not break when the API is configured for mTLS.
  2. Since the UI cannot be run via HTTPS, allow the UI to be bound to a different IP than the API. This can facilitate the API with mTLS being exposed to non-local clients, while keeping the UI available to localhost.

Why?

Temporalite is great, and I want to use it safely in situations where I'm experimenting with mTLS in temporal, especially while implementing mTLS in workers, without the UI being broken.

How did you test it?

Ran temporalite locally without any TLS, and with TLS certificates created by a self-signed root CA. The latter required the creation of temporalite.yaml and temporalite-ui.yaml files in a configuration directory, to allow mTLS to be set up for temporal API components, and allow the UI to make requests to the API via mTLS.

Updated the ui unit test to verify that it can still create a valid configuration when the temporalite-ui.yaml file is absent even though a config directory has been provided, and that it loads the file when it is present.

Updated the mtls test to verify proper integration between the UI and the API when mtls is enabled.

temporalite.yaml

global:
  tls:
    internode:
      server:
        certFile: dist/local.dev+2-client.pem
        keyFile: dist/local.dev+2-client-key.pem
        requireClientAuth: true
        clientCaFiles:
          - dist/rootCA.pem
      client:
        serverName: local.dev
        rootCaFiles:
          - dist/rootCA.pem
    frontend:
      server:
        certFile: dist/local.dev+2-client.pem
        keyFile: dist/local.dev+2-client-key.pem
        requireClientAuth: true
        clientCaFiles:
          - dist/rootCA.pem
      client:
        serverName: local.dev
        rootCaFiles:
          - dist/rootCA.pem

# dummy values, required by yaml parser
# replaced at runtime by temporalite
persistence:
  defaultStore: default
  numHistoryShards: 1

temporalite-ui.yaml

tls:
  caFile: dist/rootCA.pem
  certFile: dist/client.pem
  keyFile: dist/client-key.pem
  serverName: local.dev

Potential risks

Applications with existing command line configuration will still work as advertised.

Is hotfix candidate?

No.

@CLAassistant
Copy link

CLAassistant commented Sep 22, 2022

CLA assistant check
All committers have signed the CLA.

func newUIConfig(frontendAddr string, uiIP string, uiPort int, configDir string) (*uiconfig.Config, error) {
cfg := &uiconfig.Config{}
if configDir != "" {
if err := config.Load("temporalite-ui", configDir, "", cfg); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Even though it appears to basically be the same thing, I wonder if you would rather use https://pkg.go.dev/github.com/temporalio/ui-server/v2/plugins/fs_config_provider#Load to match what the UI server project does

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure.

}
cfg.TemporalGRPCAddress = frontendAddr
cfg.Host = uiIP
Copy link
Member

Choose a reason for hiding this comment

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

Wonder if this should only overwrite if not set from the config file

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I guess the only non-negotiable settings are TemporalGRPCAddress and EnableUI. The others can be overridden by the config file.

EnableUI: true,
func newUIConfig(frontendAddr string, uiIP string, uiPort int, configDir string) (*uiconfig.Config, error) {
cfg := &uiconfig.Config{}
if configDir != "" {
Copy link
Member

Choose a reason for hiding this comment

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

one caveat: if a config directory is present, the new version will look for a "temporalite-ui.yaml" config file and the app will die with a "no config files found" error if a config file cannot be found.

Not the biggest fan of this caveat. It seems reasonable for some people to only want a Temporal config and still get the UI. A file check here is proably fine for now since we don't have a bunch of environments to configure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, that makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Update pushed, caveat removed.

caFile: dist/rootCA.pem
certFile: dist/client.pem
keyFile: dist/client-key.pem
serverName: local.dev
Copy link
Collaborator

Choose a reason for hiding this comment

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

Allow temporal web/ui to be configured from a yaml file so that the web UI does not break when the API is configured for mTLS.

I'm wondering if it would be possible to be smarter about this specific use case in Temporalite so that users don't have to create a UI specific config file as soon as they enable mTLS.

Would there be a way to detect that mTLS is enabled in Temporal's server config and then automatically configure the UI server connection options appropriately?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Possibly, and that might be a thing for another PR. Enabling temporalite for mTLS is something that already requires a config file, so this feels like nothing new.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, even though temporal-ui is run by this process, do we really want to have it use the same certificate to talk to the frontend? This is what we would have to do if we wanted to reuse the mTLS configuration from the temporal server. While that is technically possible, it feels weird & odd to have to set up mTLS for internode and frontend and then just have the UI reuse either one. That feels like a decision beyond this PR.

Copy link
Collaborator

@jlegrone jlegrone Sep 23, 2022

Choose a reason for hiding this comment

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

👍 not a blocker; we still should be able to pass in a UI config file anyway. One day maybe we'll be able to do away with all ports in Temporalite other than frontend and the ui-server, which will make "internode" communication less weird since it's all a single process already.

I'm not too concerned about reuse as long as we add an easy readonly mode toggle for the UI.

Copy link
Member

@cretz cretz left a comment

Choose a reason for hiding this comment

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

Approved, but want @jlegrone's approval too before merge

}
if configDir != "" {
if err := provider.Load(configDir, cfg, "temporalite-ui"); err != nil {
if !strings.HasPrefix(err.Error(), "no config files found") {
Copy link
Member

Choose a reason for hiding this comment

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

Hrmm, would have liked to see if we could prevent the error instead of baking in expectations around its English message. I forget the config file loading whether it's easy to know what files to check for existence eagerly. But I guess it's not that bad since you have a unit test that confirms this conditional works.

Copy link
Contributor Author

@tomcz tomcz Sep 23, 2022

Choose a reason for hiding this comment

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

Ideally it would be great if the Load function returned an error we can check for with errors.Is since that is the best way of avoiding tedious string comparisons. I also wanted to avoid having to duplicate the file resolution algorithm applied by Load. I reckon for now the unit test check will at least blow up if the algorithm changes or the error changes.

@jlegrone
Copy link
Collaborator

jlegrone commented Sep 23, 2022

This could be done in a followup PR, but I'd love to add some documentation around enabling TLS and the web UI together. Perhaps in the internal/examples/mtls directory.

@tomcz
Copy link
Contributor Author

tomcz commented Sep 23, 2022

@jlegrone I've not created any extra documentation, but this directory is used by mtls_test.go so I added a template for temporalite-ui.yaml and updated the test to make an api call to temporal-ui just like a browser page would. This at least verifies that temporal-ui can now properly talk to temporal-api.

@tomcz
Copy link
Contributor Author

tomcz commented Sep 24, 2022

@cretz the failed macos headless test passes locally on my mac. Not sure how to fix it for the PR since none of the code changed in the PR should have any effect on the test that broke. What am I missing?

go test -tags headless ./...
?   	github.com/temporalio/temporalite	[no test files]
ok  	github.com/temporalio/temporalite/cmd/temporalite	1.478s
?   	github.com/temporalio/temporalite/internal/copyright	[no test files]
?   	github.com/temporalio/temporalite/internal/examples/helloworld	[no test files]
?   	github.com/temporalio/temporalite/internal/examples/mtls	[no test files]
?   	github.com/temporalio/temporalite/internal/liteconfig	[no test files]
ok  	github.com/temporalio/temporalite/temporaltest	3.330s

@jlegrone jlegrone merged commit 06b1f4a into temporalio:main Sep 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants