remove specific cookie in nginx reverse proxy - security

I have a nginx as reverse proxy that proxies my requests to different destinations. Client send different to nginx. I want to remove a specific cookie for one of my locations. For example if client send cookie A and B, I want to sent A form for /api.
How can I do that?

Assuming you are using proxy_pass directive and your cookie name is my_cookie, you can cut this cookie and its value from Cookie HTTP header this way:
location /api {
# save original "Cookie" header value
set $altered_cookie $http_cookie;
# check if the "my_cookie" cookie is present
if ($http_cookie ~ '(.*)(^|;\s)my_cookie=("[^"]*"|[^\s]*[^;]?)(\2|$|;$)(?:;\s)?(.*)') {
# cut "my_cookie" cookie from the string
set $altered_cookie $1$4$5;
}
# hide original "Cookie" header
proxy_hide_header Cookie;
# set "Cookie" header to the new value
proxy_set_header Cookie $altered_cookie;
... # other proxy settings here
proxy_pass <upstream>; # change to your upstream server
}
This complex regex allows to check if the my_cookie cookie is present no matter it is at the beginning, at the middle or at the end of Cookie header value. Here are several examples showing how this regex works on different strings:
Whole "Cookie" string $1 $2 $3 $4 $5 $1$4$5
----------------------------------------------------------- -------------------- ---- ---------- ---- --------------------- -----------------------------------------
"some_cookie=value1; my_cookie=value2; other_cookie=value3" "some_cookie=value1" "; " "value2" "; " "other_cookie=value3" "some_cookie=value1; other_cookie=value3"
"some_cookie=value1; my_cookie=value2" "some_cookie=value1" "; " "value2" "" "" "some_cookie=value1"
"my_cookie=value2; other_cookie=value3" "" "" "value2; " "" "other_cookie=value3" "other_cookie=value3"
"my_cookie=value2" "" "" "value2" "" "" ""
For those who are looking for the same recipe but use fastcgi_pass instead of proxy_pass - use fastcgi_param HTTP_COOKIE $altered_cookie if_not_empty; instead of proxy_hide_header and proxy_set_header directives.

Related

Getting the characters of a string up to the first "."

I'm attempting to use Perl's gethostnamebyaddr function. The annoying thing is that it returns the entire domain name in scalar format. I want to parse out only the hostname and discard the rest.
I'm using split to divide the domain name into an array and then taking only the first value but this doesn't seem to work.
#!/usr/bin/perl
use Socket;
my $name;
my $hostname;
my #tmpStr;
$name = gethostbyaddr(inet_aton("192.168.2.3"), AF_INET);
print "$name\n";
#tmpStr = split ".", $name;
$hostname = $tmpStr[0];
print "Host name is $hostname\n";
When the above code is executed, I get the following:
dc1-ent.ent.ped.local
Host name is
According to this website the return value is not a string but is rather a scalar value and so my attempt at splitting it doesn't work.
I can't figure out how to convert it to a string before I can split it or parse out the hostname by itself.
The dot character has special meaning for regular expressions in Perl, and the 1st argument to split is a regular expression. You need to escape the dot:
use warnings;
use strict;
my $name = 'dc1-ent.ent.ped.local';
print "$name\n";
my #tmpStr = split /\./, $name;
my $hostname = $tmpStr[0];
print "Host name is $hostname\n";
This outputs:
dc1-ent.ent.ped.local
Host name is dc1-ent
I would write it like this
my $name = gethostbyaddr(inet_aton('192.168.2.3'), AF_INET);
my ($host) = $name =~ /([^.]+)/;
say $host;
Your problem is not related to gethostbyaddr() but by what follows.
Proof:
DB<1> $name = 'dc1-ent.ent.ped.local';
DB<2> #tmpStr = split ".", $name;
DB<3> print #tmpStr;
(nothing printed)
Try instead using split that way:
DB<8> $name = 'dc1-ent.ent.ped.local';
DB<9> #tmpStr = split(/\./, $name);
DB<10> print #tmpStr;
dc1-ententpedlocal
DB<11> print join(' ', #tmpStr);
dc1-ent ent ped local
DB<12> x #tmpStr;
0 'dc1-ent'
1 'ent'
2 'ped'
3 'local'
Or if you absolutely want a string and not a regex, protect the dot also as your string is still parsed as a regular expression (which is why being explicit with / / has its merits, it forces you to remember that some character have special meaning there, like the dot):
DB<1> $name = 'dc1-ent.ent.ped.local';
DB<2> #tmpStr = split('.', $name);
DB<3> print #tmpStr;
DB<4> #tmpStr = split('\.', $name);
DB<5> x #tmpStr
0 'dc1-ent'
1 'ent'
2 'ped'
3 'local'

varnish compilation fails on or statement

I'm trying to allow logins on my site. So I setup the following in my VCL under varnish 4:
# Allow the beta site to login
if ( req.http.host ~ "^beta\.example\.com$" && req.url ~ "^?oa_social_login_source=custom$" ) {
return (pass);
}
But when I go to do a syntax check on the VCL I get the following error:
#varnishd -C -f default.vcl
Message from VCC-compiler:
Regexp compilation error:
nothing to repeat
('input' Line 111 Pos 62)
if ( req.http.host ~ "^beta\.example\.com$" && req.url ~ "^?oa_social_login_source=custom$" ) {
-------------------------------------------------------------##################################----
Running VCC-compiler failed, exited with 2
VCL compilation failed
Can anybody help me out with the syntax on what I'm trying to achieve?
Thanks,
Tim
You need to escape the question mark - as that is regexp quantifier sign.
req.url ~ "^\?oa_social_login_source=custom$"

Using -f operator on a string that contains curly braces

Consider the following here-string that I am trying to use a template for my Nagios definitions.
$blankDefinition = #"
define host{
use windows-server ; Inherit default values from a template
host_name {0} ; The name we're giving to this host
alias {0} ; A longer name associated with the host
address {1} ; IP address of the host
}
"#
I have a script that determines which of my servers are not in nagois that should be. As it loops I'm trying to have it spit out a definion for me so that I can copy and paste into my Nagios config.
$comparison | %{
$blankDefinition -f $($_.serverName),$($_.ipAddress)
}
Which nets me the following error:
Error formatting a string: Input string was not in a correct format..
Something like this works just fine
#"
Testing
One
{0}
Three
"# -f "Twelve"
So then I found out that it is because of the other curly braces. Is there a way that i can format my string using the -f while keeping the braces? Or do i have to use variable subsitution only?
Adding another set of curly braces seems to work:
$blankDefinition = #"
define host{{
use windows-server ; Inherit default values from a template
host_name {0} ; The name we're giving to this host
alias {0} ; A longer name associated with the host
address {1} ; IP address of the host
}}
"#
$blankDefinition -f 'foo', 'bar', 'foo'
Output:
define host{
use windows-server ; Inherit default values from a template
host_name foo ; The name we're giving to this host
alias foo ; A longer name associated with the host
address bar ; IP address of the host
}

How can I split a CA certificate bundle into separate files?

I'm working with OpenSSL and need a sane default list of CAs. I'm using Mozilla's list of trusted CAs, as bundled by cURL. However, I need to split this bundle of CA certs, because the OpenSSL documentation says:
If CApath is not NULL, it points to a directory containing CA certificates in PEM format. The files each contain one CA certificate. The files are looked up by the CA subject name hash value, which must hence be available.
For example, using the ca-bundle.crt file directly works fine:
openssl-1.0.1g> ./apps/openssl s_client -connect www.google.com:443 -CAfile /home/user/certs/ca-bundle.crt
...
Verify return code: 0 (ok)
---
DONE
But specifying the directory containing the ca-bundle.crt file does not work:
openssl-1.0.1g> ./apps/openssl s_client -connect www.google.com:443 -CApath /opt/aspera/certs
Verify return code: 20 (unable to get local issuer certificate)
---
DONE
I presume this is because my folder doesn't adhere to what the documentation asks for (namely, a directory containing CA certs in PEM format, with each file containing one cert, named by hash value). My directory just has the single bundle of certs.
How can I split my bundle of certs to adhere to OpenSSL's request that each cert be in an individual file? Bonus points if the hashing can be done too (though if needed I could write a script to do that myself if all the certs are in individual files).
You can split the bundle with awk, like this, in an appropriate directory:
awk 'BEGIN {c=0;} /BEGIN CERT/{c++} { print > "cert." c ".pem"}' < ca-bundle.pem
Then, create the links OpenSSL wants by running the c_rehash utility that comes with OpenSSL:
c_rehash .
Note: use 'gawk' on non linux-platforms - as above relies on a GNU specific feature.
Just to give an alternative; facing the same issue I ended up with csplit:
csplit -k -f bar foo.pem '/END CERTIFICATE/+1' {10}
If you want to get a single certificate out of a multi-certificate PEM, try:
$ awk '/subject.*CN=host.domain.com/,/END CERTIFICATE/' INPUT.PEM
source
The following Ruby-script will split the bundle (with one or more certificates in it) into files named after the hashes -- side-stepping the c_rehash step in most cases.
To use, cd into the right directory (such as /etc/ssl/certs/) and run the script with the path to your certificate bundle as the sole argument. For example: ruby /tmp/split-certificates.rb ca-root-nss.crt.
#!/usr/bin/env ruby
require 'openssl'
blob = IO.binread(ARGV[0]) # Read the entire file at once
DELIMITER = "\n-----END CERTIFICATE-----\n"
blobs = blob.split(DELIMITER)
blobs.each do |blob|
blob.strip!
blob += DELIMITER # Does not break DER
begin
cert = OpenSSL::X509::Certificate.new blob
rescue
puts "Skipping what seems like junk"
next
end
begin
# XXX Need to handle clashes, suffix other than 0
filename=sprintf("%x.0", cert.subject.hash)
File.open(filename,
File::WRONLY|File::CREAT|File::EXCL) do |f|
f.write(blob)
end
rescue Errno::EEXIST
puts "#{filename} already exists, skipping"
end
end
Here is mine in Perl (so much code, but I like gonzo programming):
#!/usr/bin/perl -w
# -------
# Split "certificate bundles" like those found in /etc/pki/tls/certs into
# individual files and append the X509 cleartext description to each file.
#
# The file to split is given on the command line or piped via STDIN.
#
# Files are simply created in the current directory!
#
# Created files are named "certificate.XX" or "trusted-certificate.XX",
# with XX an index value.
#
# If a file with the same name as the output file already exists, it is not
# overwritten. Instead a new name with a higher index is tried.
#
# This works for bundles of both trusted and non-trusted certificates.
#
# See http://tygerclan.net/?q=node/49 for another program of this kind,
# which sets the name of the split-off files in function of the subject
# -------
my #lines = <> or die "Could not slurp: $!";
my $state = "outside"; # reader state machine state
my $count = 0; # index of the certificate file we create
my $fh; # file handle of the certificate file we create
my $fn; # file name of the certificate file we create
my $trusted; # either undef or "TRUSTED" depend on type of certificate
for my $line (#lines) {
chomp $line;
if ($state eq "outside") {
if ($line =~ /^(-----BEGIN (TRUSTED )?CERTIFICATE-----)\s*$/) {
my $marker = $1;
$trusted = $2;
$state = "inside";
my $created = 0;
my $prefix = "";
if ($trusted) {
$prefix = "trusted-"
}
while (!$created) {
$fn = "${prefix}certificate.$count";
$count++;
if (-f $fn) {
# print STDERR "File '$fn' exists; increasing version number to $count\n";
}
else {
print STDERR "Certificate data goes to file '$fn'\n";
open($fh,">$fn") || die "Could not create file '$fn': $!\n";
$created = 1;
print $fh "$marker\n"
}
}
}
else {
print STDERR "Skipping line '$line'\n"
}
}
else {
if ($line =~ /^(-----END (TRUSTED )?CERTIFICATE-----)\s*$/) {
my $marker = $1;
my $trustedCheck = $2;
if (!((($trusted && $trustedCheck) || (!$trusted && !$trustedCheck)))) {
die "Trusted flag difference detected\n"
}
$state = "outside";
print $fh "$marker\n";
print STDERR "Closing file '$fn'\n";
close $fh;
# Append x509 cleartext output by calling openssl tool
`openssl x509 -noout -text -in '$fn' >> '$fn'`;
if ($? != 0) {
die "Could not run 'openssl x509' command: $!\n";
}
}
else {
print $fh "$line\n"
}
}
}
if ($state eq "inside") {
die "Last certificate was not properly terminated\n";
}

Replace part of URL with IP adress

What I want to do is something like below. I have a URL, say http://www.google.com/one/two/three
I need to extract the main domain name "www.google.com", to feed it to nslookup (As nslookup/dig does not seem to work with full URL) and then replace the URL with resolved IP address.e.g.
$ echo "http://www.google.com/one/two/three" | sed "s/<pattern>//g"
$ www.google.com
The problem is that "http://" may not always be there. And then
$ echo "http://www.google.com/one/two/three" | sed "s/<pattern>//g"
$ http://11.22.33.44/one/two/three
Can anyone provide any related link or related examples ?
Try this sed command:
echo "http://www.google.com/one/two/three" | sed -r 's#(https?://)?([^/]+).*#\2#'
OUTPUT:
www.google.com
And when you have fetched IP address:
$> IP="11.22.33.44"
$> echo "https://www.google.com/one/two/three" | sed -r "s#(https?://)?([^/]+)(.*)#\1$IP\3#"
https://11.22.33.44/one/two/three
This will work with http://, https:// or without any https?:// before the URL.
Since URL's can be messy ("http://user:pass#www.example.com:8080/one/two/three") in the general case I'd recommend using a language with a URI parsing library. For example
url="http://stackoverflow.com/questions/18087200/replace-part-of-url-with-ip-adress"
newurl=$(perl -MURI -le '
$uri = URI->new(shift);
$cmd = "nslookup " . $uri->host;
$out = qx($cmd);
if ($out =~ /Name:.*?\KAddress:\s+([\d.]+)/s) {
$ip = $1;
$uri->host($ip);
print $uri
} else {warn "cannot find IP address in nslookup output"}
' "$url")
echo "$newurl"
outputs
http://198.252.206.16/questions/18087200/replace-part-of-url-with-ip-adress

Resources