diff --git a/README.md b/README.md index d130029..26177f6 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,32 @@ # README -Astral is an api-only application intended to simplify -certificate acquisition for other applications/services. Broadly speaking, -it will: +Astral is an api-only application intended to simplify certificate +acquisition and secrets storage. Invoking a single endpoint can issue +an mTLS certificate or fetch a secret for applications in your +environment, without any need to configure the underlying PKI or +secrets storage (Vault). -1) Authenticate the request for cerficate using a third party trusted source (JWT, etc) -2) Authorize the request using a Domain Ownership Registry -3) If authorized, obtain a certificate from PKI CLM (such as Vault/OpenBao) -4) Log this transaction in audit infrastructure (ELK, etc). +Some features of Astral: + +0) Configure Astral-specific Certificate Authority and Key-Value stores in Vault +1) Authenticate requests for cerficates or secrets using a third party + trusted source (JWT with signing key, eg) +2) For certiciates: + a) Authorize the request using a Domain Ownership registry, where domain owner + or authorized groups must match the identity of the requesting client + b) When authorized, obtain a certificate for the common name +3) For secrets: + a) Create secrets with a policy for reading + b) Read only when the requesting client identity has the policy. +4) Log all transactions in audit infrastructure (ELK, etc). # Running in development -This Rails app is most easily run and developed in its devcontainer. +This Rails app is most easily run and developed in its devcontainer, which includes Vault +and a Domain Ownership registry (AppRegistry) in the compose environment. -1) Open in devcontainer -2) Launch server using vscode launch config, or in terminal run: +1) Open in devcontainer (automatic in vscode) +2) Launch server using vscode launch config, or in the terminal run: ``` rails s ``` @@ -47,5 +59,5 @@ docker build -t astral:latest . ``` 2) Run the prod image: ``` -docker run -e SECRET_KEY_BASE=mysecrit -p 3000:3000 astral:latest +docker run -p 3000:3000 astral:latest ``` diff --git a/app/lib/clients/vault.rb b/app/lib/clients/vault.rb index 42cf3c9..5f0c5e0 100644 --- a/app/lib/clients/vault.rb +++ b/app/lib/clients/vault.rb @@ -1,5 +1,11 @@ module Clients class Vault + extend Clients::Vault::Certificate + extend Clients::Vault::KeyValue + extend Clients::Vault::Policy + extend Clients::Vault::Entity + extend Clients::Vault::EntityAlias + class_attribute :token class << self @@ -21,10 +27,4 @@ def enable_engine(mount, type) end end end - - require_relative "vault/key_value" - require_relative "vault/certificate" - require_relative "vault/policy" - require_relative "vault/entity" - require_relative "vault/entity_alias" end diff --git a/app/lib/clients/vault/certificate.rb b/app/lib/clients/vault/certificate.rb index 4469d5a..f3bc31a 100644 --- a/app/lib/clients/vault/certificate.rb +++ b/app/lib/clients/vault/certificate.rb @@ -1,6 +1,6 @@ module Clients class Vault - class << self + module Certificate def issue_cert(cert_issue_request) opts = cert_issue_request.attributes # Generate the TLS certificate using the intermediate CA @@ -57,21 +57,21 @@ def configure_root_ca # generate root certificate root_cert = client.logical.write("#{root_ca_mount}/root/generate/internal", - common_name: "astral.internal", - issuer_name: root_ca_ref, - ttl: "87600h").data[:certificate] + common_name: "astral.internal", + issuer_name: root_ca_ref, + ttl: "87600h").data[:certificate] # save the root certificate File.write("tmp/#{root_ca_mount}.crt", root_cert) client.logical.write("#{root_ca_mount}/config/cluster", - path: "#{address}/v1/#{root_ca_mount}", - aia_path: "#{address}/v1/#{root_ca_mount}") + path: "#{address}/v1/#{root_ca_mount}", + aia_path: "#{address}/v1/#{root_ca_mount}") client.logical.write("#{root_ca_mount}/config/urls", - issuing_certificates: "{{cluster_aia_path}}/issuer/{{issuer_id}}/der", - crl_distribution_points: "{{cluster_aia_path}}/issuer/{{issuer_id}}/crl/der", - ocsp_servers: "{{cluster_path}}/ocsp", - enable_templating: true) + issuing_certificates: "{{cluster_aia_path}}/issuer/{{issuer_id}}/der", + crl_distribution_points: "{{cluster_aia_path}}/issuer/{{issuer_id}}/crl/der", + ocsp_servers: "{{cluster_path}}/ocsp", + enable_templating: true) end def sign_cert diff --git a/app/lib/clients/vault/entity.rb b/app/lib/clients/vault/entity.rb index 2f894d3..8e2e89c 100644 --- a/app/lib/clients/vault/entity.rb +++ b/app/lib/clients/vault/entity.rb @@ -1,6 +1,6 @@ module Clients class Vault - class << self + module Entity def put_entity(name, policies) client.logical.write("identity/entity", name: name, diff --git a/app/lib/clients/vault/entity_alias.rb b/app/lib/clients/vault/entity_alias.rb index 010a3a4..fb2456c 100644 --- a/app/lib/clients/vault/entity_alias.rb +++ b/app/lib/clients/vault/entity_alias.rb @@ -1,6 +1,6 @@ module Clients class Vault - class << self + module EntityAlias def put_entity_alias(entity_name, alias_name, auth_method) e = read_entity(entity_name) if e.nil? diff --git a/app/lib/clients/vault/key_value.rb b/app/lib/clients/vault/key_value.rb index c11f9f3..dee6718 100644 --- a/app/lib/clients/vault/key_value.rb +++ b/app/lib/clients/vault/key_value.rb @@ -1,6 +1,6 @@ module Clients class Vault - class << self + module KeyValue def kv_read(path) client.kv(kv_mount).read(path) end diff --git a/app/lib/clients/vault/policy.rb b/app/lib/clients/vault/policy.rb index 13ad3a5..0d670c7 100644 --- a/app/lib/clients/vault/policy.rb +++ b/app/lib/clients/vault/policy.rb @@ -1,6 +1,6 @@ module Clients class Vault - class << self + module Policy def rotate_token create_astral_policy token = create_astral_token diff --git a/app/lib/requests/cert_issue_request.rb b/app/lib/requests/cert_issue_request.rb index b718cc3..6f2c02b 100644 --- a/app/lib/requests/cert_issue_request.rb +++ b/app/lib/requests/cert_issue_request.rb @@ -24,7 +24,7 @@ class CertIssueRequest validates :format, presence: true, inclusion: { in: %w[pem der pem_bundle] } validates :private_key_format, presence: true, inclusion: { in: %w[pem der pkcs8] } validates :ttl, numericality: { - less_than_or_equal_to: Config[:cert_ttl], + less_than_or_equal_to: Config[:cert_ttl].to_i, greater_than: 0 } validate :validate_no_wildcards diff --git a/config/application.rb b/config/application.rb index c3cdf58..a4bd5f2 100644 --- a/config/application.rb +++ b/config/application.rb @@ -29,6 +29,9 @@ class Application < Rails::Application # Skip views, helpers and assets when generating a new resource. config.api_only = true + # the secret_key_base isn't used, but Rails requires it + config.secret_key_base = "secret_key_base_not_used!" + # Application configs from config/astral.yml config.astral = config_for :astral