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

Add OIDC config primitives #49

Merged
merged 66 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
e7d31cb
rebased
Oct 7, 2024
593126b
class vars
Oct 8, 2024
ad3c237
cleanup
Oct 8, 2024
770bc42
cleanup
Oct 8, 2024
8e999f3
adding comments
Oct 8, 2024
3e9da0e
comments
Oct 8, 2024
616e979
fixed yml file
Oct 8, 2024
1903d11
cleanup
Oct 8, 2024
806fca2
cleanup client configure
Oct 8, 2024
84b1638
comments
Oct 8, 2024
9d55d26
cleanup
Oct 9, 2024
6915b0e
fix provider addr
Oct 9, 2024
9ff6d1f
separate out issuer config
Oct 9, 2024
97fdf62
added comments
Oct 9, 2024
20cb10d
cleanup readme
Oct 9, 2024
8f6d97a
cleanup
Oct 9, 2024
e496ef6
fix for prod
Oct 10, 2024
d33a69c
fixed issuer
Oct 10, 2024
14326f0
cleanup
Oct 10, 2024
4957347
comment
Oct 10, 2024
f3a4828
cleanup local names
Oct 10, 2024
3d3d1fb
cleanup local names
Oct 10, 2024
5c59530
review fixes
Oct 11, 2024
52e2420
application.rb fixes
Oct 11, 2024
0617b30
application.rb fixes
Oct 11, 2024
71a2259
cleanup configs
Oct 11, 2024
289dea6
added initial_user()
Oct 11, 2024
87a56aa
don't refresh the provider unnecessarily
Oct 11, 2024
66c9bb1
cleanup
Oct 11, 2024
0d6bf1c
rubocop
Oct 11, 2024
bb90ac4
memoized oidc_provider
Oct 11, 2024
a5dfa94
cleanup
Oct 11, 2024
3897835
removed unneeded reader policy
Oct 11, 2024
9a49426
moved provider to test dir
Oct 11, 2024
9bf5124
init
Oct 15, 2024
a733396
moved provider
Oct 15, 2024
2a90392
refactored init code
Oct 15, 2024
56b6f76
rake task
Oct 16, 2024
52122d0
rake task
Oct 16, 2024
2793425
added Config
Oct 16, 2024
609cbcf
rake task working
Oct 16, 2024
a43272f
moved oidc comment block
Oct 16, 2024
0772fdd
removed utils/oidc.rb
Oct 16, 2024
3402488
cleanup
Oct 16, 2024
a474cbd
added tests
Oct 16, 2024
4f7d5d3
fixed token
Oct 16, 2024
2aba8fb
provider tests
Oct 16, 2024
7456b02
cleanup
Oct 17, 2024
3774510
fix test comments
Oct 17, 2024
caeb603
commented rake task
Oct 17, 2024
ed18d74
fixed initial user
Oct 17, 2024
bbb3255
cleanup review comments
Oct 17, 2024
5c5f3c5
rubocop
Oct 17, 2024
ca256c8
Merge branch 'main' into addOidcProviderRebase
GeorgeJahad Oct 17, 2024
70329c3
Because the "VAULT_SSL_CERT" env var is set, added ssl
Oct 18, 2024
de43ef3
updated Brakeman
Oct 18, 2024
8f195ca
added oidc provider ssl
Oct 18, 2024
a87e951
fixed up provider certs
Oct 18, 2024
6b866af
fixed oidc_provider for ssl
Oct 20, 2024
7ae52ba
fix for oidc_provider/ssl
Oct 21, 2024
5e0eda0
fixed issuer path
Oct 21, 2024
350d286
add comment for oidcProvider tls
Oct 21, 2024
dc4ece1
fixed issuer
Oct 21, 2024
879b0db
fixed comment
Oct 21, 2024
2f35777
rubocop
Oct 21, 2024
909516e
fixed readme
Oct 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or the host.
"forwardPorts": [3000, 5432, 8200],
"forwardPorts": [3000, 5432, 8200, 8300],

// Use 'postCreateCommand' to run commands after the container is created.
"postCreateCommand": "bundle install && rake db:setup",
"postCreateCommand": "bundle install && rake oidc_provider:configure && rake db:setup",

// Configure tool-specific properties.
// "customizations": {},
Expand Down
28 changes: 28 additions & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,34 @@ services:
"max_lease_ttl": "720h"
}

oidc_provider:
image: hashicorp/vault:latest
restart: unless-stopped
ports:
- 8300:8300
- 9443:9443
volumes:
- ../cert:/vault/cert
environment:
VAULT_DEV_ROOT_TOKEN_ID: root_token
VAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8300
VAULT_LOCAL_CONFIG: >
{
"listener": [
{
"tcp": {
"address": "0.0.0.0:9443",
"tls_disable": "0",
"tls_cert_file": "/vault/cert/oidc_provider.pem",
"tls_key_file": "/vault/cert/oidc_provider.key"
}
}
],
"default_lease_ttl": "168h",
"max_lease_ttl": "720h"
}


app_registry:
image: node:latest
restart: unless-stopped
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ GEM
bigdecimal (3.1.8)
bootsnap (1.18.4)
msgpack (~> 1.2)
brakeman (6.2.1)
brakeman (6.2.2)
racc
builder (3.3.0)
concurrent-ruby (1.3.4)
Expand Down
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,76 @@ rake configure:ssl

To use SSL in the devcontainer, edit `.devcontainer/docker-compose.yml` so
that the `app` service has `VAULT_ADDRESS` of `https://vault:8443`.

## OIDC configuration
The OIDC modules allow the assignment of a policy to an OIDC user, by
mapping that user's email address to a policy we create. They work as
follows:

OidcProvider.new.configure creates an OIDC provider
and user on a separate dedicated Vault instance. The user created has
a username/password/email address, that can be accessed with OIDC auth
from in the principal Vault instance.
Copy link
Collaborator

Choose a reason for hiding this comment

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

"from in"


Clients::Vault::configure_as_oidc_client creates an OIDC
client on our Vault instance. It connects to that provider just
created. When a user tries to auth, the client connects to the
provider, which opens up a browser window allowing the user to enter
their username/password.

On success, the provider returns an OIDC token, which includes the
user's email address.

The OIDC client has been configured to map that email address to an
entity in Vault, which has the policy which we want the user to have.

So the mapping goes from the email address on the provider, to the
policy in Vault.

Note that this provider is mainly meant to be used in our dev/test
environment to excercise the client. In a prod env, a real OIDC
provider would probably be used instead, (by configuring it in
config/astral.yml).

# Logging into Vault with OIDC

The rails test's configure the OIDC initial user, so if the tests pass,
you can invoke the oidc login as follows:
suprjinx marked this conversation as resolved.
Show resolved Hide resolved

```
export VAULT_ADDR=http://127.0.0.1:8200; vault login -method=oidc
```

You should do this on your host machine, not in docker. This will
allow a browser window to open on your host. When it does, select
"username" option with user test/test. (That is the username/pw
configured at startup.)

When that succeeds, you should see something like the following in the cli:
```
Success! You are now authenticated
.
identity_policies ["[email protected]"]
.
.
```

Note that "identity_policies" includes "[email protected]", which is the
policy we created for this user.

To make this work smoothly with the browser, you should add the
following to the /etc/hosts file on your host:

```
127.0.0.1 oidc_provider
```

Finally, if you restart the docker Vault container, it will recreate
the provider settings, so you will need to clear the browser's
"oidc_provider" cookie. Otherwise you will see this error:

```
* Vault login failed. Expired or missing OAuth state.
```


1 change: 1 addition & 0 deletions app/lib/clients/vault.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Vault
extend Clients::Vault::Policy
extend Clients::Vault::Entity
extend Clients::Vault::EntityAlias
extend Clients::Vault::Oidc

class_attribute :token

Expand Down
50 changes: 50 additions & 0 deletions app/lib/clients/vault/oidc.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module Clients
class Vault
module Oidc
def configure_as_oidc_client(issuer, client_id, client_secret)
if client_id.nil? || !oidc_auth_data.nil?
return
end
create_client_config(issuer, client_id, client_secret)
create_default_role(client_id)
end

def configure_oidc_user(name, email, policy)
client.sys.put_policy(email, policy)
put_entity(name, email)
put_entity_alias(name, email, "oidc")
end

def get_oidc_client_config
client.logical.read("auth/oidc/config")
end

private
suprjinx marked this conversation as resolved.
Show resolved Hide resolved

def create_client_config(issuer, client_id, client_secret)
client.logical.write("/sys/auth/oidc", type: "oidc")
client.logical.write("auth/oidc/config",
oidc_discovery_url: issuer,
oidc_discovery_ca_pem: File.read(Config[:oidc_provider_ssl_cert]),
oidc_client_id: client_id,
oidc_client_secret: client_secret,
default_role: "default")
end

def create_default_role(client_id)
client.logical.write(
"auth/oidc/role/default",
bound_audiences: client_id,
allowed_redirect_uris: Config[:oidc_redirect_uris],
user_claim: "email",
oidc_scopes: "email",
token_policies: "default")
end

def oidc_auth_data
auth_list = client.logical.read("/sys/auth")
auth_list.data[:"oidc/"]
end
end
end
end
18 changes: 18 additions & 0 deletions app/lib/clients/vault/policy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ def create_astral_policy
path "#{kv_mount}/data/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "identity/entity" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "identity/entity/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "identity/entity-alias" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "/sys/auth" {
capabilities = ["read"]
}
path "auth/oidc/config" {
capabilities = ["read"]
}
path "/sys/policy/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
HCL

client.sys.put_policy("astral_policy", policy)
Expand Down
93 changes: 93 additions & 0 deletions app/lib/utils/oidc_provider.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
class OidcProvider
attr_reader :client_id
attr_reader :client_secret
attr_reader :vault_client

def configure
provider = vault_client.logical.read("identity/oidc/provider/astral")
if provider.nil?
create_provider_webapp
create_provider_with_email_scope
create_entity_for_initial_user
create_userpass_for_initial_user
map_userpass_to_entity
else
get_client_info
end
end


def get_client_info
app = vault_client.logical.read(WEBAPP_NAME)
@client_id = app.data[:client_id]
@client_secret = app.data[:client_secret]
[ @client_id, @client_secret ]
end

def get_info
vault_client.logical.read("identity/oidc/provider/astral")
end

def self.get_configured_issuer
Config[:oidc_provider_addr] + Config[:oidc_issuer_path]
end

private
WEBAPP_NAME = "identity/oidc/client/astral"

def vault_client
@vault_client ||=
::Vault::Client.new(
address: Config[:oidc_provider_addr],
token: Config[:vault_token],
ssl_ca_cert: Config[:oidc_provider_ssl_cert],
ssl_pem_file: Config[:oidc_provider_ssl_client_cert],
ssl_key_file: Config[:oidc_provider_ssl_client_key]
)
end

def create_provider_webapp
vault_client.logical.write(
WEBAPP_NAME,
redirect_uris: Config[:oidc_redirect_uris],
assignments: "allow_all")
get_client_info
end

def create_provider_with_email_scope
vault_client.logical.write("identity/oidc/scope/email",
template: '{"email": {{identity.entity.metadata.email}}}')
vault_client.logical.write("identity/oidc/provider/astral",
issuer: Config[:oidc_provider_addr],
allowed_client_ids: @client_id,
scopes_supported: "email")
vault_client.logical.read("identity/oidc/provider/astral")
end

def create_entity_for_initial_user
vault_client.logical.write("identity/entity",
policies: "default",
name: Config[:initial_user_name],
metadata: "email=#{Config[:initial_user_email]}",
disabled: false)
end

def create_userpass_for_initial_user
vault_client.logical.delete("/sys/auth/userpass")
vault_client.logical.write("/sys/auth/userpass", type: "userpass")
vault_client.logical.write("/auth/userpass/users/#{Config[:initial_user_name]}",
password: Config[:initial_user_password])
end

def map_userpass_to_entity
entity = vault_client.logical.read(
"identity/entity/name/#{Config[:initial_user_name]}")
entity_id = entity.data[:id]
auth_list = vault_client.logical.read("/sys/auth")
accessor = auth_list.data[:"userpass/"][:accessor]
vault_client.logical.write("identity/entity-alias",
name: Config[:initial_user_name],
canonical_id: entity_id,
mount_accessor: accessor)
end
end
28 changes: 28 additions & 0 deletions cert/oidc_provider.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE REQUEST-----
MIIEyjCCArICAQAwWjELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEUMBIG
A1UEBwwLU3ByaW5nZmllbGQxDDAKBgNVBAoMA0RpczEWMBQGA1UEAwwNb2lkY19w
cm92aWRlcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL9vn3CPEeeX
hw+Ti+MF5jFTjVYjNXD05lPx8p2CapGBTU1qQTQBgtHzz6X/DfBZ6g+m/uDqtFIj
W4VrJ0SAT8Mn7ift4lPZB7cJJDiJ1rXYgkeISk1v9xchcpnPChS8WpsK37MS8sZX
1asuCQFWkRxCOIOcIHTGjYZ/CAupFUIR24AoA/ubtpegB/oiaVS8aRg+jq5o2t2g
X9/x25ZM+TXm9+Ofg43y7oc431qbLzih83EmG2Fxgku+4Swb2s6vHUCCggqn8Rz6
b5sGl3SUxxXlw4+Eh2lXiOkI38VAXE9z4OpD08p+iaWGqyFRmw3798yAMCO9WUer
ty+PxFZ1G68GAeNmREE+xE1/fMvVQw+uQxQKJqUCfEHCujAQUWHzG/TDEytkzR4H
zPyOnDXLHX1GNBdRvp+hVCxdmXR4K7NMNNWuOs3g6s30BWA27Wic67AwDibLfUqu
Cq/2ifv2iim/lVhBF7gpaKsJxfsqAHggerEPVpIGQS3E7lTbVh+y5lQAGpzT517+
JXCxh4qVyw7SWd/U0qvp0GogdcJDU4JUSMKSuAK/uvpLr+MxNxHWZZ1q4uxJvslj
7LAttKQi7V9ctk0jzgSyQNoY9xBPkQFvmO+7XNkJg11bWIxW94W37CNeyglFpbh0
c2S7tQfVI14r/aQwewfBt16uDxPq/gNzAgMBAAGgKzApBgkqhkiG9w0BCQ4xHDAa
MBgGA1UdEQQRMA+CDW9pZGNfcHJvdmlkZXIwDQYJKoZIhvcNAQELBQADggIBAAH7
kCGnejVLi30rOHlU/QjyaHBJYQsBf1vrhqrM7xNB+u7uXaZ6A13L6dVrgvosKNOs
PYeABtmD9viIwfQoXhqiaUIsx/1hnReOJVlBaLyG9cOJB1tsZ7udWCVEbfg7GmoW
RDX7+P/sb1MV2raXbp3EWT/uU6Ro8VIxyMyivRlk7ghEEJHsGyH8RvNZi00eZkw4
n10smbMQKlDxDXAsrGp2ez9KXso84xvx3NqZXc/uO5SQcIfT4haWSsNBVavtYXd1
0eQ6KoJ8ptImgPFmPo0c1m4Hg5hdlY5FTGE8vp4Zj1J2sbfxqA0vsqPQ6mNOLsRh
AOtED9rDeh/prTMp3agbkF/Janwt5O69JgbH4iJMLc6PwYs3/MOlcceIlmHMt7dI
MFtbTN0PyBQfVN3D417/UCFcY8LoIQLfyquKay5tQSByxdqK9g+0bfeMXjDHIo8u
xNlTrxQwzgzoNqNMYk4vGEELvwRLPBxcXF1jaQoXGZiH6pVXg5jqqDNSOiqQr/WV
Ox466J47DxGJBfbRMQqfQoaLTYWTVyhZSSYrTi1nwvXd2VaC7YIQVRwy6M4gEAG6
W3DWkO9d1stGBL+PJlAgQ77vsAkKjINolcQYfigV+4Wt+4DTl5lUIW8hxM/C+DmN
uI9e5QlUbt850qrTOuNW8BOXbaPmBL4R0iDV9kzK
-----END CERTIFICATE REQUEST-----
52 changes: 52 additions & 0 deletions cert/oidc_provider.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
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
cXlsC3wuADNQnvoMNW1ZvKkhEubiwQ==
-----END PRIVATE KEY-----
Loading