Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aligned TLS options with hydra: allow cert&key to be specified with file #114

Merged
merged 1 commit into from
Aug 31, 2018
Merged

Aligned TLS options with hydra: allow cert&key to be specified with file #114

merged 1 commit into from
Aug 31, 2018

Conversation

fredbi
Copy link
Contributor

@fredbi fredbi commented Aug 31, 2018

Signed-off-by: Frederic BIDON [email protected]

- HTTP_TLS_KEY: Base64 encoded (without padding) string of the private key (PEM encoded) to be used for HTTP over TLS (HTTPS).
If not set, HTTPS will be disabled and instead HTTP will be served.
Example: HTTPS_TLS_CERT="-----BEGIN CERTIFICATE-----\nMIIDZTCCAk2gAwIBAgIEV5xOtDANBgkqhkiG9w0BAQ0FADA0MTIwMAYDVQQDDClP..."
Copy link
Member

Choose a reason for hiding this comment

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

This is mixed up, should probably be the key?


- HTTP_TLS_CERT: Base64 encoded (without padding) string of the TLS certificate (PEM encoded) to be used for HTTP over TLS (HTTPS).
If not set, HTTPS will be disabled and instead HTTP will be served.
Example: HTTPS_TLS_KEY="-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg..."
Copy link
Member

Choose a reason for hiding this comment

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

Same here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

darn!

If not set, HTTPS will be disabled and instead HTTP will be served.
Example: HTTPS_TLS_KEY="-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg..."

NOTE: configure TLS params both as PATH or as string. If no TLS pair is set, HTTPS will be disabled and instead HTTP will be served.
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be at the top of this section?

certPath := viper.GetString("HTTP_TLS_CERT_PATH")
keyPath := viper.GetString("HTTP_TLS_KEY_PATH")
if cert, err = tls.LoadX509KeyPair(certPath, keyPath); err != nil {
logger.WithError(err).Fatalln("Could not load x509 key pair files")
Copy link
Member

Choose a reason for hiding this comment

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

if all four variables are empty, this will always fatal, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep. Let me fix this...

@fredbi
Copy link
Contributor Author

fredbi commented Aug 31, 2018

ok I changed a bit to factorize this, in view of TLS addition to serve api

tlsCert := viper.GetString("HTTP_TLS_CERT")
tlsKey := viper.GetString("HTTP_TLS_KEY")

if tlsCert == "" && tlsKey == "" {
Copy link
Member

Choose a reason for hiding this comment

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

Can we improve this block? It does not have any tests and since it's difficult to read I'd love to see some reduction in complexity here. Also, I think that the naming of the variables is quite misleading and easy to misunderstand. A very simple way to improve this is with something like this:

var cert tls.Certificate
if certPath, keyPath := viper.GetString(), viper.GetString(); certPath == "" && keyPath == "" {
  cert, err := ...
  if err != nil ...
  return cert
}

if certString, keyString := viper.GetString(), viper.GetString(); certString== "" && keyString == "" {
  cert, err := ...
  if err != nil ...
  return cert
}

And then just check if cert != nil before ListenAndServe

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 I'll do that. Please bear that the original section was terrible style, with variables shadowing at every corner... I'll try to add a test as well.

Copy link
Member

Choose a reason for hiding this comment

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

My comment was not intended to be critical of you. I don't care who wrote the code - I write terrible code all the time. But while we work on this, let's improve it too! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used a boolean because this is a struct: the func cannot return nil, and testing that the returned value is an empty structure is ugly. However, I agree the if cascade if bad.

Copy link
Member

Choose a reason for hiding this comment

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

You can return a pointer *tls.Certificate which can evaluate to nil instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why not. This just incurs some more changes in the existing code...

@fredbi
Copy link
Contributor Author

fredbi commented Aug 31, 2018

@aeneasr how about this instead? [now with pointer]
This removes the fatalf() in func so I can add a unit test (fatalf() is carried on by caller).

func getTLSCertAndKey() (*tls.Certificate, error) {
	certString, keyString := viper.GetString("HTTP_TLS_CERT"), viper.GetString("HTTP_TLS_KEY")
	certPath, keyPath := viper.GetString("HTTP_TLS_CERT_PATH"), viper.GetString("HTTP_TLS_KEY_PATH")

	if certString == "" && keyString == "" && certPath == "" && keyPath == "" {
		// serve http
		return nil, nil
	}
	if certString != "" && keyString != "" {
		tlsCertBytes, err := base64.StdEncoding.DecodeString(certString)
		if err != nil {
			return nil, fmt.Errorf("unable to base64 decode the TLS certificate: %v", err)
		}
		tlsKeyBytes, err := base64.StdEncoding.DecodeString(keyString)
		if err != nil {
			return nil, fmt.Errorf("unable to base64 decode the TLS private key: %v", err)
		}

		cert, err := tls.X509KeyPair(tlsCertBytes, tlsKeyBytes)
		if err != nil {
			return nil, fmt.Errorf("unable to load X509 key pair: %v", err)
		}
		return &cert, nil
	}
	if certPath != "" && keyPath != "" {
		cert, err := tls.LoadX509KeyPair(certPath, keyPath)
		if err != nil {
			return nil, fmt.Errorf("unable to load X509 key pair from files: %v", err)
		}
		return &cert, nil
	}
	// serve http
	logger.Warnln("TLS requires both cert and key to be specified. Fall back to serving HTTP")
	return nil, nil
}

@aeneasr
Copy link
Member

aeneasr commented Aug 31, 2018

Nice! This looks much cleaner. I think the if logic can be nested:

func getTLSCertAndKey() (*tls.Certificate, error) {
	certString, keyString := viper.GetString("HTTP_TLS_CERT"), viper.GetString("HTTP_TLS_KEY")
	certPath, keyPath := viper.GetString("HTTP_TLS_CERT_PATH"), viper.GetString("HTTP_TLS_KEY_PATH")

	if len(certString) + len(keyString) + len(certPath) + len(keyPath) == 0 {
		// serve http
		return nil, nil
	} else if certString != "" && keyString != "" {
		tlsCertBytes, err := base64.StdEncoding.DecodeString(certString)
		if err != nil {
			return nil, fmt.Errorf("unable to base64 decode the TLS certificate: %v", err)
		}
		tlsKeyBytes, err := base64.StdEncoding.DecodeString(keyString)
		if err != nil {
			return nil, fmt.Errorf("unable to base64 decode the TLS private key: %v", err)
		}

		cert, err := tls.X509KeyPair(tlsCertBytes, tlsKeyBytes)
		if err != nil {
			return nil, fmt.Errorf("unable to load X509 key pair: %v", err)
		}
		return &cert, nil
	} else if certPath != "" && keyPath != "" {
		cert, err := tls.LoadX509KeyPair(certPath, keyPath)
		if err != nil {
			return nil, fmt.Errorf("unable to load X509 key pair from files: %v", err)
		}
		return &cert, nil
	}
	// serve http
	logger.Warnln("TLS requires both cert and key to be specified. Fall back to serving HTTP")
	return nil, nil
}

@fredbi
Copy link
Contributor Author

fredbi commented Aug 31, 2018

len(certString) + len(keyString) + len(certPath) + len(keyPath) == 0
looks like an elegant construct... it is actually slower (i.e. less efficient) than the ... != "" &&...

@aeneasr
Copy link
Member

aeneasr commented Aug 31, 2018

Feel free to choose either one. You can also do certString + keyString + certPath + keyPath == "" iirc :D

* allows cert&key to be specified with file

Signed-off-by: Frederic BIDON <[email protected]>
@aeneasr
Copy link
Member

aeneasr commented Aug 31, 2018

Awesome! Thank you :)

@aeneasr aeneasr merged commit c763152 into ory:master Aug 31, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants