Skip to content

Commit

Permalink
Merge pull request puppetlabs#3464 from rlinehan/feature/master/PUP-3…
Browse files Browse the repository at this point in the history
…526-ca-api-prefix

(PUP-3526) Add configurable prefix for CA routes
  • Loading branch information
cprice404 committed Jan 24, 2015
2 parents 60b4939 + 80a108f commit 6438c72
Show file tree
Hide file tree
Showing 38 changed files with 440 additions and 232 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
step 'run master; ensure production environment created'
with_puppet_running_on(master, master_opts, testdir) do
if master.is_using_passenger?
on(master, "curl -k --cert #{cert_path} --key #{key_path} --cacert #{cacert_path} https://localhost:8140/v2.0/environments")
on(master, "curl -k --cert #{cert_path} --key #{key_path} --cacert #{cacert_path} https://localhost:8140/puppet/v2.0/environments")
end
on(master, "test -d '#{testdir}/environments/production'")

Expand Down
10 changes: 5 additions & 5 deletions acceptance/tests/external_ca_support/fixtures/auth.conf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Puppet 3.1.1 auth.conf, modified to allow requests from example.org for
# Puppet 4.0.0 auth.conf, modified to allow requests from example.org for
# external ca testing.

# allow nodes to retrieve their own catalog
Expand All @@ -14,7 +14,7 @@ allow *.example.org
allow $1

# allow all nodes to access the certificates services
path /puppet/v3/certificate_revocation_list/ca
path /puppet-ca/v1/certificate_revocation_list/ca
method find
allow *

Expand All @@ -37,19 +37,19 @@ allow *

# allow access to the CA certificate; unauthenticated nodes need this
# in order to validate the puppet master's certificate
path /puppet/v3/certificate/ca
path /puppet-ca/v1/certificate/ca
auth any
method find
allow *

# allow nodes to retrieve the certificate they requested earlier
path /puppet/v3/certificate/
path /puppet-ca/v1/certificate/
auth any
method find
allow *

# allow nodes to request a new certificate
path /puppet/v3/certificate_request
path /puppet-ca/v1/certificate_request
auth any
method find, save
allow *
Expand Down
2 changes: 1 addition & 1 deletion acceptance/tests/external_ca_support/fixtures/certchain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ check_puppetmaster() {
--header 'Accept: yaml' \
--cert "${B}/leaves/client2a.example.org.crt" \
--key "${B}/leaves/client2a.example.org.key" \
"https://127.0.0.1:${HTTPS_PORT}/production/catalog/client2a.example.org" >/dev/null
"https://127.0.0.1:${HTTPS_PORT}/puppet/v3/catalog/client2a.example.org?environment=production" >/dev/null
echo
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
exploited = "#{yamldir}/node/you.lose.yaml"
on master, "rm -rf #{exploited}"
on master, "rm -rf #{yamldir}/node/*"
payload2 = "https://#{master}:8140/production/node/#{certname}?instance=" +
"---+%21ruby%2Fobject%3APuppet%3A%3ANode%0A+classes" +
payload2 = "https://#{master}:8140/puppet/v3/node/#{certname}?environment=production" +
"&instance=---+%21ruby%2Fobject%3APuppet%3A%3ANode%0A+classes" +
"%3A%0A+-+foo%0A+name%3A+you.lose%0A+parameters" +
"%3A+%7B%7D%0A+time%3A+2013-02-28+15%3A12%3A30.367008+-08%3A00"

Expand Down
2 changes: 1 addition & 1 deletion acceptance/tests/security/cve-2013-4761_resource_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

step "Request a type that maps to the exploit file" do
type_name = "::..::..::..::..::..::tmp::exploit"
payload = "https://#{master}:8140/production/resource_type/#{type_name}"
payload = "https://#{master}:8140/puppet/v3/resource_type/#{type_name}?environment=production"
cert_path = on(agent, puppet("agent", "--configprint hostcert")).stdout.chomp
key_path = on(agent, puppet("agent", "--configprint hostprivkey")).stdout.chomp
curl_base = "curl --tlsv1 -g --cert \"#{cert_path}\" --key \"#{key_path}\" -k -H 'Accept: pson'"
Expand Down
56 changes: 30 additions & 26 deletions conf/auth.conf
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,18 @@
#
# Examples:
#
# path ~ ^/path/to/resource # Equivalent to `path /path/to/resource`.
# allow * # Allow all authenticated nodes (since auth
# # defaults to `yes`).
# path ~ ^/puppet/v3/path/to/resource # Equivalent to `path /puppet/v3/path/to/resource`.
# allow * # Allow all authenticated nodes (since auth
# # defaults to `yes`).
#
# path ~ ^/catalog/([^/]+)$ # Permit nodes to access their own catalog (by
# allow $1 # certname), but not any other node's catalog.
# path ~ ^/puppet/v3/catalog/([^/]+)$ # Permit nodes to access their own catalog (by
# allow $1 # certname), but not any other node's catalog.
#
# path ~ ^/file_(metadata|content)/extra_files/ # Only allow certain nodes to
# auth yes # access the "extra_files"
# allow /^(.+)\.example\.com$/ # mount point; note this must
# allow_ip 192.168.100.0/24 # go ABOVE the "/file" rule,
# # since it is more specific.
# path ~ ^/puppet/v3/file_(metadata|content)/extra_files/ # Only allow certain nodes to
# auth yes # access the "extra_files"
# allow /^(.+)\.example\.com$/ # mount point; note this must
# allow_ip 192.168.100.0/24 # go ABOVE the "/file" rule,
# # since it is more specific.
#
# environment:: restrict an ACL to a comma-separated list of environments
# method:: restrict an ACL to a comma-separated list of HTTP methods
Expand All @@ -60,6 +60,14 @@
### Authenticated ACLs - these rules apply only when the client
### has a valid certificate and is thus authenticated

path /puppet/v2.0/environments
method find
allow *

path /puppet/v3/environments
method find
allow *

# allow nodes to retrieve their own catalog
path ~ ^/puppet/v3/catalog/([^/]+)$
method find
Expand All @@ -70,11 +78,6 @@ path ~ ^/puppet/v3/node/([^/]+)$
method find
allow $1

# allow all nodes to access the certificates services
path /puppet/v3/certificate_revocation_list/ca
method find
allow *

# allow all nodes to store their own reports
path ~ ^/puppet/v3/report/([^/]+)$
method save
Expand All @@ -88,36 +91,37 @@ allow $1
path /puppet/v3/file
allow *

path /puppet/v3/status
method find
allow *

# allow all nodes to access the certificates services
path /puppet-ca/v1/certificate_revocation_list/ca
method find
allow *

### Unauthenticated ACLs, for clients without valid certificates; authenticated
### clients can also access these paths, though they rarely need to.

# allow access to the CA certificate; unauthenticated nodes need this
# in order to validate the puppet master's certificate
path /puppet/v3/certificate/ca
path /puppet-ca/v1/certificate/ca
auth any
method find
allow *

# allow nodes to retrieve the certificate they requested earlier
path /puppet/v3/certificate/
path /puppet-ca/v1/certificate/
auth any
method find
allow *

# allow nodes to request a new certificate
path /puppet/v3/certificate_request
path /puppet-ca/v1/certificate_request
auth any
method find, save
allow *

path /puppet/v2.0/environments
method find
allow *

path /puppet/v3/environments
method find
allow *

# deny everything else; this ACL is not strictly necessary, but
# illustrates the default policy.
path /
Expand Down
3 changes: 1 addition & 2 deletions lib/puppet/indirector/rest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
require 'uri'

require 'puppet/network/http'
require 'puppet/network/http/api/v3/indirected_routes'
require 'puppet/network/http_pool'

# Access objects via REST
class Puppet::Indirector::REST < Puppet::Indirector::Terminus
include Puppet::Network::HTTP::Compression.module

IndirectedRoutes = Puppet::Network::HTTP::API::V3::IndirectedRoutes
IndirectedRoutes = Puppet::Network::HTTP::API::IndirectedRoutes

class << self
attr_reader :server_setting, :port_setting
Expand Down
41 changes: 26 additions & 15 deletions lib/puppet/network/authconfig.rb
Original file line number Diff line number Diff line change
@@ -1,34 +1,45 @@
require 'puppet/network/rights'
require 'puppet/network/http'

module Puppet
class ConfigurationError < Puppet::Error; end
class Network::AuthConfig
attr_accessor :rights

def self.url_prefix
Puppet[:master_url_prefix]
def self.master_url_prefix
Puppet::Network::HTTP::MASTER_URL_PREFIX
end

def self.ca_url_prefix
Puppet::Network::HTTP::CA_URL_PREFIX
end

def self.default_acl
[
# API V2.0
{ :acl => "#{url_prefix}/v2.0/environments", :method => :find, :allow => '*', :authenticated => true },
# Master API V2.0
{ :acl => "#{master_url_prefix}/v2.0/environments", :method => :find, :allow => '*', :authenticated => true },

# Master API V3
{ :acl => "#{master_url_prefix}/v3/environments", :method => :find, :allow => '*', :authenticated => true },

{ :acl => "~ ^#{master_url_prefix}\/v3\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true },
{ :acl => "~ ^#{master_url_prefix}\/v3\/node\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true },
{ :acl => "~ ^#{master_url_prefix}\/v3\/report\/([^\/]+)$", :method => :save, :allow => '$1', :authenticated => true },

# API V3
{ :acl => "~ ^#{url_prefix}\/v3\/catalog\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true },
{ :acl => "~ ^#{url_prefix}\/v3\/node\/([^\/]+)$", :method => :find, :allow => '$1', :authenticated => true },
# this one will allow all file access, and thus delegate
# to fileserver.conf
{ :acl => "#{url_prefix}/v3/file" },
{ :acl => "#{url_prefix}/v3/certificate_revocation_list/ca", :method => :find, :authenticated => true },
{ :acl => "~ ^#{url_prefix}\/v3\/report\/([^\/]+)$", :method => :save, :allow => '$1', :authenticated => true },
{ :acl => "#{master_url_prefix}/v3/file" },

{ :acl => "#{master_url_prefix}/v3/status", :method => [:find], :authenticated => true },

# CA API V1
{ :acl => "#{ca_url_prefix}/v1/certificate_revocation_list/ca", :method => :find, :authenticated => true },

# These allow `auth any`, because if you can do them anonymously you
# should probably also be able to do them when trusted.
{ :acl => "#{url_prefix}/v3/certificate/ca", :method => :find, :authenticated => :any },
{ :acl => "#{url_prefix}/v3/certificate/", :method => :find, :authenticated => :any },
{ :acl => "#{url_prefix}/v3/certificate_request", :method => [:find, :save], :authenticated => :any },
{ :acl => "#{url_prefix}/v3/status", :method => [:find], :authenticated => true },
{ :acl => "#{url_prefix}/v3/environments", :method => :find, :allow => '*', :authenticated => true },
{ :acl => "#{ca_url_prefix}/v1/certificate/ca", :method => :find, :authenticated => :any },
{ :acl => "#{ca_url_prefix}/v1/certificate/", :method => :find, :authenticated => :any },
{ :acl => "#{ca_url_prefix}/v1/certificate_request", :method => [:find, :save], :authenticated => :any },
]
end

Expand Down
10 changes: 8 additions & 2 deletions lib/puppet/network/http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ module Puppet::Network::HTTP
HEADER_ENABLE_PROFILING = "X-Puppet-Profiling"
HEADER_PUPPET_VERSION = "X-Puppet-Version"

MASTER_URL_PREFIX = "/puppet"
CA_URL_PREFIX = "/puppet-ca"

require 'puppet/network/authorization'
require 'puppet/network/http/issues'
require 'puppet/network/http/error'
require 'puppet/network/http/route'
require 'puppet/network/http/api'
require 'puppet/network/http/api/v2'
require 'puppet/network/http/api/v3'
require 'puppet/network/http/api/ca'
require 'puppet/network/http/api/ca/v1'
require 'puppet/network/http/api/master'
require 'puppet/network/http/api/master/v2'
require 'puppet/network/http/api/master/v3'
require 'puppet/network/http/handler'
require 'puppet/network/http/response'
require 'puppet/network/http/request'
Expand Down
17 changes: 17 additions & 0 deletions lib/puppet/network/http/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,21 @@ def self.not_found
raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new("No route for #{req.method} #{req.path}", Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND)
end)
end

def self.master_routes
master_prefix = Regexp.new("^#{Puppet::Network::HTTP::MASTER_URL_PREFIX}/")
Puppet::Network::HTTP::Route.path(master_prefix).
any.
chain(Puppet::Network::HTTP::API::Master::V3.routes,
Puppet::Network::HTTP::API::Master::V2.routes,
Puppet::Network::HTTP::API.not_found)
end

def self.ca_routes
ca_prefix = Regexp.new("^#{Puppet::Network::HTTP::CA_URL_PREFIX}/")
Puppet::Network::HTTP::Route.path(ca_prefix).
any.
chain(Puppet::Network::HTTP::API::CA::V1.routes,
Puppet::Network::HTTP::API.not_found)
end
end
2 changes: 2 additions & 0 deletions lib/puppet/network/http/api/ca.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module Puppet::Network::HTTP::API::CA
end
11 changes: 11 additions & 0 deletions lib/puppet/network/http/api/ca/v1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'puppet/network/http/api/indirected_routes'
class Puppet::Network::HTTP::API::CA::V1

INDIRECTED = Puppet::Network::HTTP::Route.
path(/.*/).
any(Puppet::Network::HTTP::API::IndirectedRoutes.new)

def self.routes
Puppet::Network::HTTP::Route.path(%r{v1}).any.chain(INDIRECTED)
end
end
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'puppet/network/authorization'
require 'puppet/network/http/api/indirection_type'

class Puppet::Network::HTTP::API::V3::IndirectedRoutes
class Puppet::Network::HTTP::API::IndirectedRoutes
include Puppet::Network::Authorization

# How we map http methods and the indirection name in the URI
Expand All @@ -24,6 +25,8 @@ class Puppet::Network::HTTP::API::V3::IndirectedRoutes
}
}

IndirectionType = Puppet::Network::HTTP::API::IndirectionType

def self.routes
Puppet::Network::HTTP::Route.path(/.*/).any(new)
end
Expand All @@ -50,21 +53,26 @@ def call(request, response)
return do_exception(response, e)
end

def self.url_prefix
"#{Puppet[:master_url_prefix]}/v3"
end

def uri2indirection(http_method, uri, params)
# the first field is always nil because of the leading slash,
indirection_name, key = uri.split("/", 5)[3..-1]
indirection_type, version, indirection_name, key = uri.split("/", 5)[1..-1]
url_prefix = "/#{indirection_type}/#{version}"
environment = params.delete(:environment)

if indirection_name !~ /^\w+$/
raise ArgumentError, "The indirection name must be purely alphanumeric, not '#{indirection_name}'"
end

# this also depluralizes the indirection_name if it is a search
method = indirection_method(http_method, indirection_name)
check_authorization(method, "#{self.class.url_prefix}/#{indirection_name}/#{key}", params)

# check whether this indirection matches the prefix and version in the
# request
if url_prefix != IndirectionType.url_prefix_for(indirection_name)
raise ArgumentError, "Indirection '#{indirection_name}' does not match url prefix '#{url_prefix}'"
end

check_authorization(method, "#{url_prefix}/#{indirection_name}/#{key}", params)

indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym)
if !indirection
Expand Down Expand Up @@ -214,6 +222,7 @@ def self.request_to_uri(request)
end

def self.request_to_uri_and_body(request)
url_prefix = IndirectionType.url_prefix_for(request.indirection_name.to_s)
indirection = request.method == :search ? pluralize(request.indirection_name.to_s) : request.indirection_name.to_s
["#{url_prefix}/#{indirection}/#{request.escaped_key}", "environment=#{request.environment.name}&#{request.query_string}"]
end
Expand Down
Loading

0 comments on commit 6438c72

Please sign in to comment.