0

Compiling NGINX with — with-http_auth_request_module on Centos 7

While looking at SSO solutions I decided to investigate a bit more options how I could use Nginx with solutions like Okta to protect my resources. One of interesting ones was using authentication proxy with Nginx.

The afore functionality is available through use of http_auth_request_module. However this module is not compiled by default. This got me the idea that would be nice to exercise going step by step through compiling Nginx with auth module Centos 7.

Yes – I do know that there are solutions on the market/internet which would save me from this – however I value the learning process in this challenge as well 🙂 If you have interesting links to alternatives please leave them in the comment section.

Getting the sources

Our journey begins with getting the sources. I have tried following the official Nginx documentation but I find it …. somehow not up to the task. Hence there are some modifications or additions that I did to get this through 🙂

mkdir nginx-from-source && cd $_

Once we have our new folder we can download the pre-reqs

Here we are taking Nginx version 1.19.0 – please be sure to check whats the latest version before running the command

   wget https://ftp.pcre.org/pub/pcre/pcre-8.44.tar.gz
   wget http://zlib.net/zlib-1.2.11.tar.gz
   wget http://www.openssl.org/source/openssl-1.1.1g.tar.gz
   wget https://nginx.org/download/nginx-1.19.0.tar.gz
   tar zxf nginx-1.19.0.tar.gz

Compile PCRE

tar -zxf pcre-8.44.tar.gz
cd pcre-8.44
./configure
make
sudo make install

Compile ZLIB

tar -zxf zlib-1.2.11.tar.gz
cd zlib-1.2.11
./configure
make
sudo make install

Compiling OpenSSL

OpenSSL deserves spot for bit more insights than just dry code. We will use never version than the one running on the box right now.

Pre-reqs

We will start off by installing required packages via yum and extracting the content of downloaded archive

yum group install 'Development Tools'
yum install perl-core zlib-devel -y
tar -xf openssl-1.1.1g.tar.gz 
cd openssl-1.1.1g

Configure & install OpenSSL

sudo ./config --prefix=/usr/local/ssl --openssldir=/usr/local/ssl shared zlib
sudo make
sudo make test
sudo make install

Configure shared libraries

Navigate to /etc/ld.so.conf.d and run the following

sudo echo "/usr/local/ssl/lib" >> /etc/ld.so.conf.d/openssl-1.1.1g.conf

ldconfig is used to create, update and remove symbolic links for the current shared libraries based on the lib directories present in the /etc/ld.so.conf

Reload with verbose

sudo ldconfig -v

Configure OpenSSL binary

Start with backing up the current OpenSSL

sudo mv /bin/openssl /bin/openssl.backup

Create script which will be executed on the system…

sudo vi /etc/profile.d/openssl.sh

… and set contents to

OPENSSL_PATH="/usr/local/ssl/bin"
export OPENSSL_PATH
PATH=$PATH:$OPENSSL_PATH
export PATH

Once done we need to make sure that the script is allowed to be executed

sudo chmod +x /etc/profile.d/openssl.sh

Next reload the profile to get the openSSL new binary with your PATH

source /etc/profile.d/openssl.sh

Verify openSSL version

 which openssl
 openssl version -a

If you reached this moment then we are ready to move on the next part 🙂

Compiling Nginx with extra modules

Create user under which the process will be running

useradd -s/sbin/nologin -d/usr/local/nginx -M nginx

Navigate to folder with nginx sources created during download of our pre-reqs and run the config command

./configure 
--user=nginx 
--group=nginx 
--error-log-path=/var/log/nginx/error.log 
--http-log-path=/var/log/nginx/access.log 
--sbin-path=/usr/local/nginx/nginx 
--pid-path=/usr/local/nginx/nginx.pid 
--with-pcre=../pcre-8.44 
--with-zlib=../zlib-1.2.11 
--with-http_auth_request_module 
--with-http_geoip_module 
--with-http_gzip_static_module 
--with-http_gunzip_module 
--with-http_realip_module 
--with-http_secure_link_module 
--with-http_slice_module 
--with-http_ssl_module 
--with-http_v2_module

The above compiles Nginx with extra modules. For a comprehensive list with detailed information about each of the extra modules please refer to official Nginx documentation.

Once the above command finishes run

  make
  make install

When the above process finishes you should have nginx installed in /usr/local/nginx

Initial configuration of Nginx

In order to use Nginx we need to configure it. Right now our system knows nothing about running it.

Run vi /etc/systemd/system/nginx.service and set the content to

[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/nginx.pid
ExecStartPre=/usr/local/nginx/nginx -t
ExecStart=/usr/local/nginx/nginx
ExecReload=/usr/local/nginx/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Now when you run systemctl status nginx you should see our service available.

Go ahead and run it! Type systemctl start nginx

At this moment you should have Nginx running with extra modules compiled!

0

Golang app to authenticate with AWS Cognito Pool

Since started to work with AWS I sometimes hit the same problems more than one time 😉 One of those happen was when working with AWS Cognito – just needed to authenticate and get token – or just verify the user 😉 using command line.  I honestly did not want to be bothered with any complexity to get simple tokens which I planned to use in accessing other systems etc.

For this purposes I have created simple CLI ( right now with just 2 methods ) to help me out in those situations.  Usage is extremely simple you just need to have your AWS profile configured and have details of your AppClient from your user pool.

Authenticate

> [SHELL] RafPe $ go-cognito-authy --profile cloudy --region eu-central-1 auth --username rafpe --password 'Password.0ne!'  --clientID 2jxxxiuui123
{
AuthenticationResult: {
    AccessToken: "eyJraWQiOiJ0QXVBNmxtNngrYkxoSmZ",
    ExpiresIn: 3600,
    IdToken: "eyJraWQiOiJ0bHF2UElTV0pn",
    RefreshToken: "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R-TpkR_uompG7fyajYeFvn-rJVC_tDO4pB3",
    TokenType: "Bearer"
},
ChallengeParameters: {}
}

which in return should give you response with tokens needed further in your adventures with AWS…. but if you would have user in state that a password needs to be changed 😉 ….

> [INSERT] RafPe $ go-cognito-authy --profile cloudy --region eu-central-1 auth --username rafpe --password 'Password.0ne!'  --clientID 2jxxxiuui123
{
ChallengeName: "NEW_PASSWORD_REQUIRED",
ChallengeParameters: {
    requiredAttributes: "[]",
    userAttributes: "{\"email_verified\":\"true\",\"email\":\"[email protected]\"}",
    USER_ID_FOR_SRP: "rafpe"
},
Session: "bCqSkLeoJR_ys...."
}

 

Administratively set new pass

With the session above and known challenge for new pass you can use it to set desired password

> [INSERT] RafPe $ go-cognito-authy --profile cloudy -region eu-central-1 admin reset-pass --username rafpe --pass-new 'Password.0ne2!' --clientID 2jxxxiuui123 --userPoolID  eu-central-1_CWNnTiR0j --session "bCqSkLeoJR_ys...."

and voilla 😉 we can now continue playing with tokens

 

 

Patches welcome

The whole solution is available on Github https://github.com/RafPe/go-cognito-authy/tree/master  and if you are missing something please create a PR 😉

19

.Net core JWT authentication using AWS Cognito User Pool

While working with .net core I needed to create API. For this what I aimed to have was proper authentication. Therefore I decided to use JSON Web Token (JWT) authentication.

However I wanted to avoid creating any of this logic by myself or spending too much time on it. That’s why I decided to use AWS Cognito User Pools to provide me with user management and to generate JWT I need.

It took me some time to gather information how to wire it all together so I will try to outline the most important.

AWS setup

  1. Create user pool in AWS Cognito
  2. Get the newly created user pool ID and run the following command
    curl https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/jwks.json > result.json

    * if you want to you can also just navigate to the URL (https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/jwks.json ) . Just replace region and user pool ID with correct information.

  3. The information you receive will be used by us to validate the tokens given by AWS.
  4. Save the results for later use.

.Net core API project

  1. Create new .net core webapi project
    dotnet new webapi
  2. Install additional packages
    dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
    dotnet add package Microsoft.IdentityModel.Tokens
    dotnet add package Microsoft.AspNetCore.Identity

     

  3. add JWT authentication policy ( we will decorate our controllers with it )
                services.AddAuthorization(auth =>
                {
                    auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()   
                        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme‌​)
                        .RequireAuthenticatedUser()
                        .Build());
                });

     

  4. Before making further modifications we will add 2 methods used which will be used to validate the signature and issuer ( this has potential to be made much better 🙂 )Key is the “n” value and Expo is the “e” value in the keys you got form the url in AWS setup
            public RsaSecurityKey SigningKey(string Key, string Expo)
            {
                    RSA rrr = RSA.Create();
    
                    rrr.ImportParameters(
                        new RSAParameters()
                        {
                            Modulus =  Base64UrlEncoder.DecodeBytes(Key),
                            Exponent = Base64UrlEncoder.DecodeBytes(Expo)
                        }
                    );
        
                    return new RsaSecurityKey(rrr);  
            }
    
            public TokenValidationParameters TokenValidationParameters(string issuer)
            {
                    // Basic settings - signing key to validate with, audience and issuer.
                    return new TokenValidationParameters
                    {
                        // Basic settings - signing key to validate with, IssuerSigningKey and issuer.
                        IssuerSigningKey = this.SigningKey(<key-comes-here>,<expo-comes-here>),
                        ValidIssuer      = issuer,
                            
                        // when receiving a token, check that the signing key
                        ValidateIssuerSigningKey = true,
        
                        // When receiving a token, check that we've signed it.
                        ValidateIssuer = true,
        
                        // When receiving a token, check that it is still valid.
                        ValidateLifetime = true,
                            
                        // Do not validate Audience on the "access" token since Cognito does not supply it but it is      on the "id"
                        ValidateAudience = false,
        
                        // This defines the maximum allowable clock skew - i.e. provides a tolerance on the token expiry time 
                        // when validating the lifetime. As we're creating the tokens locally and validating them on the same 
                        // machines which should have synchronised time, this can be set to zero. Where external tokens are
                        // used, some leeway here could be useful.
                        ClockSkew = TimeSpan.FromMinutes(0)
                    };
                
            }

     

  5. Modify Configure method to enable JWT
                app.UseJwtBearerAuthentication(new JwtBearerOptions()
                { 
                    
                    TokenValidationParameters = this.TokenValidationParameters(<issuer-comes-here>)
                });

    The issuer format has the following format : https://cognito-idp.<region>.amazonaws.com/<user-pool-id>

     

  6. Modify controller and enable the authentication by using the following decorator
        [Authorize(Policy = "Bearer")]

     

Testing the solution

With the authentication enabled we get the following while requesting controller

> http http://localhost:5000/api/values

HTTP/1.1 401 Unauthorized
Content-Length: 0
Date: Sun, 30 Jul 2017 11:41:33 GMT
Server: Kestrel
WWW-Authenticate: Bearer

 

And if we pass the JWT 🙂

http --auth-type=jwt -v http://localhost:5000/api/values

GET /api/values HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Authorization: Bearer ey.....
Host: localhost:5000
User-Agent: HTTPie/0.9.9



HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Sun, 30 Jul 2017 11:45:41 GMT
Server: Kestrel
Transfer-Encoding: chunked

 

 

Code

Full gist below 🙂

 

1

Redhat 7 – LDAP authentication using Ansible

Hey! Recently along with Sanderv32 we have been trying to get LDAP authentication working on Redhat machines. I must admit that we have spent some quite looking for more structured and decent information how to get this working. However up to our surprise information were completely inaccurate or outdated.

So without big delays we have decided to tackle this challenge using Ansible. Of course first attempts were just to get the idea working. As we were moving our playbook were growing to reach stage at which we could deploy LDAP authentication mechanism to all of our RedHat 7 systems

Below is the output of the runbook being used:

    - name: "LDAP Authentication | Install the required packages"
      yum: >
        name="{{item}}"
        state=present
      with_items:
        - "nss-pam-ldapd"
        - "oddjob"
        - "oddjob-mkhomedir"
      tags:
        - "ldap"
        - "packages"
        - "packages_ldap"

    - name: "LDAP Authentication | Ensure services are running"
      service:
          name={{item}}
          enabled=yes
          state=started
      with_items:
        - "nscd"
        - "nslcd"
        - "oddjobd"
      register: services_ldap
      tags:
        - "ldap"
        - "services_ldap"

    - name: "Debug | Display results"
      debug: msg="{{services_ldap.results}}"
      tags:
        - "ldap"

    - name: "LDAP Authentication | Enable LDAP PAM modules"
      command: "authconfig --enableldap --enableldapauth --enablemkhomedir --update"
      tags:
        - "ldap"

    - name: "LDAP Authentication | Adding configuration templates"
      template: >
        src="templates/{{item}}.j2"
        dest="/etc/{{item}}"
      with_items:
        - "nslcd.conf"
      tags:
        - "ldap"
        - "repository"
        - "repository_ldap"
      notify:
        - restart services ldap

And associated handler

---
  - name: "restart services ldap"
    service: >
      name="{{item.name}}" 
      state=restarted
    with_items: services_ldap.results
    tags:
      - "ldap"
      - "services_ldap"

 

In the above I have highlighted the part which we use to template NLSCD config file. The file contents are completely overwritten so make sure you adjust it to your needs.

This template has been used to connect to Active Directory with dedicated bind user and modified pagesize ( so our results are not trimmed )

# {{ ansible_managed }}
uid nslcd
gid ldap

uri {{ ldap.uri }}
base {{ ldap.basedn }}
binddn {{ ldap.binduser }}
bindpw {{ ldap.binduserpw }}
scope sub
tls_reqcert allow

pagesize 10000
referrals off
idle_timelimit 800
filter passwd (&(objectClass=user)(!(objectClass=computer))(uidNumber=*)(unixHomeDirectory=*))
map    passwd uid              sAMAccountName
map    passwd homeDirectory    unixHomeDirectory
map    passwd gecos            displayName
map    passwd loginShell       "/bin/bash"
filter shadow (&(objectClass=user)(!(objectClass=computer))(uidNumber=*)(unixHomeDirectory=*))
map    shadow uid              sAMAccountName
map    shadow shadowLastChange pwdLastSet
filter group  (objectClass=group)


ssl no
tls_cacertdir /etc/openldap/cacerts

 

 

Thats it folks! If it would not work with you please leave some comments as this is used to make sure we have means of using LDAP auth on Linux boxes