puppetlabs/puppet · error · ArgumentError

invalid key

Error message

invalid key

What it means

The YAML terminus's path() rejects indirection keys matching Puppet::Indirector::BadNameRegexp (patterns like a leading '../', '/..' inside the name, or a leading '/'), logs a critical message about directory traversal, and raises ArgumentError 'invalid key'. The check prevents keys from escaping the terminus's base directory, so it fires on absolute paths and dot-dot relative paths used as indirection keys — whether from a bug or a deliberate traversal attempt.

Source

Thrown at lib/puppet/indirector/yaml.rb:42

    file = path(request.key)

    basedir = File.dirname(file)

    # This is quite likely a bad idea, since we're not managing ownership or modes.
    Dir.mkdir(basedir) unless Puppet::FileSystem.exist?(basedir)

    begin
      Puppet::Util::Yaml.dump(request.instance, file)
    rescue TypeError => detail
      Puppet.err _("Could not save %{indirection} %{request}: %{detail}") % { indirection: name, request: request.key, detail: detail }
    end
  end

  # Return the path to a given node's file.
  def path(name, ext = '.yaml')
    if name =~ Puppet::Indirector::BadNameRegexp then
      Puppet.crit(_("directory traversal detected in %{indirection}: %{name}") % { indirection: self.class, name: name.inspect })
      raise ArgumentError, _("invalid key")
    end

    base = Puppet.run_mode.server? ? Puppet[:yamldir] : Puppet[:clientyamldir]
    File.join(base, self.class.indirection_name.to_s, name.to_s + ext)
  end

  def destroy(request)
    file_path = path(request.key)
    Puppet::FileSystem.unlink(file_path) if Puppet::FileSystem.exist?(file_path)
  end

  def search(request)
    Dir.glob(path(request.key, '')).collect do |file|
      load_file(file)
    end
  end

  protected

View on GitHub (pinned to e227c27540)

Solutions

  1. Sanitize keys before use: name = File.basename(key) (and reject empty results)
  2. Validate certnames/identifiers at ingress: reject '/' and '..' in names that will become indirection keys
  3. Find the caller passing the raw path — the crit log line names the indirection and the offending name.inspect
  4. If traversal appeared in server logs from external traffic, treat as a probing attempt and check access controls (auth.conf) on the affected endpoints

Example fix

# before
key = params[:node]                 # attacker-controlled: '../../etc/passwd'
Puppet::Node.indirection.find(key)  # crit + ArgumentError: invalid key

# after
key = File.basename(params[:node].to_s)
raise ArgumentError, 'bad node name' if key.empty? || key =~ /\A\.+\z/
Puppet::Node.indirection.find(key)
Defensive patterns

Strategy: validation

Validate before calling

def safe_indirection_key?(name)
  name.is_a?(String) && !name.empty? && name !~ Puppet::Indirector::BadNameRegexp && !name.start_with?('/')
end
raise ArgumentError, 'invalid key' unless safe_indirection_key?(user_input)

Type guard

def safe_indirection_key?(name)
  name.is_a?(String) && !name.empty? && name !~ Puppet::Indirector::BadNameRegexp
end

Prevention

When it happens

Trigger: Calling a yaml/msgpack/json-backed indirection with key '../../../etc/passwd', '/etc/shadow', or 'cert/../other'; commonly triggered when certnames or filenames containing slashes/dotdot segments are passed straight through as request keys (e.g., a node whose certname was issued with a '/' in it).

Common situations: Security testing / fuzzing of Puppet REST endpoints; bugs where File.basename is forgotten and full paths are passed as keys; compromised or malformed ENC output feeding crafted node names into yaml store paths.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/bd4594dab9c8fe6e. Report an issue: GitHub.