Skip to content

Commit

Permalink
mask secrets in template debug logging
Browse files Browse the repository at this point in the history
  • Loading branch information
wr0ngway committed Jun 22, 2021
1 parent 7c3e53c commit bf4bf81
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 13 deletions.
2 changes: 1 addition & 1 deletion lib/kubetruth/etl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def apply

project.spec.resource_templates.each_with_index do |pair, i|
template_name, template = *pair
logger.debug { "Processing template '#{template_name}' (#{i}/#{project.spec.resource_templates.size})" }
logger.debug { "Processing template '#{template_name}' (#{i+1}/#{project.spec.resource_templates.size})" }
resource_yml = template.render(
template: template_name,
project: project.name,
Expand Down
28 changes: 19 additions & 9 deletions lib/kubetruth/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ def liquid_method_missing(key)
end
end

def inspect
{source: @source, parsed: @parsed}.inspect
def encode_with(coder)
coder.represent_map(nil, @source)
end

end
Expand Down Expand Up @@ -125,42 +125,52 @@ def initialize(template_source)
def render(*args, **kwargs)
begin

secrets = kwargs[:secrets] || {}

logger.debug do
msg = "Evaluating template:\n"
@source.to_s.lines.collect {|l| msg << (INDENT * 2) << l }
msg << "\n" << INDENT << "with context:\n"
kwargs.deep_stringify_keys.to_yaml.lines.collect {|l| msg << (INDENT * 2) << l }

secrets.each {|k, v| msg.gsub!(v, "<masked:#{k}>") }
msg
end

result = @liquid.render!(*args, kwargs.stringify_keys, strict_variables: true, strict_filters: true)

logger.debug do
# we only ever have to sub base64 encoded in this debug block
both_secrets = secrets.merge(Hash[secrets.collect {|k, v| ["#{k}_base64", Base64.strict_encode64(v)]}])

msg = "Rendered template:\n"
result.lines.collect {|l| msg << (INDENT * 2) << l }
both_secrets.each {|k, v| msg.gsub!(v, "<masked:#{k}>") }
msg
end

result

rescue Liquid::Error => e
indent = " "
msg = "Template failed to render:\n"
@source.lines.each {|l| msg << (indent * 2) << l }
msg << indent << "with error message:\n" << (indent * 2) << "#{e.message}"
@source.lines.each {|l| msg << (INDENT * 2) << l }
msg << INDENT << "with error message:\n" << (INDENT * 2) << "#{e.message}"
if e.is_a?(Liquid::UndefinedVariable)
msg << "\n" << indent << "and variable context:\n"
msg << (indent * 2) << kwargs.inspect
msg << "\n" << INDENT << "and variable context:\n"
kwargs.deep_stringify_keys.to_yaml.lines.collect {|l| msg << (INDENT * 2) << l }
end
secrets.each {|k, v| msg.gsub!(v, "<masked:#{k}>") }
raise Error, msg

raise Error, "Template failed to render: #{e.message}"
end
end

def to_s
@source
end

def encode_with(coder)
coder.represent_scalar(nil, @source)
end

end
end
37 changes: 34 additions & 3 deletions spec/kubetruth/template_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ module Kubetruth

end

describe "#to_yaml" do

it "produces clean yaml for logging" do
expect(Template.new("foo").to_yaml).to eq("--- foo\n")
end

end

describe "CustomLiquidFilters" do

include Kubetruth::Template::CustomLiquidFilters
Expand Down Expand Up @@ -160,9 +168,10 @@ module Kubetruth

describe Kubetruth::Template::TemplateHashDrop do

it "can be inspected" do
drop = described_class.new({})
expect(drop.inspect).to eq("{:source=>{}, :parsed=>{}}")
it "produces clean yaml for logging" do
drop = described_class.new({"foo" => "bar"})
c = {context: drop}
expect(c.to_yaml).to eq("---\n:context:\n foo: bar\n")
end

it "fails for missing key" do
Expand Down Expand Up @@ -264,6 +273,28 @@ module Kubetruth
expect(top.render(lambda: ->() { i += 2 } )).to eq("7")
end

it "masks secrets in logs" do
secrets = {"foo" => "sekret"}
tmpl = described_class.new("secret: {{secrets.foo}} encoded: {{secrets.foo | encode64}}")
expect(tmpl.render(secrets: secrets)).to eq("secret: sekret encoded: #{Base64.strict_encode64("sekret")}")
expect(Logging.contents).to_not include("sekret")
expect(Logging.contents).to include("<masked:foo>")
expect(Logging.contents).to_not include(Base64.strict_encode64("sekret"))
expect(Logging.contents).to include("<masked:foo_base64>")
end

it "masks secrets in exception" do
secrets = {"foo" => "sekret"}
tmpl = described_class.new("{{fail}}")
expect { tmpl.render(secrets: secrets) }.to raise_error(Template::Error) do |error|
expect(error.message).to_not include("sekret")
expect(error.message).to include("<masked:foo>")
expect(error.message).to_not include(Base64.strict_encode64("sekret"))
expect(error.message).to_not include("<masked:foo_base64>")
end

end

end

end
Expand Down

0 comments on commit bf4bf81

Please sign in to comment.