Giter VIP home page Giter VIP logo

burakkavak / nginx-auth-server Goto Github PK

View Code? Open in Web Editor NEW
9.0 2.0 5.0 9.38 MB

lightweight authentication server designed to be used with the nginx 'http_auth_request' module / subrequest based authentication using the 'auth_request' directive

License: MIT License

Makefile 1.96% Go 66.94% TypeScript 15.93% HTML 3.86% JavaScript 3.81% Dockerfile 3.11% Shell 2.30% SCSS 2.10%
auth authentication go golang nginx subrequest http-auth-request-module http ldap recaptcha 2fa otp totp docker

nginx-auth-server's Introduction

nginx-auth-server

A lightweight authentication server designed to be used in conjunction with nginx 'http_auth_request_module'. nginx-auth-server provides an additional authentication layer that is useful for reverse proxy scenarios, where the proxy does not support user authentication.

Table of Contents

Demo

demo.gif

Features

  • low latency (<1ms)
  • support for Two-Factor Authentication (2FA)
  • support for LDAP to validate user credentials
  • optional bot protection with Google reCAPTCHA

Getting Started

With Docker

Download the docker-compose.yml into a directory of your liking.

$ mkdir -p ~/docker/nginx-auth-server
$ cd ~/docker/nginx-auth-server
$ wget --content-disposition https://raw.githubusercontent.com/burakkavak/nginx-auth-server/master/docker-compose.yml

Start the container:

$ docker-compose up -d

You can now point a NGINX server to this docker container, please refer to the 'Native' section for a NGINX configuration example.

The CLI is called using docker exec. Please refer to the CLI reference for all commands. Here are some examples:

# docker exec -it <container_name> nginx-auth-server <command_parameters>
$ docker exec -it nginx-auth-server nginx-auth-server user add --username foo
$ docker exec -it nginx-auth-server nginx-auth-server user list
$ docker exec -it nginx-auth-server nginx-auth-server cookie list

Environment variables

The docker application can be configured using environment variables. Modify the docker-compose.yml and restart the container so the changes take effect.

Environment variable Default value Description
SERVER_LISTEN_ADDRESS 0.0.0.0 The HTTP(S) server is listening to requests on this address (inside the container)
SERVER_LISTEN_PORT 17397 The application is going to listen for HTTP requests on this port (inside the container)
SERVER_DOMAIN localhost Domain used to set the authentication cookie and as issuer for TOTP. E.g. example.org
TLS_ENABLED false Enable HTTPS/TLS encryption for the webserver. The unencrypted HTTP server will be disabled
TLS_LISTEN_PORT 17760 The application is going to listen for HTTPS requests on this port (inside the container)
TLS_CERT_PATH /opt/nginx-auth-server/certs/server.crt Path of the SSL certificate (inside the container)
TLS_CERT_KEY /opt/nginx-auth-server/certs/server.key Path of the SSL certificate key (inside the container)
COOKIES_LIFETIME 7 Cookie lifetime in days. User has to re-authenticate after expiration
COOKIES_SECURE true Set secure attribute for cookies. The browser will only send the auth cookie in a HTTPS context if this is enabled
LDAP_ENABLED false Enable/disable LDAP support. The application will prioritize local authentication data first
LDAP_URL LDAP url. Example for TLS connection: ldaps://ldap.example.com:636. Example for non-TLS connection: ldap://ldap.example.com:389
LDAP_ORGANIZATIONAL_UNIT users LDAP organizational unit (OU) that is used to search the user
LDAP_DOMAIN_COMPONENTS LDAP baseDN (DC) of the LDAP tree. Example: dc=example,dc=org
RECAPTCHA_ENABLED false Enable/disable Google reCAPTCHA v2 (invisible) support for the login form
RECAPTCHA_SITE_KEY reCAPTCHA site key that is provided by Google upon site creation
RECAPTCHA_SECRET_KEY reCAPTCHA secret key that is provided by Google upon site creation

Native

Download the appropriate binary from the Releases section.

Download the current config.ini into the same directory:

$ wget --content-disposition https://raw.githubusercontent.com/burakkavak/nginx-auth-server/master/config.ini

Run the server:

$ ./nginx-auth-server run

For user management (adding/removing users) refer to the CLI usage information:

$ ./nginx-auth-server help
$ ./nginx-auth-server user add --username foo --otp

Reconfigure nginx server:

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /var/www/html;

  index index.html index.htm index.nginx-debian.html;

  server_name _;

  # Redirect user to /login if nginx-auth-server responds with '401 Unauthorized'
  error_page 401 /login;

  location / {
    auth_request /auth;

    # pass Set-Cookie headers from the subrequest response back to requestor
    auth_request_set $auth_cookie $upstream_http_set_cookie;
    add_header Set-Cookie $auth_cookie;

    auth_request_set $auth_status $upstream_status;

    # serve files if the user is authenticated
    try_files $uri $uri/ /index.html;
  }

  location = /auth {
    # internally only, /auth can not be accessed from outside
    internal;

    # nginx-auth-server running on port 17397
    proxy_pass http://localhost:17397;

    # don't pass request body to proxied server, we only need the headers which are passed on by default
    proxy_pass_request_body off;

    # there is no content length since we stripped the request body
    proxy_set_header Content-Length "";

    # let proxy server know more details of request
    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Remote-Addr $remote_addr;
    proxy_set_header X-Original-Host $host;
  }

  # these are handled by nginx-auth-server as part of the auth routines
  location ~ ^/(login|logout|whoami)$ {
    proxy_pass http://localhost:17397;

    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Remote-Addr $remote_addr;
    proxy_set_header X-Original-Host $host;
  }

  # static nginx-auth-server assets (css, js, ...)
  location /nginx-auth-server-static {
    proxy_pass http://localhost:17397/nginx-auth-server-static;

    proxy_set_header X-Original-URI $request_uri;
    proxy_set_header X-Original-Remote-Addr $remote_addr;
    proxy_set_header X-Original-Host $host;
  }
}

You can also run the server as a systemd service. Example configuration for user www-data:

[Unit]
Description=nginx-auth-server
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/www/nginx-auth-server
ExecStart=/var/www/nginx-auth-server/nginx-auth-server run
Restart=on-failure
# Other restart options: always, on-abort, etc

# The install section is needed to use
# `systemctl enable` to start on boot
# For a user service that you want to enable
# and start automatically, use `default.target`
# For system level services, use `multi-user.target`
[Install]
WantedBy=multi-user.target

Contributing

Fork this repo and checkout the develop branch.

$ git clone <your_forked_repo> -b develop
$ cd nginx-auth-server

Install the npm dependencies.

$ npm i

Build the JavaScript/TypeScript/SCSS stack once.

$ npm run build

Run the Go application

$ go build -o nginx-auth-server ./src/ && ./nginx-auth-server run

You can now point a nginx webserver to this auth-server. Refer to the nginx configuration in the Getting Started section.

If you want to make changes in the TypeScript/SCSS, you can run npm in watch mode:

$ npm run watch-ts
$ npm run watch-scss

You have to restart the Go application after every change for the changes to take effect.

Documentation

The CLI and HTTP API documentation is available here: https://burakkavak.github.io/nginx-auth-server/

Changelog

See CHANGELOG

Credits

License

See LICENSE

nginx-auth-server's People

Contributors

burakkavak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

nginx-auth-server's Issues

auth server can not boot on CentOS 7

While run auth server on CentOS 7, it through a error:

# ./nginx-auth-server-linux-amd64 
./nginx-auth-server-linux-amd64: /lib64/libc.so.6: version `GLIBC_2.32' not found (required by ./nginx-auth-server-linux-amd64)
./nginx-auth-server-linux-amd64: /lib64/libc.so.6: version `GLIBC_2.34' not found (required by ./nginx-auth-server-linux-amd64)

It seems need glibc 2.32, but centos 7 default glibc is 2.17.

Does the auth server support CentOS 7?

callback url handling

A little patch to handle call back URL to return to original resource after authorization.

index 9ed14f3..462e03a 100644
--- a/src/main.go
+++ b/src/main.go
@@ -212,7 +212,8 @@ func login(c *gin.Context) {
        if err == nil {
                if _, err = VerifyCookie(token); err == nil {
                        // user already authorized
-                       c.Status(200)
+                       //c.Status(200)
+                       c.Redirect(302, c.Query("callback"))
                        return
                }
        }

nginx.conf error_page for protected resource:

error_page 401 = https://sso.example.net/login?callback=$scheme://$host$request_uri;

a little patch to support few auth servers on the same domain

diff --git a/Dockerfile b/Dockerfile
index bb9e513..c667f27 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
-FROM alpine:3.17.0
+FROM alpine:3.17.2

-ARG VERSION="0.0.9"
+ARG VERSION="0.0.9-cookie-name-patch"

 LABEL build_version="nginx-auth-server version ${VERSION}"
 LABEL maintainer="burakkavak"
@@ -21,6 +21,7 @@ ENV TLS_CERT_KEY="/opt/nginx-auth-server/certs/server.key"

 ENV COOKIES_LIFETIME=7
 ENV COOKIES_SECURE="true"
+ENV COOKIES_NAME="Nginx-Auth-Server-Token"

 ENV LDAP_ENABLED="false"
 ENV LDAP_URL=""
diff --git a/config.ini b/config.ini
index b754163..289cc04 100644
--- a/config.ini
+++ b/config.ini
@@ -31,6 +31,9 @@ lifetime = 7
 # Refer to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie. Defaults to true.
 secure = true

+# Set cookie name attribute for cookie. Useful if you running more than one auth server on the same domain.
+name = "Nginx-Auth-Server-Token"
+
 [LDAP]
 # Enable/disable LDAP support. The application will prioritize local authentication data first. Default is false.
 enabled = false
diff --git a/docker-compose.yml b/docker-compose.yml
index abe6355..98a2355 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -11,6 +11,7 @@ services:
 #      - TLS_CERT_PATH=/opt/nginx-auth-server/certs/server.crt
 #      - TLS_CERT_KEY=/opt/nginx-auth-server/certs/server.key
 #      - COOKIES_LIFETIME=7
+#      - COOKIES_NAME="Nginx-Auth-Server-Token"
 #      - LDAP_ENABLED=false
 #      - LDAP_URL=
 #      - LDAP_ORGANIZATIONAL_UNIT=users
diff --git a/scripts/docker_run.sh b/scripts/docker_run.sh
index 878fb49..070f06c 100644
--- a/scripts/docker_run.sh
+++ b/scripts/docker_run.sh
@@ -18,6 +18,7 @@
   echo "[Cookies]"
   echo "lifetime = $COOKIES_LIFETIME"
   echo "secure = $COOKIES_SECURE"
+  echo "name = $COOKIES_NAME"

   echo "[LDAP]"
   echo "enabled = $LDAP_ENABLED"
diff --git a/src/config.go b/src/config.go
index 345f10a..a95d76a 100644
--- a/src/config.go
+++ b/src/config.go
@@ -26,8 +26,9 @@ type TLS struct {

 // Cookies :: [Cookies]-Section of .ini
 type Cookies struct {
-	Lifetime int  `ini:"lifetime"`
-	Secure   bool `ini:"secure"`
+	Lifetime int    `ini:"lifetime"`
+	Secure   bool   `ini:"secure"`
+	Name     string `ini:"name"`
 }

 // LDAP :: [LDAP]-Section of .ini
@@ -73,6 +74,7 @@ var (
 		Cookies: Cookies{
 			Lifetime: 7,
 			Secure:   true,
+			Name:     "Nginx-Auth-Server-Token",
 		},
 		LDAP: LDAP{
 			Enabled:            false,
@@ -162,6 +164,11 @@ func GetCookieSecure() bool {
 	return config.Cookies.Secure
 }

+func GetCookieName() string {
+	parse()
+	return config.Cookies.Name
+}
+
 func GetLDAPEnabled() bool {
 	parse()
 	return config.LDAP.Enabled
diff --git a/src/main.go b/src/main.go
index 0a3208e..ba207e3 100644
--- a/src/main.go
+++ b/src/main.go
@@ -210,7 +210,7 @@ func removeUser(username string) {
 // authenticate handles the /auth route. If a valid cookie is found in the request header, the
 // the response will be 200. If the cookie is invalid or expired, 401 is set as a response status.
 func authenticate(c *gin.Context) {
-	token, err := c.Cookie("Nginx-Auth-Server-Token")
+	token, err := c.Cookie(GetCookieName())

 	if err != nil {
 		c.AbortWithStatus(401)
@@ -232,7 +232,7 @@ func authenticate(c *gin.Context) {
 // will be redirected to the root page. If the user is not authenticated,
 // the login form template will be displayed.
 func login(c *gin.Context) {
-	token, err := c.Cookie("Nginx-Auth-Server-Token")
+	token, err := c.Cookie(GetCookieName())

 	if err == nil {
 		if _, err = VerifyCookie(token); err == nil {
@@ -258,7 +258,7 @@ func login(c *gin.Context) {
 // logout handles the /logout route. If a valid cookie is found in the request header, the
 // the response will be 200 and the cookie will be deleted.
 func logout(c *gin.Context) {
-	token, err := c.Cookie("Nginx-Auth-Server-Token")
+	token, err := c.Cookie(GetCookieName())
 	clientIp := GetClientIpFromContext(c)

 	if err != nil {
@@ -314,7 +314,7 @@ type RecaptchaResponse struct {
 // If the user has provided valid credentials in the login form, the response will contain a new cookie (200).
 // If the username, the password, the TOTP token or the reCAPTCHA token is invalid, the request is rejected.
 func processLoginForm(c *gin.Context) {
-	token, err := c.Cookie("Nginx-Auth-Server-Token")
+	token, err := c.Cookie(GetCookieName())
 	clientIp := GetClientIpFromContext(c)

 	if err == nil {
@@ -411,7 +411,7 @@ func createAndSetAuthCookie(c *gin.Context, username string) Cookie {
 	plainCookieValue := GeneratePassword(96, 25, 35)

 	cookie := Cookie{
-		Name:     "Nginx-Auth-Server-Token",
+		Name:     GetCookieName(),
 		Value:    GenerateHash(plainCookieValue),
 		Expires:  time.Now().AddDate(0, 0, GetCookieLifetime()),
 		Domain:   GetDomain(),
@@ -444,7 +444,7 @@ func createAndSetAuthCookie(c *gin.Context, username string) Cookie {
 // 200 and the username (formatted as JSON) is returned.
 // If the cookie in the request header is invalid, 401 Unauthorized is returned.
 func whoami(c *gin.Context) {
-	token, err := c.Cookie("Nginx-Auth-Server-Token")
+	token, err := c.Cookie(GetCookieName())

 	if err != nil {
 		c.AbortWithStatus(401)

cannot redirect to previous link

I deploy the docker in my enviroment, but seems after authentication, the page won't re-direct to the previous page. Is there any additional settings needed?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.