mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
server: add authentication based on TLS
* identifier: add TLSBased This is only the identifier, the server setup still has to be done. Note that it diverges a little from what was proposed in the issue: not every client cert needs to have a CN record -- so instead, we'll use whatever is the cert's subject as client identity. * Drive-by fix: identifier_test: don't use same package for TokenBased tests. * server: require and verify client cert for AuthenticationTLS * server: allow setting CA pool via --tls-ca-cert-file * server: expose new authentication via parameter * [nit] server: simplify getListenerForHTTPServer * server_test: use httptest for integration-y TLS tests * book/security: mention TLS authn with example Signed-off-by: Stephan Renatus <srenatus@chef.io>
This commit is contained in:
committed by
Torin Sandall
parent
85b1931abf
commit
3286c39822
+29
-4
@@ -7,14 +7,17 @@ package cmd
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/open-policy-agent/opa/runtime"
|
||||
"github.com/open-policy-agent/opa/server"
|
||||
"github.com/open-policy-agent/opa/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,14 +29,14 @@ const (
|
||||
func init() {
|
||||
|
||||
var serverMode bool
|
||||
var tlsCertFile string
|
||||
var tlsPrivateKeyFile string
|
||||
var tlsCertFile, tlsPrivateKeyFile, tlsCACertFile string
|
||||
var ignore []string
|
||||
|
||||
authentication := util.NewEnumFlag("off", []string{"token", "off"})
|
||||
authentication := util.NewEnumFlag("off", []string{"token", "tls", "off"})
|
||||
|
||||
authenticationSchemes := map[string]server.AuthenticationScheme{
|
||||
"token": server.AuthenticationToken,
|
||||
"tls": server.AuthenticationTLS,
|
||||
"off": server.AuthenticationOff,
|
||||
}
|
||||
|
||||
@@ -95,6 +98,15 @@ the data document with the following syntax:
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if tlsCACertFile != "" {
|
||||
pool, err := loadCertPool(tlsCACertFile)
|
||||
if err != nil {
|
||||
fmt.Println("error:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
params.CertPool = pool
|
||||
}
|
||||
|
||||
params.Authentication = authenticationSchemes[authentication.String()]
|
||||
params.Authorization = authorizationScheme[authorization.String()]
|
||||
params.Certificate = cert
|
||||
@@ -137,6 +149,7 @@ the data document with the following syntax:
|
||||
runCommand.Flags().MarkDeprecated("server-diagnostics-buffer-size", "use decision logging instead")
|
||||
runCommand.Flags().StringVarP(&tlsCertFile, "tls-cert-file", "", "", "set path of TLS certificate file")
|
||||
runCommand.Flags().StringVarP(&tlsPrivateKeyFile, "tls-private-key-file", "", "", "set path of TLS private key file")
|
||||
runCommand.Flags().StringVarP(&tlsCACertFile, "tls-ca-cert-file", "", "", "set path of TLS CA cert file")
|
||||
runCommand.Flags().VarP(authentication, "authentication", "", "set authentication scheme")
|
||||
runCommand.Flags().VarP(authorization, "authorization", "", "set authorization scheme")
|
||||
runCommand.Flags().VarP(logLevel, "log-level", "l", "set log level")
|
||||
@@ -177,3 +190,15 @@ func loadCertificate(tlsCertFile, tlsPrivateKeyFile string) (*tls.Certificate, e
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func loadCertPool(tlsCACertFile string) (*x509.CertPool, error) {
|
||||
caCertPEM, err := ioutil.ReadFile(tlsCACertFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read CA cert file: %v", err)
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
if ok := pool.AppendCertsFromPEM(caCertPEM); !ok {
|
||||
return nil, fmt.Errorf("failed to parse CA cert %q", tlsCACertFile)
|
||||
}
|
||||
return pool, nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,13 @@ startup:
|
||||
OPA will exit immediately with a non-zero status code if only one of these flags
|
||||
is specified.
|
||||
|
||||
Note that for using TLS-based authentication, a CA cert file can be provided:
|
||||
|
||||
- ``--tls-ca-cert-file=<path>`` specifies the path of the file containing the CA cert.
|
||||
|
||||
If provided, it will be used to validate clients' TLS certificates when using TLS
|
||||
authentication (see below).
|
||||
|
||||
By default, OPA ignores insecure HTTP connections when TLS is enabled. To allow
|
||||
insecure HTTP connections in addition to HTTPS connections, provide another
|
||||
listening address with `--addr`. For example:
|
||||
@@ -98,6 +105,17 @@ and provide to the authorization handler. When you use the `token`
|
||||
authentication, you must configure an authorization policy that checks the
|
||||
tokens. If the client does not supply a Bearer token, the `input.identity`
|
||||
value will be undefined when the authorization policy is evaluated.
|
||||
- Client TLS certificates: Client TLS authentication is enabled by starting
|
||||
OPA with ``--authentication=tls``. When this authentication mode is enabled,
|
||||
OPA will require all clients to provide a client certificate. It is verified
|
||||
against the CA certificate(s) provided via `--tls-ca-cert-path`. Upon successful
|
||||
verification, the `input.identity` value is set to the TLS certificate's
|
||||
subject.
|
||||
|
||||
Note that TLS authentication does not disable non-HTTPS listeners. To ensure
|
||||
that all your communication is secured, it should be paired with an
|
||||
authorization policy (see below) that at least requires the client identity
|
||||
(`input.identity`) to _be set_.
|
||||
|
||||
For authorization, OPA relies on policy written in Rego. Authorization is
|
||||
enabled by starting OPA with ``--authorization=basic``.
|
||||
@@ -198,6 +216,8 @@ HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### Token-based Authentication Example
|
||||
|
||||
When Bearer tokens are used for authentication, the policy should at minimum
|
||||
validate the identity:
|
||||
|
||||
@@ -276,3 +296,127 @@ identity_rights[right] { # Right is in the identity_rights set if...
|
||||
right = rights[role] # Role has rights defined.
|
||||
}
|
||||
```
|
||||
|
||||
### TLS-based Authentication Example
|
||||
|
||||
To set up authentication based on TLS, we will need three certificates:
|
||||
|
||||
1. the CA cert (self-signed),
|
||||
2. the server cert (signed by the CA), and
|
||||
3. the client cert (signed by the CA).
|
||||
|
||||
These are example invocations using `openssl`.
|
||||
Don't use these in production, the key sizes are only good for demonstration purposes.
|
||||
|
||||
Note that we're creating an extra client, which has a certificate signed by the proper
|
||||
CA, but will later be used to illustrate the authorization policy.
|
||||
|
||||
```bash
|
||||
# CA
|
||||
openssl genrsa -out ca-key.pem 2048
|
||||
openssl req -x509 -new -nodes -key ca-key.pem -days 1000 -out ca.pem -subj "/CN=my-ca"
|
||||
|
||||
# client 1
|
||||
openssl genrsa -out client-key.pem 2048
|
||||
openssl req -new -key client-key.pem -out csr.pem -subj "/CN=my-client"
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 1000
|
||||
|
||||
# client 2
|
||||
openssl genrsa -out client-key-2.pem 2048
|
||||
openssl req -new -key client-key-2.pem -out csr.pem -subj "/CN=my-client-2"
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert-2.pem -days 1000
|
||||
|
||||
# create server cert with IP and DNS SANs
|
||||
cat <<EOF >req.cnf
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[req_distinguished_name]
|
||||
|
||||
[v3_req]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = opa.example.com
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
openssl genrsa -out server-key.pem 2048
|
||||
openssl req -new -key server-key.pem -out csr.pem -subj "/CN=my-server" -config req.cnf
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 1000 -extensions v3_req -extfile req.cnf
|
||||
```
|
||||
|
||||
We also create a simple authorization policy file, called `check.rego`:
|
||||
```ruby
|
||||
package system.authz
|
||||
|
||||
# client_cns may defined in policy or pushed into OPA as data.
|
||||
client_cns = {
|
||||
"my-client": true
|
||||
}
|
||||
|
||||
default allow = false
|
||||
|
||||
allow { # Allow request if
|
||||
split(input.identity, "=", ["CN", cn]) # the cert subject is a CN, and
|
||||
client_cns[cn] # the name is a known client.
|
||||
}
|
||||
```
|
||||
|
||||
Now, we're ready to starting the server with `-authentication=tls` and the
|
||||
certificate-related parameters:
|
||||
```console
|
||||
$ opa run -s \
|
||||
--tls-cert-file server-cert.pem \
|
||||
--tls-private-key-file server-key.pem \
|
||||
--tls-ca-cert-file ca.pem \
|
||||
--authentication=tls \
|
||||
--authorization=basic \
|
||||
-a https://127.0.0.1:8181 \
|
||||
check.rego
|
||||
INFO[2019-01-14T10:24:52+01:00] First line of log stream. addrs="[https://127.0.0.1:8181]" insecure_addr=
|
||||
```
|
||||
|
||||
We can use `curl` to validate our TLS-based authentication setup:
|
||||
|
||||
First, we use the client certificate that was signed by the CA, and has a subject
|
||||
matching our authorization policy:
|
||||
|
||||
```console
|
||||
$ curl --key client-key.pem \
|
||||
--cert client-cert.pem \
|
||||
--cacert ca.pem \
|
||||
--resolve opa.example.com:8181:127.0.0.1 \
|
||||
https://opa.example.com:8181/v1/data
|
||||
{"result":{}}
|
||||
```
|
||||
|
||||
Note that we're passing the CA cert to curl -- this is done to have curl accept
|
||||
the server's certificate, which has been signed by our CA cert.
|
||||
|
||||
Since we've setup an IP SAN, we may also `curl https://127.0.0.1:8181/v1/data`
|
||||
directly. (To keep our examples focused, we'll do that from here on.)
|
||||
|
||||
Using a valid certificate whose subject will be declined by our authorization
|
||||
policy:
|
||||
|
||||
```console
|
||||
$ curl --key client-key-2.pem \
|
||||
--cert client-cert-2.pem \
|
||||
--cacert ca.pem \
|
||||
https://127.0.0.1:8181/v1/data
|
||||
{
|
||||
"code": "unauthorized",
|
||||
"message": "request rejected by administrative policy"
|
||||
}
|
||||
```
|
||||
|
||||
Finally, we'll attempt to query without a client certificate:
|
||||
```console
|
||||
$ curl --cacert ca.pem https://127.0.0.1:8181/v1/data
|
||||
curl: (35) error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
|
||||
```
|
||||
|
||||
As you can see, TLS-based authentication disallows these request completely.
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -71,6 +72,9 @@ type Params struct {
|
||||
// is nil, the server will NOT use TLS.
|
||||
Certificate *tls.Certificate
|
||||
|
||||
// CertPool holds the CA certs trusted by the OPA server.
|
||||
CertPool *x509.CertPool
|
||||
|
||||
// HistoryPath is the filename to store the interactive shell user
|
||||
// input history.
|
||||
HistoryPath string
|
||||
@@ -235,6 +239,7 @@ func (rt *Runtime) StartServer(ctx context.Context) {
|
||||
WithAddresses(*rt.Params.Addrs).
|
||||
WithInsecureAddress(rt.Params.InsecureAddr).
|
||||
WithCertificate(rt.Params.Certificate).
|
||||
WithCertPool(rt.Params.CertPool).
|
||||
WithAuthentication(rt.Params.Authentication).
|
||||
WithAuthorization(rt.Params.Authorization).
|
||||
WithDiagnosticsBuffer(rt.Params.DiagnosticsBuffer).
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package identifier
|
||||
package identifier_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/server/identifier"
|
||||
)
|
||||
|
||||
type mockHandler struct {
|
||||
@@ -15,13 +17,13 @@ type mockHandler struct {
|
||||
}
|
||||
|
||||
func (h *mockHandler) ServeHTTP(_ http.ResponseWriter, r *http.Request) {
|
||||
h.identity, h.defined = Identity(r)
|
||||
h.identity, h.defined = identifier.Identity(r)
|
||||
}
|
||||
|
||||
func TestTokenBased(t *testing.T) {
|
||||
|
||||
mock := &mockHandler{}
|
||||
handler := NewTokenBased(mock)
|
||||
handler := identifier.NewTokenBased(mock)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "/foo/bar/baz", nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
*.srl
|
||||
*.cnf
|
||||
csr.pem
|
||||
ca-key.pem
|
||||
ca.pem
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC5DCCAcygAwIBAgIJAJEUtRCBCdAGMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
|
||||
BAMMBW15LWNhMB4XDTE5MDExMTA5MjM1NFoXDTIxMTAwNzA5MjM1NFowFDESMBAG
|
||||
A1UEAwwJbXktY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
2niMJJAlwthZjF2hT8IoaQ1FwZ1DyDPh0HBWRNQxAW0yNZVaeLUEwt9U5FkZiusr
|
||||
bKDI0jD0MOVWQw1s8oj3ys7Zxei9B8c0KBhnXDbkKdFKs5uzgUMnJyyAwjRi8T3P
|
||||
tyQtKK6phTBwseBEAXleoLYTUWAE9Q6W1wf9PHiY6Rak1HEUOU2AoV5gmZd8odKo
|
||||
gn+5bmgm6so6BXty7DQod4J2tnq8/TC/1HM7TN+N0eaLtyjhmbU9TA/C7yeil4Fm
|
||||
i3Sk+P/5CprLnPEGN3fXs5RAMk7PxHR+NhEIWMeNKzAUDMQZ662cWAJRe5L6l8aB
|
||||
O4zN7vzcJLyiHfZMJEqzSQIDAQABoz0wOzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF
|
||||
4DAhBgNVHREEGjAYghZjbGllbnQub3BhLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEB
|
||||
CwUAA4IBAQAYtVMyy799xyAbMzn0EuoFmrNaiuCWkNC4NV6I7CotEnBJqeCIR7LU
|
||||
VxHOGPLeXlLki1rDx1elTNY3HuSSEyCTSAQc9thhVoBlnndHnTF+sTEiFBAZxrjw
|
||||
+j7Kdh9AEAMOTAl/CnU8mziIDaoGLDEckpi32QzgGht4yIetNR85R6Y1J4RhuEI9
|
||||
cNMu5hMbEhL9L6wcIWEn9w9Y6bYqBHPWrprUG9AjTteP2CRadqYiHmbo8FG6Sor/
|
||||
jGIChe39L/fL6mG8bT4ageZdTWlA6fr+jcbnYFG+zhT9KhmUVlo7G7OMvgq81lsF
|
||||
nCDsGTDE/U424T0s8uWw8P/WmTXJihr/
|
||||
-----END CERTIFICATE-----
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
#!/bin/bash
|
||||
# taken from
|
||||
# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22
|
||||
|
||||
cat <<EOF >req.cnf
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[req_distinguished_name]
|
||||
|
||||
[v3_req]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = client.opa.example.com
|
||||
EOF
|
||||
|
||||
openssl genrsa -out ca-key.pem 2048
|
||||
openssl req -x509 -new -nodes -key ca-key.pem -days 1000 -out ca.pem -subj "/CN=my-ca"
|
||||
|
||||
openssl genrsa -out key.pem 2048
|
||||
openssl req -new -key key.pem -out csr.pem -subj "/CN=my-client" -config req.cnf
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cn-cert.pem -days 1000 -extensions v3_req -extfile req.cnf
|
||||
|
||||
openssl req -new -key key.pem -out csr.pem -subj "/O=Torchwood/OU=opa-client-01" -config req.cnf
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out ou-cert.pem -days 1000 -extensions v3_req -extfile req.cnf
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA2niMJJAlwthZjF2hT8IoaQ1FwZ1DyDPh0HBWRNQxAW0yNZVa
|
||||
eLUEwt9U5FkZiusrbKDI0jD0MOVWQw1s8oj3ys7Zxei9B8c0KBhnXDbkKdFKs5uz
|
||||
gUMnJyyAwjRi8T3PtyQtKK6phTBwseBEAXleoLYTUWAE9Q6W1wf9PHiY6Rak1HEU
|
||||
OU2AoV5gmZd8odKogn+5bmgm6so6BXty7DQod4J2tnq8/TC/1HM7TN+N0eaLtyjh
|
||||
mbU9TA/C7yeil4Fmi3Sk+P/5CprLnPEGN3fXs5RAMk7PxHR+NhEIWMeNKzAUDMQZ
|
||||
662cWAJRe5L6l8aBO4zN7vzcJLyiHfZMJEqzSQIDAQABAoIBAQDBQl4GghVFVYlx
|
||||
p+no2kJRG9KXQX0SfwLFFnraDDMFpgkCaYpMuSTrFhDMpxz3TK1vPJQpi/CXyGgU
|
||||
jK3RpuQ8Xds7PXTqiodS6LOWWWBgtam1VIjoUfUyrCWCpkDYUuuKgNAJ6ug+z+kB
|
||||
EPhXrXvOAwL3u07nUO6SbZjQg4YQubpT7znOzAaqE+33sCDsP/tdvliYdm18udvw
|
||||
0TCTGUwEPGVfdYsSO8iHDikmaIFAjepd+w+PytsdCpzb7YjThHJu0SzvA3DozBWO
|
||||
V0OpEX2tst48/HVYv7UrLeJXdizaa37o1v8Bxsai0p9WW9PY2376EglxPTf7EhTp
|
||||
ANjrEFTZAoGBAPg3D2VBgETawknZHYOH+Zh0gTEEEOZkC+cJmgAVvVdjfIJXvJ8u
|
||||
Rfrb13hCR2VS6xoymN6w8FTS1UTVBBV6IXssrFinWwuNQaoa3qrkmlNVl2nRQxvq
|
||||
3OmqyckWXNqwBWL+Hhn0sfhD+b55C9evrcW2tf/6qj9w6r1qPle4D0NTAoGBAOFS
|
||||
qy5ECUyZddJTpRhEDMOneFqMsymD28G6fuitACaxeqOAEaAMTSZDuf3JS6JkpnQS
|
||||
EqrR1PEzFu2rKzRw/rX4GYplH/7gvimGd1hvLtftDCSMOFXwVmdgKHZjiqg74Nzl
|
||||
QWH7kUfjNQgTldTFWzDLfuyIStXAv76f24UOsRdzAoGAaXyc4l9v79M4dsH6tQd4
|
||||
n74DmZ0swX0LQejmtdqHWThClfJLiyrTOsVrUQR56ynOGJggN6Piv2nKkTImRipd
|
||||
SEe4BwU4wDQMEArTTrVQkNHzQ1lXt+mccQHQN9F1LMtZvrRYfpdreyMIZFZ1Hfjf
|
||||
VQNNXbhd2hBW8qDQVd83PVkCgYAh9Gc/bZlJJccPjvNOGNMjmNUWMCW/l9NB+myt
|
||||
e4SOUCh/Awmk6LWnkoUwrWjsa+Z5j0+o1j4UqvJFlonIOU7o9R5EMMEFk7CUaWMK
|
||||
vJZ+i4ZM66SBrtoWcfMnBBEdEQjtwM59iX93KdIQCYOGsMbxL3lNA6zjUUyT2Vsn
|
||||
TfN56QKBgAEGdgdbiidvZiifEKtrE7/dEOJvGa/d8zTAfyURQwqOSSN1MhECLyo4
|
||||
NBHjcH2AOW+lmwwCv5MyCX9VX8hUIVUllODcnU4ssMmliZmi6i6bqW+/zJ+w3p+F
|
||||
RM3yPNTMHhM6IBgmXam7ZApN4NLdU4O5oZsLPS8tTQIWObP/M/nd
|
||||
-----END RSA PRIVATE KEY-----
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC/DCCAeSgAwIBAgIJAJEUtRCBCdAHMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
|
||||
BAMMBW15LWNhMB4XDTE5MDExMTA5MjM1NFoXDTIxMTAwNzA5MjM1NFowLDESMBAG
|
||||
A1UECgwJVG9yY2h3b29kMRYwFAYDVQQLDA1vcGEtY2xpZW50LTAxMIIBIjANBgkq
|
||||
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2niMJJAlwthZjF2hT8IoaQ1FwZ1DyDPh
|
||||
0HBWRNQxAW0yNZVaeLUEwt9U5FkZiusrbKDI0jD0MOVWQw1s8oj3ys7Zxei9B8c0
|
||||
KBhnXDbkKdFKs5uzgUMnJyyAwjRi8T3PtyQtKK6phTBwseBEAXleoLYTUWAE9Q6W
|
||||
1wf9PHiY6Rak1HEUOU2AoV5gmZd8odKogn+5bmgm6so6BXty7DQod4J2tnq8/TC/
|
||||
1HM7TN+N0eaLtyjhmbU9TA/C7yeil4Fmi3Sk+P/5CprLnPEGN3fXs5RAMk7PxHR+
|
||||
NhEIWMeNKzAUDMQZ662cWAJRe5L6l8aBO4zN7vzcJLyiHfZMJEqzSQIDAQABoz0w
|
||||
OzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAhBgNVHREEGjAYghZjbGllbnQub3Bh
|
||||
LmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA4rCzJLbAJ7hBdsoAn7vmn
|
||||
RW7ut2JkUxrzxj7AaUd8p3wUddLzN8xkdAhNhFVhBS3lsVFyV+00uowvRqHiHCMz
|
||||
r5KbBR8ZHTyaITDI3nybzDUctkjaYUmZ4F8P70Byi8xWsbagQ126wucCE/nZUDoR
|
||||
jI87gn+TYL5w3mx5x0Sc/H69hwuTCNA5Q9PN8zANi5WupxHZpHWQdmkDzlbRSKRw
|
||||
z75fYFYL1sxpDnPNZ6/7bNh+/XgvORYU3l9QwIL/y+yrZb0U2u9nvacNnnE2R6Z3
|
||||
XYJBh0I/RC5G58e9pJJc23awVjqh3mFlZ0QZN8hKmoVg3WqoKJ3bJ9MmuS2J1K5k
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2019 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package identifier
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// TLSBased extracts the CN of the client's TLS ceritificate
|
||||
type TLSBased struct {
|
||||
inner http.Handler
|
||||
}
|
||||
|
||||
// NewTLSBased returns a new TLSBased object.
|
||||
func NewTLSBased(inner http.Handler) *TLSBased {
|
||||
return &TLSBased{
|
||||
inner: inner,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *TLSBased) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if tls := r.TLS; tls != nil {
|
||||
if certs := tls.PeerCertificates; certs != nil && len(certs) > 0 {
|
||||
r = SetIdentity(r, certs[0].Subject.ToRDNSequence().String())
|
||||
}
|
||||
}
|
||||
|
||||
h.inner.ServeHTTP(w, r)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2019 The OPA Authors. All rights reserved.
|
||||
// Use of this source code is governed by an Apache2
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package identifier_test
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/server/identifier"
|
||||
)
|
||||
|
||||
// Note: In these tests, we don't worry about the server actually verifying the
|
||||
// client's certs; that's done in a different place. We only request it, and
|
||||
// check what the identifier does with it.
|
||||
|
||||
func TestTLSBased(t *testing.T) {
|
||||
mock := &mockHandler{}
|
||||
handler := identifier.NewTLSBased(mock)
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
cert string
|
||||
key string
|
||||
expected string
|
||||
defined bool
|
||||
}{
|
||||
{
|
||||
desc: "no cert",
|
||||
},
|
||||
{
|
||||
desc: "cert with CN=<name>",
|
||||
cert: "testdata/cn-cert.pem",
|
||||
key: "testdata/key.pem",
|
||||
expected: "CN=my-client",
|
||||
defined: true,
|
||||
},
|
||||
{
|
||||
desc: "cert with long DN",
|
||||
cert: "testdata/ou-cert.pem",
|
||||
key: "testdata/key.pem",
|
||||
expected: "OU=opa-client-01,O=Torchwood",
|
||||
defined: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
// Note: some re-use happens if this server is outside of the tests loop,
|
||||
// causing weird overlaps. Let's keep setting up a fresh one in each
|
||||
// iteration to be safe.
|
||||
s := httptest.NewUnstartedServer(handler)
|
||||
s.TLS = &tls.Config{ClientAuth: tls.RequestClientCert}
|
||||
s.StartTLS()
|
||||
defer s.Close()
|
||||
c := s.Client() // trusts the httptest server's TLS cert
|
||||
|
||||
if tc.cert != "" && tc.key != "" {
|
||||
cert, err := tls.LoadX509KeyPair(tc.cert, tc.key)
|
||||
if err != nil {
|
||||
t.Fatalf("read test cert/key (%s/%s): %s", tc.cert, tc.key, err)
|
||||
}
|
||||
c.Transport.(*http.Transport).TLSClientConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
_, err := c.Get(s.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error in GET %s: %s", s.URL, err)
|
||||
}
|
||||
if mock.defined != tc.defined {
|
||||
t.Fatalf("Expected defined to be %v but got: %v", tc.defined, mock.defined)
|
||||
}
|
||||
|
||||
if mock.identity != tc.expected {
|
||||
t.Fatalf("Expected identity to be %s but got: %s", tc.expected, mock.identity)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
+20
-7
@@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
@@ -48,8 +49,9 @@ type AuthenticationScheme int
|
||||
|
||||
// Set of supported authentication schemes.
|
||||
const (
|
||||
AuthenticationOff AuthenticationScheme = iota
|
||||
AuthenticationToken = iota
|
||||
AuthenticationOff AuthenticationScheme = iota
|
||||
AuthenticationToken
|
||||
AuthenticationTLS
|
||||
)
|
||||
|
||||
// AuthorizationScheme enumerates the supported authorization schemes. The authorization
|
||||
@@ -58,8 +60,8 @@ type AuthorizationScheme int
|
||||
|
||||
// Set of supported authorization schemes.
|
||||
const (
|
||||
AuthorizationOff AuthorizationScheme = iota
|
||||
AuthorizationBasic = iota
|
||||
AuthorizationOff AuthorizationScheme = iota
|
||||
AuthorizationBasic
|
||||
)
|
||||
|
||||
// Set of handlers for use in the "handler" dimension of the duration metric.
|
||||
@@ -87,6 +89,7 @@ type Server struct {
|
||||
authentication AuthenticationScheme
|
||||
authorization AuthorizationScheme
|
||||
cert *tls.Certificate
|
||||
certPool *x509.CertPool
|
||||
mtx sync.RWMutex
|
||||
partials map[string]rego.PartialResult
|
||||
store storage.Store
|
||||
@@ -129,6 +132,8 @@ func (s *Server) Init(ctx context.Context) (*Server, error) {
|
||||
switch s.authentication {
|
||||
case AuthenticationToken:
|
||||
s.Handler = identifier.NewTokenBased(s.Handler)
|
||||
case AuthenticationTLS:
|
||||
s.Handler = identifier.NewTLSBased(s.Handler)
|
||||
}
|
||||
|
||||
txn, err := s.store.NewTransaction(ctx, storage.WriteParams)
|
||||
@@ -188,6 +193,12 @@ func (s *Server) WithCertificate(cert *tls.Certificate) *Server {
|
||||
return s
|
||||
}
|
||||
|
||||
// WithCertPool sets the server-side cert pool that the server will use.
|
||||
func (s *Server) WithCertPool(pool *x509.CertPool) *Server {
|
||||
s.certPool = pool
|
||||
return s
|
||||
}
|
||||
|
||||
// WithStore sets the storage used by the server.
|
||||
func (s *Server) WithStore(store storage.Store) *Server {
|
||||
s.store = store
|
||||
@@ -284,9 +295,7 @@ func (s *Server) getListenerForHTTPServer(u *url.URL) (Loop, error) {
|
||||
Handler: s.Handler,
|
||||
}
|
||||
|
||||
httpLoop := func() error { return httpServer.ListenAndServe() }
|
||||
|
||||
return httpLoop, nil
|
||||
return httpServer.ListenAndServe, nil
|
||||
}
|
||||
|
||||
func (s *Server) getListenerForHTTPSServer(u *url.URL) (Loop, error) {
|
||||
@@ -300,8 +309,12 @@ func (s *Server) getListenerForHTTPSServer(u *url.URL) (Loop, error) {
|
||||
Handler: s.Handler,
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{*s.cert},
|
||||
ClientCAs: s.certPool,
|
||||
},
|
||||
}
|
||||
if s.authentication == AuthenticationTLS {
|
||||
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
|
||||
}
|
||||
|
||||
httpsLoop := func() error { return httpsServer.ListenAndServeTLS("", "") }
|
||||
|
||||
|
||||
@@ -8,12 +8,16 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -2885,3 +2889,130 @@ func (m *mockResponseWriterConn) consumeQueryResultStream() ([]queryResultStream
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func TestAuthenticationTLS(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
store := inmem.New()
|
||||
m, err := plugins.New([]byte{}, "test", store)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := m.Start(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams)
|
||||
|
||||
authzPolicy := `package system.authz
|
||||
import input.identity
|
||||
default allow = false
|
||||
allow {
|
||||
identity = "CN=my-client"
|
||||
}`
|
||||
|
||||
if err := store.UpsertPolicy(ctx, txn, "test", []byte(authzPolicy)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err := store.Commit(ctx, txn); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
caCertPEM, err := ioutil.ReadFile("testdata/ca.pem")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pool := x509.NewCertPool()
|
||||
if ok := pool.AppendCertsFromPEM(caCertPEM); !ok {
|
||||
t.Fatal("failed to parse CA cert")
|
||||
}
|
||||
cert, err := tls.LoadX509KeyPair("testdata/server-cert.pem", "testdata/server-key.pem")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
server, err := New().
|
||||
WithAddresses([]string{"https://127.0.0.1:8182"}).
|
||||
WithStore(store).
|
||||
WithManager(m).
|
||||
WithCertificate(&cert).
|
||||
WithCertPool(pool).
|
||||
WithAuthentication(AuthenticationTLS).
|
||||
WithAuthorization(AuthorizationBasic).
|
||||
Init(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Replicating some of what happens in the server's HTTPS listener
|
||||
s := httptest.NewUnstartedServer(server.Handler)
|
||||
s.TLS = &tls.Config{
|
||||
Certificates: []tls.Certificate{cert},
|
||||
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||
ClientCAs: pool,
|
||||
}
|
||||
s.StartTLS()
|
||||
defer s.Close()
|
||||
endpoint := s.URL + "/v1/data/foo"
|
||||
|
||||
t.Run("happy path", func(t *testing.T) {
|
||||
clientCert, err := tls.LoadX509KeyPair("testdata/client-cert.pem", "testdata/client-key.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("read test client cert/key: %v", err)
|
||||
}
|
||||
c := http.DefaultClient
|
||||
tr := http.DefaultTransport.(*http.Transport)
|
||||
tr.TLSClientConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{clientCert},
|
||||
RootCAs: pool,
|
||||
}
|
||||
c.Transport = tr
|
||||
|
||||
resp, err := c.Get(endpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("GET: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %s", resp.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("authn successful, authz failed", func(t *testing.T) {
|
||||
clientCert, err := tls.LoadX509KeyPair("testdata/client-cert-2.pem", "testdata/client-key-2.pem")
|
||||
if err != nil {
|
||||
t.Fatalf("read test client cert/key: %v", err)
|
||||
}
|
||||
c := http.DefaultClient
|
||||
tr := http.DefaultTransport.(*http.Transport)
|
||||
tr.TLSClientConfig = &tls.Config{
|
||||
Certificates: []tls.Certificate{clientCert},
|
||||
RootCAs: pool,
|
||||
}
|
||||
c.Transport = tr
|
||||
|
||||
resp, err := c.Get(endpoint)
|
||||
if err != nil {
|
||||
t.Fatalf("GET: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusUnauthorized {
|
||||
t.Errorf("expected status 401, got %s", resp.Status)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("client trusts server, but doesn't provide client cert", func(t *testing.T) {
|
||||
c := http.DefaultClient
|
||||
tr := http.DefaultTransport.(*http.Transport)
|
||||
tr.TLSClientConfig = &tls.Config{
|
||||
RootCAs: pool,
|
||||
}
|
||||
c.Transport = tr
|
||||
|
||||
_, err := c.Get(endpoint)
|
||||
if _, ok := err.(*url.Error); !ok {
|
||||
t.Errorf("expected *url.Error, got %T: %v", err, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
*.srl
|
||||
*.cnf
|
||||
csr.pem
|
||||
ca-key.pem
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC8zCCAdugAwIBAgIJAMHu6iWKhLk/MA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
|
||||
BAMMBW15LWNhMB4XDTE5MDExMTExMjI0MFoXDTIxMTAwNzExMjI0MFowEDEOMAwG
|
||||
A1UEAwwFbXktY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7RmRo
|
||||
rdbz/cObXySp8ul4430gWuQxo0GKplxf4TECSpNxY3HUZ3bdexi/d06ZQSdxPbKg
|
||||
DXwvuzNz2NrBk++CgSYU8clwiFpjB0WIzQZDe1rMRSYfD75n6AcqXj+0uz/M+bEy
|
||||
HQUGgRqLdmWwLB7APvgqHXbjpPVHWi1iQDWsflmXK5FzHwIOc20H0CgszfpTY3q4
|
||||
uDbeXunlwsdpBsT0rV6F4sipO/qpbPuXQ54pXE7OcFyvEJKE++NXOEGkxML1hLws
|
||||
SI3kVYgkHVp9uRNm7+wijTDY8xdNqGEiP9nEg9uFbR0hvhIWWqmHY4vOLiCKZTYC
|
||||
V8baC7Y3g6Osi+2hAgMBAAGjUDBOMB0GA1UdDgQWBBRQEkQ+CO07gADyd2girlSx
|
||||
VTS27jAfBgNVHSMEGDAWgBRQEkQ+CO07gADyd2girlSxVTS27jAMBgNVHRMEBTAD
|
||||
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCb532T6FXc83aahdyhd1PgqqrQQqb80j5H
|
||||
ep5nkLPUwf4qOfMqVG75n7WmlUKe7eVU0R4vp1frUGazh6/GV9ZoaEp2/Gh/JIUc
|
||||
KEz2a6zvu8+13vRTfp5B3P06ek0FaqAljer6oSfV6/tMTQ9ArLezVdnvfX4+gf7h
|
||||
2443SDo6g0LeFXc4CeIrff1b3sOqpvn2F+b87qQFy0EDRbF2i8p2ZmheompWPlmw
|
||||
d0LXd8aXHWOzfpUkI5mLMiN7Ft1j8k701SeuOsawIDmTy3looN+tq1kl7cJswWrs
|
||||
uwK+XpX0qg815BB5jcEwOvpk+bMKzBhkIf8VGFBL88A6anwuX6k7
|
||||
-----END CERTIFICATE-----
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICojCCAYoCCQDOA0JzmDQJATANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVt
|
||||
eS1jYTAeFw0xOTAxMTExMTIyNDBaFw0yMTEwMDcxMTIyNDBaMBYxFDASBgNVBAMM
|
||||
C215LWNsaWVudC0yMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv4WZ
|
||||
biCVj4yfITYSH5Bocph7xy+gvsCVYUWZU8Nulgo3OLx4q1qRKMjGqDvhMK6BvPoE
|
||||
bUoMRHn8Jk9ABIMdLKBwpby9oovZOwSYh8NLrkmaUlzTDQlzDEdKYf428b0n6HOZ
|
||||
045GU5Tvm06ER2fa/qqlsCfP6adr9+u0KnL9KIZkh1PM69vUF+JteXAyDnYrciIN
|
||||
RcEq9Fb6gcMG3mBr0jAKVDA7KtFf/diPfEtKxtASYq98g7AY5JgjI8lovGkXQMJc
|
||||
kCQxWQ+uaX1tr7tRpNLMM7n6MTV3HvKAp8zCjijOcog/7h0atd3n3u2tCbchEPnT
|
||||
+Jt/ikFa3VFs3oJzFwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAtTXalo8+0Cg7d
|
||||
9gzMODAgCSynumzTmOyH1IYL8UmNgHImG26EpDmda2EKsIDTcgQV1dKDKPCTJwoB
|
||||
akBwb9+EwqEMd7h7bGKhPizg7tguIz4h08UYZkoVcyEiU8Azc6X8dLcYjKNTl5XH
|
||||
wIX8wEt+yU973bWKqFuMqNT7Ex70efDX2DVa5ZOrIKltyx5K0cv0qQZ7HZULIMyh
|
||||
iWGC3LiB48gMRx7CkyCiBvCAg/Ux8/dNi87p72pzerpyRJt0VbU94SW9tsVLWeOj
|
||||
KtGdnzH2J9Uop4+B7Aa/TBqsXue0JFJRkjf5YbVkY1b6SFPQA7t2mbf42DILC7Ff
|
||||
rmNWH95k
|
||||
-----END CERTIFICATE-----
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICoDCCAYgCCQDOA0JzmDQJADANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVt
|
||||
eS1jYTAeFw0xOTAxMTExMTIyNDBaFw0yMTEwMDcxMTIyNDBaMBQxEjAQBgNVBAMM
|
||||
CW15LWNsaWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALUVYoeA
|
||||
pg0UEL+ixFwRU16ukrE1CMTSCGAVYaadxMjaFd8QNXzP+lK3eyttEFPoVRZJ6z12
|
||||
JxUSRYhiIai2kII2ySrkqWvD9G6To/YJdgw9HdDjkibZxG536jQUAx3NHZib7qL2
|
||||
AGd9Sm1XJd9jgORlQ7e26V2y1KOE4v6GUGBzeWPhgYPsCKI16wdr40d9co/s6Ycb
|
||||
Ft90r3KdBLW60bCLBgJKYBo/1oVDiVJW8mY+eFwqqdm2G/Fftcyf7UySFCVE8WPg
|
||||
Xtb7M3nSlcD8Cc86Z93OfLd+E87THLqDg8vsp++yj3voHoDr4eyiItHVqklbM4Kb
|
||||
38JMTsR2UAif5YMCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAuTKYwIfvnWgL0Lw+
|
||||
oT054kwhcODyzQ0kHXNhxywPCEWRF8n9uDQOyy4nG7zHtIf2LG5iB/uwhS4QLpZ+
|
||||
k09rWI3+pVKyPOHTwBiUHmfKz5Neh+VGbY4hW4TvQT/XQgTTAcvc3dMAP/0KN9wp
|
||||
YjE9COBVNZT1pxDooczQA8KwhIIQY8v8TnFyh0tOGX9o4F6VtLoEPrvfphh4ozNh
|
||||
2cP6HD+BvygsK53jl7xN94RQeAq4PCw+GZR47JN/QCe9tkHVLe2McDZoMsxCtoBX
|
||||
gK4rc4gLmUaSdsrSnU9isJM5nUz+6m705dzCjJZhM70LYbWiqbChs+YqCHsJChOj
|
||||
PmigKg==
|
||||
-----END CERTIFICATE-----
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAv4WZbiCVj4yfITYSH5Bocph7xy+gvsCVYUWZU8Nulgo3OLx4
|
||||
q1qRKMjGqDvhMK6BvPoEbUoMRHn8Jk9ABIMdLKBwpby9oovZOwSYh8NLrkmaUlzT
|
||||
DQlzDEdKYf428b0n6HOZ045GU5Tvm06ER2fa/qqlsCfP6adr9+u0KnL9KIZkh1PM
|
||||
69vUF+JteXAyDnYrciINRcEq9Fb6gcMG3mBr0jAKVDA7KtFf/diPfEtKxtASYq98
|
||||
g7AY5JgjI8lovGkXQMJckCQxWQ+uaX1tr7tRpNLMM7n6MTV3HvKAp8zCjijOcog/
|
||||
7h0atd3n3u2tCbchEPnT+Jt/ikFa3VFs3oJzFwIDAQABAoIBAFlZy+Xr/7qT4V2O
|
||||
K1Bcf84Ow5h+6OB1WnuiC9FfWMVZCMBIdYeD3jsHyF6OaRXcJBz1C4GtTSHZQ0Dg
|
||||
Mj6oqMC8LTG1Z0dhNTxqfXrBKxqprfyE39WyNeFhlRs4jYukMu0omXhwZPDziAGN
|
||||
1Pq3Qh0Toe8X89DPUfi9FzXCpcb0dIMJQLUMt2PLXTlcx4PD8wXR9m5hJsuevgc5
|
||||
WEUCqS97F0d/4ilTVlUu9pbRF4KpPvbXabDi7xcyk5qQDRv7/4kVQrK0IbXRjFTo
|
||||
RRooip8o4lVy0pZN03mTEAZo4OZvJ/rOqfFmpZyBeJxIIgCrhtvTO8E1OmBkRrrl
|
||||
+20W/IkCgYEA823mJallv/CzC1mUBFA3ESughsvfhT6vkZ6q+MjYlRf2hKPWtfY+
|
||||
dA/wwkNIrjVsW/B+CPply/bYH4CY9VFYMstQI4VcsVDZF33Hc3yFWGQwgMwnnAh+
|
||||
OGsQrJ2sUcuHsoXrssFk9MdByc9kdLt7lBN5qvPsGaEIMbY4lEca3L0CgYEAyWl9
|
||||
1PTEF+IRufusLZrImFSl2DnMkkUNImvi80NMFqUFtof5c/sMWPs7ilvixFMFrANE
|
||||
Nd4isFA52Wl4PhDsRgkLZ/BSj7cAtX6dq74pTIotR6ApnV9+9MI2Q3QCuP2Bu4ck
|
||||
1qeAlffLTkx78lOyFtRfVXMH+DixZNdhq6DrzmMCgYAEYCNU5fiYPKFbQN7yPObg
|
||||
fiJCigOYh4nsWifElQeflnCt7av8VVLGD0tKkp8J0pgCBw6c5rNRuTwlRNB7CF1H
|
||||
fHQST2FdJpDgZThikQhskFB24DSOD3EFXpdMIwFn7vqfcNzNt8AyzioWkI3Ds2MD
|
||||
RF5ZfkzuIQDes+HMeIK5GQKBgQCqCxgjTKqph0F0BZitv/oPMZf634FpFEcsgm1O
|
||||
98grbhNOsanXU2JvVVxFSaJAId+uA+v4lpwuwuy0MylDLUJtNoGctPbfQp+km0s+
|
||||
jgOMDTSBPiUkKlWjCryJYk5SJCT9T/G4EB6tflD0v/n88tBuGcqK990IocSS4Iwj
|
||||
PG8ZBQKBgGwZfRkYKoVB4VjewHL1+JLPvWOcsCEc+3T6mkizxpPK7fewi2YChcCn
|
||||
q8YrTP41ZUlcwALbxsSD2SQPjcjFKhgiFOOezV3qJ/oDC4+SC0/1jAQHWHC20kKZ
|
||||
ZyWBL6FdR+yUY7Zafo16M49ioZh8hYrA6gYDC/Zm/sKXdIaTveQU
|
||||
-----END RSA PRIVATE KEY-----
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAtRVih4CmDRQQv6LEXBFTXq6SsTUIxNIIYBVhpp3EyNoV3xA1
|
||||
fM/6Urd7K20QU+hVFknrPXYnFRJFiGIhqLaQgjbJKuSpa8P0bpOj9gl2DD0d0OOS
|
||||
JtnEbnfqNBQDHc0dmJvuovYAZ31KbVcl32OA5GVDt7bpXbLUo4Ti/oZQYHN5Y+GB
|
||||
g+wIojXrB2vjR31yj+zphxsW33Svcp0EtbrRsIsGAkpgGj/WhUOJUlbyZj54XCqp
|
||||
2bYb8V+1zJ/tTJIUJUTxY+Be1vszedKVwPwJzzpn3c58t34TztMcuoODy+yn77KP
|
||||
e+gegOvh7KIi0dWqSVszgpvfwkxOxHZQCJ/lgwIDAQABAoIBAE4QqwpZdrx6owHK
|
||||
jLZYLcXKoqjMgTxAg6XQcemfaD9ibT1dwoFNM9BHE01UVk6mAVB7i3NSpWSbHOi/
|
||||
zVzrX38TWUQpkbRIpO6qrWubBo4eJuVvxgkDpoAtKAVNf05wp8qrpoQQY3o59EUQ
|
||||
5d3ZCq7rzPa9Xvuq5aqc1jL9+lapl5/hxXKeDXID8yVqUlpnDsf2KzsxH4Fxu1o5
|
||||
pdUbLLKTDGfrKHAiUq90z2uerFg3ENOeTlOhZSwJ5FzFx8BN2jajxpXG9EnphdJj
|
||||
VJg8Vas6bJzN729xEtzp4EIGHc+jtbLpCagi8OwZh0lf8ZsbFSMqLMIB3znsMYof
|
||||
40yKwFECgYEA4Ql+LmMBuweHSRlvgbh++zg65tztxBPUCOXdh75J3Sr4Lssox5T2
|
||||
lqZWKG7sijNwxN6U2TlSONK5wsMFtU1f+UMUGHiyLmTac0Tnp5CdVUM3yn+BwM1f
|
||||
Lww+uypBCWCEnFYSsl81gGiJeS80GA1MuNI+FtD92cZ2gfcIOCD5EksCgYEAzf+2
|
||||
DGwUpQGArHt8rXupx9Jx6UgbVxErH1VG0o8RU8qOovYBPFvVlB8xPL/OHUrUXE8C
|
||||
s4hDZ8TDP7uTtUzqVlr7nv7wAx1rIoTqbqaESoTsdklISG3KUMtYDTBDo7Zx3UZU
|
||||
R+mPqOVEbpEqM+B//R/he4Gdk8frxMonuNClNqkCgYAcnQVjRol4y3tDKy3Hc7sJ
|
||||
nFu48Qx2awMB4qBpWyOvMICACqrzvZghDaNU3s3KwMGs9pQ8jpJK0Vh3UC7Qn7b9
|
||||
Ta8ncWlOhtpT38YIw1WN+s8EO3Q3HKXmqIrtZ3D/jTsvWa5k6cA2xJP+Sac5C6/e
|
||||
rDTi183/O8HjwpJT+LVSwwKBgDncYV4L3aCbU3EfHl38JiwiSCymPEewYtRiN4pc
|
||||
9Rj8BCRxxNcXVF5OhJ3zoglIQ23KI5AZ7hTvh4gXP93kxM2MsLBwGypE0RrZCqAZ
|
||||
1X6451IyM0Nk0zbuZsUNkXIihMkKQBIS4G8oNph33crEfC61DbiLJ5+iv+5+ebnH
|
||||
oSBBAoGAWCYAO14A/UBx3EwPac4Tw22DhgC7AM8TBiMKpMsXNJrhoMfB2Jlx4p6e
|
||||
+B1EExck0fdp46FJaXFFEKmu0xAXZ8Xyi7N+IpDGhIVkHhBANdvoMmwdKKTPcajT
|
||||
5V40n97gka96YC8TPrWQrpEgFhTCNaFgEfzy2cixKjfNtiBMZQk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
# taken from
|
||||
# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22
|
||||
|
||||
cat <<EOF >req.cnf
|
||||
[req]
|
||||
req_extensions = v3_req
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[req_distinguished_name]
|
||||
|
||||
[v3_req]
|
||||
basicConstraints = CA:FALSE
|
||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
||||
subjectAltName = @alt_names
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = opa.example.com
|
||||
IP.1 = 127.0.0.1
|
||||
EOF
|
||||
|
||||
openssl genrsa -out ca-key.pem 2048
|
||||
openssl req -x509 -new -nodes -key ca-key.pem -days 1000 -out ca.pem -subj "/CN=my-ca"
|
||||
|
||||
openssl genrsa -out client-key.pem 2048
|
||||
openssl req -new -key client-key.pem -out csr.pem -subj "/CN=my-client"
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 1000
|
||||
|
||||
openssl genrsa -out client-key-2.pem 2048
|
||||
openssl req -new -key client-key-2.pem -out csr.pem -subj "/CN=my-client-2"
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert-2.pem -days 1000
|
||||
|
||||
openssl genrsa -out server-key.pem 2048
|
||||
openssl req -new -key server-key.pem -out csr.pem -subj "/CN=my-server" -config req.cnf
|
||||
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 1000 -extensions v3_req -extfile req.cnf
|
||||
Vendored
+18
@@ -0,0 +1,18 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC4zCCAcugAwIBAgIJAM4DQnOYNAkCMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
|
||||
BAMMBW15LWNhMB4XDTE5MDExMTExMjI0MFoXDTIxMTAwNzExMjI0MFowFDESMBAG
|
||||
A1UEAwwJbXktc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
|
||||
wLsRfXAK6EfjLNl+JPoPa0dYOigjqj3MDyMRjoYoOox0PD5oYVHQZ9vgtllihTxd
|
||||
q9aHnO1eW5dZungit3OiomC9dl9OTX1mVulO0U0T6wuksHC2z/SRnmKYYCO0yNqJ
|
||||
VVniZ8c6J2ZwtNUQytSMFPvOGWSK9z2jcyhoUCsg88+7xXmuKFGTHWhKBlkCg0DJ
|
||||
7FjN2PXJ3osQwjTSX8z2emyTvdJI/ZYBb1z64aysBLdV3QO+PIBYzhKlVuSBWNUQ
|
||||
F+JR30iUyeN8bWjMCGftLJc58bZsQsIBWcLJN+os/DWt2dvv2/TFMiwxSYKOy6rA
|
||||
bsOPb0vSRKjd8Th62VOlxQIDAQABozwwOjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIF
|
||||
4DAgBgNVHREEGTAXgg9vcGEuZXhhbXBsZS5jb22HBH8AAAEwDQYJKoZIhvcNAQEL
|
||||
BQADggEBAJq6pCjj6db/48oZoRUTdIfzpqk/cqWlvWIklVNX+2wWrZ4AGAro7G1z
|
||||
ppNL9nIfBp6gO62iotFCrudlrtq1M2YaiXXBGwVPZcQjThhVp/gd+1VNUL+0PB3Z
|
||||
8N5wgHch9mzMgHStfOLtwR6tFDGWXfcdIMwEHehZH5M0uQbJYEkBL5xh1DEuK0/c
|
||||
kTiSdb4vFCrX7C67YMzfCuoq5xGr5ZNU0NugzAmf/fZhFFmDVSr6/GfS02Ippfw8
|
||||
pBt42gFcEDPpK2wFhqMfiIIIvOvgtkjvx9ctY5boRnh1sZcayDMq/QfPOrsj55Vv
|
||||
Cq8Il7ESE2bJP1Bt9/rHLCKP6GKD6Jk=
|
||||
-----END CERTIFICATE-----
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAwLsRfXAK6EfjLNl+JPoPa0dYOigjqj3MDyMRjoYoOox0PD5o
|
||||
YVHQZ9vgtllihTxdq9aHnO1eW5dZungit3OiomC9dl9OTX1mVulO0U0T6wuksHC2
|
||||
z/SRnmKYYCO0yNqJVVniZ8c6J2ZwtNUQytSMFPvOGWSK9z2jcyhoUCsg88+7xXmu
|
||||
KFGTHWhKBlkCg0DJ7FjN2PXJ3osQwjTSX8z2emyTvdJI/ZYBb1z64aysBLdV3QO+
|
||||
PIBYzhKlVuSBWNUQF+JR30iUyeN8bWjMCGftLJc58bZsQsIBWcLJN+os/DWt2dvv
|
||||
2/TFMiwxSYKOy6rAbsOPb0vSRKjd8Th62VOlxQIDAQABAoIBAHFybCQrPLBoCGhR
|
||||
CfjYHRVn5J4vHHdMcv6CBQahH7L/FDiV/7kxgHxyKD8ONHj7BKRu3lrYOo+tcorZ
|
||||
xo24CoaMbVntVdk1NHV6VgPL3CqiKuoVHvcgHktd/BWzTupgATD1fNjbo/anTTaU
|
||||
qJLMNDzxz+qQjWBlrv3NTkHKcVYx7td4jkd8v3NurI9HmwABRKWTiZzKS/4mpIKS
|
||||
m/A7u4oM7GQAKKbHDYz2sGDp4BdrWKm4plTU3RRPfhpUFc0sy/6BXwY9nIRFO/aa
|
||||
UGub3ZgiW2GyXhmUzZIrs+SSOPNbHdf05225ajdh4tfyPcGetyWoa7rWI7au9dTn
|
||||
192zN2ECgYEA5LurcFmFU8pYr3jthKa6mSCDuIzniAMvcjnv6ociyMPpEff74nD7
|
||||
h8ETAKkQYDwJn1wjys2m5i/c3Uji72m4kDioCRLQY0xoC7X8/lXiSXQ7aiDNqxZW
|
||||
bDuZPl0ryYZmeiAn/vjOR9BnUFYuyyTs7z1K7w8vZe0JNnjF2kdZXT0CgYEA17Sz
|
||||
sTBjSPBeTRAweaMHBOSY04tuS8gOUxC9RHeWdtt0JDfGe6Hp8K99D0exUbAkX0B2
|
||||
hDxVk3+lq22H/wk7zP6WXPLLlyzyyeL1ZVNb2Br4mVcSjMnlIL235HrCOjMprVFa
|
||||
LBOzcMLtXSptDmvDdZmAUKr0HmBMlkOX8zZcAykCgYBSMVXzs1eDuoyP0YaYSkl3
|
||||
SQXMRWXVrD9abpNV1WWcezm/aTssLalVKP4pGJd33Vsf1r5N3ASDZuOY6N4TZgwa
|
||||
VyGa0RXs+MHSo3zb8AS2nHvVMSB2wDoh/nCcxmI7sn4UmIWGy+VkTPEzHyUFfqld
|
||||
dsr8iJphqAHNfyypuUXViQKBgBCsjT9jKCmZOxDl8XlVmtNMAGxJ2OrOuhy9rIPA
|
||||
YscpS58JGLSx3W2XgylNN23DGeyrBP5P06WlNl88Bkk2o4LDI0hoFEjJVrM4chO1
|
||||
D+Jyo0jnLC3p5WZUhASLYLwy/EPDiB7kHvjWeJa9EtUMi31psjuKG3jFpOXwr6xD
|
||||
RXWRAoGBAN7bGfYzHKhKiSI67PpeQ5S7KtK8/fi0wSuDoG1J6OkMBk35FJ6wPkPX
|
||||
iQVpMY0bvdQgwo0hug/3NQCKDKAnPLO9vpHZHlP+fn8F6Vq4/pvhvFSc9qihs37P
|
||||
A+kLIr1vKOL2MUIN/b3B4D6U27FwO7aJ5LYlAoPgNmMZTKSVAr42
|
||||
-----END RSA PRIVATE KEY-----
|
||||
Reference in New Issue
Block a user