Examples of mixlib-authentication use?

Hey all,

I’m having a hard time writing a seemingly simple block of code to verify a
Chef API request using mixlib-authentication gem (
https://github.com/opscode/mixlib-authentication).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, “GET”, ‘/roles’)
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
“GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n”]]

[:on_headers_complete, {“X-Ops-Timestamp”=>“2014-05-30T23:10:50Z”,
“X-Remote-Request-Id”=>“8fbe67a4-35bc-4ee9-985e-5f1bf243175e”,
“X-Ops-Authorization-6”=>“dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==”,
“X-Ops-Authorization-5”=>“RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd”,
“X-Ops-Authorization-4”=>“1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm”,
“X-Ops-Authorization-3”=>“rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s”,
“X-Ops-Authorization-2”=>“KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ”,
“X-Ops-Authorization-1”=>“RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5”,
“X-Chef-Version”=>“11.12.2”,
“X-Ops-Content-Hash”=>“2jmj7l5rSw0yVb/vlWAYkK/YBwk=”,
“Connection”=>“close”, “X-Ops-Sign”=>“algorithm=sha1;version=1.0;”,
“Accept”=>“application/json”, “X-Ops-Userid”=>“XXX”,
“Accept-Encoding”=>“gzip;q=1.0,deflate;q=0.6,identity;q=0.3”,
“User-Agent”=>“Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)”, “Host”=>“chef11-proxy-1:8080”}]

[:request, #<struct env={“X-Ops-Timestamp”=>“2014-05-30T23:10:50Z”,
“X-Remote-Request-Id”=>“8fbe67a4-35bc-4ee9-985e-5f1bf243175e”,
“X-Ops-Authorization-6”=>“dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==”,
“X-Ops-Authorization-5”=>“RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd”,
“X-Ops-Authorization-4”=>“1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm”,
“X-Ops-Authorization-3”=>“rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s”,
“X-Ops-Authorization-2”=>“KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ”,
“X-Ops-Authorization-1”=>“RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5”,
“X-Chef-Version”=>“11.12.2”,
“X-Ops-Content-Hash”=>“2jmj7l5rSw0yVb/vlWAYkK/YBwk=”,
“Connection”=>“close”, “X-Ops-Sign”=>“algorithm=sha1;version=1.0;”,
“Accept”=>“application/json”, “X-Ops-Userid”=>“XXX”,
“Accept-Encoding”=>“gzip;q=1.0,deflate;q=0.6,identity;q=0.3”,
“User-Agent”=>“Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)”, “Host”=>“chef11-proxy-1:8080”},
method=“GET”, path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined methodenv’ for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:ininitialize’
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new’

Best regards, Dmitriy V.

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to verify a Chef API request using mixlib-authentication gem (GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample in spec/mixlib/authentication/http_authentication_request_spec.rb, and came up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]
52 @m = ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)
53 if @m.user_id() == "TheOne"
54 conn.relay_to_servers @buffer
55 @buffer.clear
56 else
57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
58 end
59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp: 2014-05-30T23:10:50Z\r\nX-Remote-Request-Id: 8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6: dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5: RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4: 1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3: rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2: KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1: RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version: 11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection: close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept: application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost: chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z", "X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e", "X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==", "X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd", "X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm", "X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s", "X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ", "X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5", "X-Chef-Version"=>"11.12.2", "X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=", "Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;", "Accept"=>"application/json", "X-Ops-Userid"=>"XXX", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z", "X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e", "X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==", "X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd", "X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm", "X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s", "X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ", "X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5", "X-Chef-Version"=>"11.12.2", "X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=", "Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;", "Accept"=>"application/json", "X-Ops-Userid"=>"XXX", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}, method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in headers': undefined method env' for #Class:0x00000001ef4178 (NoMethodError)
from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from /usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in `new'

Best regards, Dmitriy V.

Thanks for this - I've actually also found one more example of how to sign
headers (which is also something I need to do), together it'll be useful to
figuring it out. But haven't found any examples of verifying signed headers
(HTTPAuthenticationRequest) yet..

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to verify
a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding clients
don't ever need to verify the signed headers, only properly assemble and
encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to sign
headers (which is also something I need to do), together it'll be useful to
figuring it out. But haven't found any examples of verifying signed headers
(HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to verify
a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

If you're looking for an example of how to do it in Ruby, check the
10-stable branch of chef. That should have the open source Ruby server code
in it. If it's not in 10-stable, it's definitely at the tag 0.10.0 (the
original name for Chef 10).

On Sunday, June 1, 2014, Jeremy Bingham jbingham@gmail.com wrote:

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding clients
don't ever need to verify the signed headers, only properly assemble and
encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to sign
headers (which is also something I need to do), together it'll be useful to
figuring it out. But haven't found any examples of verifying signed headers
(HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to verify
a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDg

--
Stephen Delano
Software Development Engineer
Opscode, Inc.
1008 Western Avenue
Suite 601
Seattle, WA 98104

Thanks, my goal's pretty simple - I'm building a proxy that sits between
Chef and clients and provides granular control over who can do what. For
example, we want certain users to be able to view/modify only certain
cookbooks, roles, data bags, etc. For this, the proxy needs to verify
client's request against its public key, and if allowed, encrypt it using
its own client private key and send it on its way.

So far I've been able to create and sign headers thanks to Seth's link, but
not verify headers coming from clients due to the exception I posted. I'll
check out the 10.x branch as suggested by Stephen..

On Sun, Jun 1, 2014 at 11:34 PM, Jeremy Bingham jbingham@gmail.com wrote:

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding clients
don't ever need to verify the signed headers, only properly assemble and
encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to
sign headers (which is also something I need to do), together it'll be
useful to figuring it out. But haven't found any examples of verifying
signed headers (HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to
verify a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

You'll probably need to set the X-Ops-Request-Source to "web" and have the
proxy pretend it's webui. That way when it checks the authentication
headers it will try and decrypt it with the right public key, rather than
the user's. Since you've decrypted it and re-encrypted it with a different
key, if you don't do that the server will try to decrypt the headers with
the proxy user's key and it will fail.

Conversely you could just forward on the original encrypted auth header.
That might work too.

-j

On Mon, Jun 2, 2014 at 12:15 AM, DV vindimy@gmail.com wrote:

Thanks, my goal's pretty simple - I'm building a proxy that sits between
Chef and clients and provides granular control over who can do what. For
example, we want certain users to be able to view/modify only certain
cookbooks, roles, data bags, etc. For this, the proxy needs to verify
client's request against its public key, and if allowed, encrypt it using
its own client private key and send it on its way.

So far I've been able to create and sign headers thanks to Seth's link,
but not verify headers coming from clients due to the exception I posted.
I'll check out the 10.x branch as suggested by Stephen..

On Sun, Jun 1, 2014 at 11:34 PM, Jeremy Bingham jbingham@gmail.com
wrote:

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding clients
don't ever need to verify the signed headers, only properly assemble and
encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to
sign headers (which is also something I need to do), together it'll be
useful to figuring it out. But haven't found any examples of verifying
signed headers (HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com
wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to
verify a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

I think you'll be best off forwarding on the original request. As far as
time signatures go, you've got 15 minutes to do any authorization work
necessary, which is an eternity in computer time. Keep in mind that your
proxy will also have to have access to user / client public keys for
authentication, unless you'd like to have the Chef Server handle that part.

The more I think about it, I might suggest some changes to your approach.
The approach you describe sounds to me like the following:

  • client makes normal chef request to proxy
  • proxy authenticates chef request, which requires access to user public
    key (stored in the chef database, different tables for clients and users)
  • proxy re-signs and forwards on request, which requires that the proxy is
    an admin and has permissions to make any request on the chef server
    (security concerns)

I think you'll be in a better position if you make the proxy more
lightweight:

  • client makes normal chef request to proxy
  • proxy doesn't do any authentication, and only compares the type of
    request and the user / client header (x-chef-userid) to do authorization
  • proxy forwards on original request with all headers built in

The second approach is so simple that you could probably code it up in an
nginx config.

Cheers!
Stephen

On Mon, Jun 2, 2014 at 9:39 AM, Jeremy Bingham jbingham@gmail.com wrote:

You'll probably need to set the X-Ops-Request-Source to "web" and have the
proxy pretend it's webui. That way when it checks the authentication
headers it will try and decrypt it with the right public key, rather than
the user's. Since you've decrypted it and re-encrypted it with a different
key, if you don't do that the server will try to decrypt the headers with
the proxy user's key and it will fail.

Conversely you could just forward on the original encrypted auth header.
That might work too.

-j

On Mon, Jun 2, 2014 at 12:15 AM, DV vindimy@gmail.com wrote:

Thanks, my goal's pretty simple - I'm building a proxy that sits between
Chef and clients and provides granular control over who can do what. For
example, we want certain users to be able to view/modify only certain
cookbooks, roles, data bags, etc. For this, the proxy needs to verify
client's request against its public key, and if allowed, encrypt it using
its own client private key and send it on its way.

So far I've been able to create and sign headers thanks to Seth's link,
but not verify headers coming from clients due to the exception I posted.
I'll check out the 10.x branch as suggested by Stephen..

On Sun, Jun 1, 2014 at 11:34 PM, Jeremy Bingham jbingham@gmail.com
wrote:

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding clients
don't ever need to verify the signed headers, only properly assemble and
encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to
sign headers (which is also something I need to do), together it'll be
useful to figuring it out. But haven't found any examples of verifying
signed headers (HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com
wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to
verify a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Stephen Delano
Software Development Engineer
Opscode, Inc.
1008 Western Avenue
Suite 601
Seattle, WA 98104

Yup, I'm replacing the value of X-Ops-Userid header with the proxy's client
name and using its key for signing the request. So far this has been
working really well, actually :slight_smile: Hopefully I'll get the
authenticating-headers-from-client thing working.

On Mon, Jun 2, 2014 at 9:39 AM, Jeremy Bingham jbingham@gmail.com wrote:

You'll probably need to set the X-Ops-Request-Source to "web" and have the
proxy pretend it's webui. That way when it checks the authentication
headers it will try and decrypt it with the right public key, rather than
the user's. Since you've decrypted it and re-encrypted it with a different
key, if you don't do that the server will try to decrypt the headers with
the proxy user's key and it will fail.

Conversely you could just forward on the original encrypted auth header.
That might work too.

-j

On Mon, Jun 2, 2014 at 12:15 AM, DV vindimy@gmail.com wrote:

Thanks, my goal's pretty simple - I'm building a proxy that sits between
Chef and clients and provides granular control over who can do what. For
example, we want certain users to be able to view/modify only certain
cookbooks, roles, data bags, etc. For this, the proxy needs to verify
client's request against its public key, and if allowed, encrypt it using
its own client private key and send it on its way.

So far I've been able to create and sign headers thanks to Seth's link,
but not verify headers coming from clients due to the exception I posted.
I'll check out the 10.x branch as suggested by Stephen..

On Sun, Jun 1, 2014 at 11:34 PM, Jeremy Bingham jbingham@gmail.com
wrote:

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding clients
don't ever need to verify the signed headers, only properly assemble and
encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to
sign headers (which is also something I need to do), together it'll be
useful to figuring it out. But haven't found any examples of verifying
signed headers (HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com
wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to
verify a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

Thanks Stephen, we did consider simply forwarding client's request to Chef,
and indeed this would be super easy to pull off - but the downside is
security and flexibility we get with processing the headers.

With proxy having its own admin-level key, clients/users don't need to have
an admin-level key (we can grant/revoke user access at proxy level quite
easily). So we can have a proxy that guarantees access restrictions are
enforced as well as logs any activity.

In the future, we may have more ambitious requirements for the proxy, such
as modifying requests on the fly (removing passwords from Chef's responses,
for example), or creating something like "anonymous Chef" where Chef
immediately forgets about a node it just bootstrapped.

Luckily since proxy is deployed with Chef, it would be super easy to create
a directory with all the clients' public keys for proxy to refer to, so
that's nice.

On Mon, Jun 2, 2014 at 9:50 AM, Stephen Delano stephen@opscode.com wrote:

I think you'll be best off forwarding on the original request. As far as
time signatures go, you've got 15 minutes to do any authorization work
necessary, which is an eternity in computer time. Keep in mind that your
proxy will also have to have access to user / client public keys for
authentication, unless you'd like to have the Chef Server handle that part.

The more I think about it, I might suggest some changes to your approach.
The approach you describe sounds to me like the following:

  • client makes normal chef request to proxy
  • proxy authenticates chef request, which requires access to user public
    key (stored in the chef database, different tables for clients and users)
  • proxy re-signs and forwards on request, which requires that the proxy is
    an admin and has permissions to make any request on the chef server
    (security concerns)

I think you'll be in a better position if you make the proxy more
lightweight:

  • client makes normal chef request to proxy
  • proxy doesn't do any authentication, and only compares the type of
    request and the user / client header (x-chef-userid) to do authorization
  • proxy forwards on original request with all headers built in

The second approach is so simple that you could probably code it up in an
nginx config.

Cheers!
Stephen

On Mon, Jun 2, 2014 at 9:39 AM, Jeremy Bingham jbingham@gmail.com wrote:

You'll probably need to set the X-Ops-Request-Source to "web" and have
the proxy pretend it's webui. That way when it checks the authentication
headers it will try and decrypt it with the right public key, rather than
the user's. Since you've decrypted it and re-encrypted it with a different
key, if you don't do that the server will try to decrypt the headers with
the proxy user's key and it will fail.

Conversely you could just forward on the original encrypted auth header.
That might work too.

-j

On Mon, Jun 2, 2014 at 12:15 AM, DV vindimy@gmail.com wrote:

Thanks, my goal's pretty simple - I'm building a proxy that sits between
Chef and clients and provides granular control over who can do what. For
example, we want certain users to be able to view/modify only certain
cookbooks, roles, data bags, etc. For this, the proxy needs to verify
client's request against its public key, and if allowed, encrypt it using
its own client private key and send it on its way.

So far I've been able to create and sign headers thanks to Seth's link,
but not verify headers coming from clients due to the exception I posted.
I'll check out the 10.x branch as suggested by Stephen..

On Sun, Jun 1, 2014 at 11:34 PM, Jeremy Bingham jbingham@gmail.com
wrote:

Signed headers are verified server-side by assembling the expected
unencrypted header from the other headers the client sent, decrypting the
signed header sent by the client, and comparing the expected header with
the decrypted header sent by the client. If the results match, it's
verified. Remember that the headers are encrypted with the client's
private key, and decrypted with the public key.

What are you trying to do exactly, though? From my understanding
clients don't ever need to verify the signed headers, only properly
assemble and encrypt them.

-j

On Sun, Jun 1, 2014 at 9:53 PM, DV vindimy@gmail.com wrote:

Thanks for this - I've actually also found one more example of how to
sign headers (which is also something I need to do), together it'll be
useful to figuring it out. But haven't found any examples of verifying
signed headers (HTTPAuthenticationRequest) yet..

https://github.com/opscode/chef/blob/master/lib/chef/http/auth_credentials.rb#L39

On Sun, Jun 1, 2014 at 3:07 PM, Seth Vargo sethvargo@getchef.com
wrote:

https://github.com/sethvargo/chef-api/blob/ee9fac591ba76342c10ceeb2d93e80722fa66e5c/lib/chef-api/connection.rb#L469-L494

Thanks,
Seth

On May 30, 2014, at 7:25 PM, DV vindimy@gmail.com wrote:

Hey all,

I'm having a hard time writing a seemingly simple block of code to
verify a Chef API request using mixlib-authentication gem (
GitHub - chef/mixlib-authentication: AuthN signing and verification. Appears in both the client and server).

I looked at the sample
in spec/mixlib/authentication/http_authentication_request_spec.rb, and came
up with the following, but somehow I get an exception.

Can anyone point me to a sample code or give any pointers? Much
appreciated.

46 @p = Http::Parser.new
47 @p.on_headers_complete = proc do |h|
48 p [:on_headers_complete, h]
49 request = Struct.new(:env, :method, :path)
50 @request = request.new(h, "GET", '/roles')
51 p [:request, @request]

  • 52 @m =
    ::Mixlib::Authentication::HTTPAuthenticationRequest.new(request)*
    53 if @m.user_id() == "TheOne"
    54 conn.relay_to_servers @buffer
    55 @buffer.clear
    56 else
    57 conn.send_data "HTTP/1.1 403 Forbidden\r\n\r\n"
    58 end
    59 end

And the output:

[[:connection,
"GET /roles HTTP/1.1\r\nX-Ops-Timestamp:
2014-05-30T23:10:50Z\r\nX-Remote-Request-Id:
8fbe67a4-35bc-4ee9-985e-5f1bf243175e\r\nX-Ops-Authorization-6:
dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==\r\nX-Ops-Authorization-5:
RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd\r\nX-Ops-Authorization-4:
1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm\r\nX-Ops-Authorization-3:
rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s\r\nX-Ops-Authorization-2:
KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ\r\nX-Ops-Authorization-1:
RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5\r\nX-Chef-Version:
11.12.2\r\nX-Ops-Content-Hash: 2jmj7l5rSw0yVb/vlWAYkK/YBwk=\r\nConnection:
close\r\nX-Ops-Sign: algorithm=sha1;version=1.0;\r\nAccept:
application/json\r\nX-Ops-Userid: XXX\r\nAccept-Encoding:
gzip;q=1.0,deflate;q=0.6,identity;q=0.3\r\nUser-Agent: Chef Knife/11.12.2
(ruby-1.8.7-p352; ohai-7.0.2; x86_64-linux; +http://opscode.com)\r\nHost:
chef11-proxy-1:8080\r\n\r\n"]]

[:on_headers_complete, {"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"}]

[:request, #<struct env={"X-Ops-Timestamp"=>"2014-05-30T23:10:50Z",
"X-Remote-Request-Id"=>"8fbe67a4-35bc-4ee9-985e-5f1bf243175e",
"X-Ops-Authorization-6"=>"dZUoWOdG7LVAxZUKoNLFRHFAWCansQKhCPnuwLOy4g==",
"X-Ops-Authorization-5"=>"RBAEk3dzqBA5DiGFKoIg6Md5tKhtOtlLUn1wpDgjsqTmNRaOtICw+I1ywvHd",
"X-Ops-Authorization-4"=>"1DGUtojK/UpD3ynTaQ3jMvDzV3dRDEXqLfiYbX6rgvoItjkjr+eW8Ihgmvmm",
"X-Ops-Authorization-3"=>"rmPxz4JQxYCur4sZSHDfqK4JHBD7PgCgimPdwiXdGnoeAafc+c3VorBW49/s",
"X-Ops-Authorization-2"=>"KipV+P2K5DFku5tnspDAZEo0Tr/rF9W5mA8gfF8P0sD3blIoH8w5+XQAZ+cQ",
"X-Ops-Authorization-1"=>"RW3GO1FBBhlc1Wpr99XMy+TUIjIOF9WRM46dmpOFxgM3anJuq0RlDxxtT6T5",
"X-Chef-Version"=>"11.12.2",
"X-Ops-Content-Hash"=>"2jmj7l5rSw0yVb/vlWAYkK/YBwk=",
"Connection"=>"close", "X-Ops-Sign"=>"algorithm=sha1;version=1.0;",
"Accept"=>"application/json", "X-Ops-Userid"=>"XXX",
"Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3",
"User-Agent"=>"Chef Knife/11.12.2 (ruby-1.8.7-p352; ohai-7.0.2;
x86_64-linux; +http://opscode.com)", "Host"=>"chef11-proxy-1:8080"},
method="GET", path="/roles">]

/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:36:in
headers': undefined method env' for #Class:0x00000001ef4178
(NoMethodError)

from
/usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:77:in
validate_headers!' from /usr/lib64/ruby/gems/1.9.1/gems/mixlib-authentication-1.3.0/lib/mixlib/authentication/http_authentication_request.rb:32:in initialize'
from
/usr/lib64/ruby/gems/1.9.1/gems/chef-proxy-0.0.1/lib/chef/proxy.rb:52:in
`new'

Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Best regards, Dmitriy V.

--
Stephen Delano
Software Development Engineer
Opscode, Inc.
1008 Western Avenue
Suite 601
Seattle, WA 98104

--
Best regards, Dmitriy V.