Skip to content

Commit

Permalink
Merge branch 'main' of github.com:G-Research/astral
Browse files Browse the repository at this point in the history
  • Loading branch information
suprjinx committed Oct 22, 2024
2 parents 08fa788 + ceb8619 commit 88b65d4
Show file tree
Hide file tree
Showing 19 changed files with 512 additions and 11 deletions.
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
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 the principal Vault instance.

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:

```
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

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

0 comments on commit 88b65d4

Please sign in to comment.