initial
This commit is contained in:
commit
cb9d1193c3
15 changed files with 394 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
data
|
21
build/nginx/Dockerfile
Normal file
21
build/nginx/Dockerfile
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
FROM alpine:3.19
|
||||||
|
|
||||||
|
# install packages
|
||||||
|
RUN apk add --no-cache nginx shadow tini
|
||||||
|
RUN rm -fr /var/cache/apk/*
|
||||||
|
|
||||||
|
# update nginx user
|
||||||
|
RUN groupmod --gid 1000 nginx
|
||||||
|
RUN usermod --uid 1000 nginx
|
||||||
|
|
||||||
|
# remove build packages
|
||||||
|
RUN apk del shadow
|
||||||
|
|
||||||
|
# make log syms
|
||||||
|
RUN ln -sf /dev/stdout /var/log/nginx/access.log && \
|
||||||
|
ln -sf /dev/stderr /var/log/nginx/error.log
|
||||||
|
|
||||||
|
# do the
|
||||||
|
USER nginx
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--"]
|
||||||
|
CMD ["/usr/sbin/nginx", "-c", "/etc/nginx/nginx.conf"]
|
16
build/php/Dockerfile
Normal file
16
build/php/Dockerfile
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
FROM php:fpm-alpine
|
||||||
|
|
||||||
|
# install packages
|
||||||
|
RUN apk add --no-cache runuser shadow openldap-dev
|
||||||
|
RUN rm -fr /var/cache/apk/*
|
||||||
|
|
||||||
|
# update php user
|
||||||
|
RUN groupmod --gid 1000 www-data
|
||||||
|
RUN usermod --uid 1000 www-data
|
||||||
|
|
||||||
|
# install php packages
|
||||||
|
RUN docker-php-ext-install ldap
|
||||||
|
|
||||||
|
# remove build packages
|
||||||
|
RUN apk del shadow
|
||||||
|
USER www-data
|
9
conf/ldap/ldap.env
Normal file
9
conf/ldap/ldap.env
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
|
||||||
|
LDAP_URL=
|
||||||
|
LDAP_BIND_DN=
|
||||||
|
LDAP_BIND_PASSWORD=
|
||||||
|
LDAP_BASE_DN=
|
||||||
|
LDAP_FILTER="(&)"
|
||||||
|
LDAP_UID="cn"
|
||||||
|
|
||||||
|
HTTP_HOST=auth.example.com
|
50
conf/nginx/nginx.conf
Normal file
50
conf/nginx/nginx.conf
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
worker_processes 4;
|
||||||
|
daemon off;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
sendfile on;
|
||||||
|
keepalive_timeout 70;
|
||||||
|
server_tokens off;
|
||||||
|
client_max_body_size 2m;
|
||||||
|
|
||||||
|
access_log /var/log/nginx/access.log;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080;
|
||||||
|
root /opt/website;
|
||||||
|
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_buffers 16 8k;
|
||||||
|
gzip_http_version 1.1;
|
||||||
|
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;
|
||||||
|
|
||||||
|
location /favicon.ico {
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
root /opt/website/public/icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /public {
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
add_header Content-Security-Policy "script-src 'none'; object-src 'none'; base-uri 'none'";
|
||||||
|
root /opt/website/web;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_pass php:9000;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
docker-compose.yml
Normal file
21
docker-compose.yml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
services:
|
||||||
|
|
||||||
|
web:
|
||||||
|
build: ./build/nginx
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '80:8080'
|
||||||
|
volumes:
|
||||||
|
- ./src:/opt/website:ro
|
||||||
|
- ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
depends_on:
|
||||||
|
- php
|
||||||
|
|
||||||
|
php:
|
||||||
|
build: ./build/php
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file:
|
||||||
|
- ./conf/ldap/ldap.env
|
||||||
|
volumes:
|
||||||
|
- ./src:/opt/website:ro
|
||||||
|
- ./data/session:/var/lib/php/session
|
BIN
src/public/bg.jpg
Normal file
BIN
src/public/bg.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 415 KiB |
70
src/public/main.css
Normal file
70
src/public/main.css
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--blue: rgb(0, 102, 204);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background: #898989;
|
||||||
|
background-image: url('./bg.jpg');
|
||||||
|
background-size: 100%;
|
||||||
|
background-position: 50% 50%;
|
||||||
|
color: #fff;
|
||||||
|
font-family: "Open Sans", Helvetica, Arial, sans-serif;
|
||||||
|
font-weight: 100;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin-top: 20vh;
|
||||||
|
height: fit-content;
|
||||||
|
width: 400px;
|
||||||
|
background: #fff;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .heading {
|
||||||
|
border-top: 5px solid var(--blue);
|
||||||
|
font-size: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .content {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main .content, form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
main label,
|
||||||
|
main #submit {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main input {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
outline: none;
|
||||||
|
padding: 7px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-bottom-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
main #submit {
|
||||||
|
background: var(--blue);
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
59
src/web/helpers/auth.php
Normal file
59
src/web/helpers/auth.php
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||||
|
|
||||||
|
$keys = array();
|
||||||
|
|
||||||
|
function load_key($key) {
|
||||||
|
$file = "/tmp/$key";
|
||||||
|
if (!file_exists($file))
|
||||||
|
return FALSE;
|
||||||
|
$content = explode("\n", file_get_contents($file));
|
||||||
|
return array(
|
||||||
|
'user' => $content[0],
|
||||||
|
'time' => $content[1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function store_key($key, $user) {
|
||||||
|
$file = "/tmp/$key";
|
||||||
|
$now = (string)time();
|
||||||
|
$content = "$user\n{$now}";
|
||||||
|
file_put_contents($file, $content, LOCK_EX);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_random($n)
|
||||||
|
{
|
||||||
|
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||||
|
$randomString = '';
|
||||||
|
|
||||||
|
for ($i = 0; $i < $n; $i++) {
|
||||||
|
$index = rand(0, strlen($characters) - 1);
|
||||||
|
$randomString .= $characters[$index];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $randomString;
|
||||||
|
}
|
||||||
|
|
||||||
|
function key_auth() {
|
||||||
|
if (!isset($_SESSION['auth'])) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
$key = $_SESSION['auth'];
|
||||||
|
$data = load_key($key);
|
||||||
|
if ($data === FALSE) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
$user = $data['user'];
|
||||||
|
$time = $data['time'];
|
||||||
|
$now = time();
|
||||||
|
if ($time > $now || $now - $time > 60 * 60 * 24) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
store_key($key, $user);
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
function key_new($user) {
|
||||||
|
$key = get_random(128);
|
||||||
|
store_key($key, $user);
|
||||||
|
$_SESSION['auth'] = $key;
|
||||||
|
}
|
41
src/web/helpers/ldap.php
Normal file
41
src/web/helpers/ldap.php
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||||
|
|
||||||
|
function ldap_auth($auth_username, $auth_password) {
|
||||||
|
$url = getenv("LDAP_URL");
|
||||||
|
$bind = getenv("LDAP_BIND_DN");
|
||||||
|
$password = getenv("LDAP_BIND_PASSWORD");
|
||||||
|
$bound = getenv("LDAP_BASE_DN");
|
||||||
|
$filter = getenv("LDAP_FILTER");
|
||||||
|
$uid = getenv("LDAP_UID");
|
||||||
|
|
||||||
|
$conn = @ldap_connect($url);
|
||||||
|
if (!$conn) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||||
|
|
||||||
|
$bind_conn = @ldap_bind($conn, $bind, $password);
|
||||||
|
if (!$bind_conn) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
$search = @ldap_search($conn, $bound, $filter);
|
||||||
|
|
||||||
|
$info = @ldap_get_entries($conn, $search);
|
||||||
|
$user = NULL;
|
||||||
|
for ($i=0; $i<$info['count']; $i++) {
|
||||||
|
$user = $info[$i];
|
||||||
|
if (!array_key_exists($uid, $user))
|
||||||
|
continue;
|
||||||
|
if ($user[$uid][0] == $auth_username)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user == NULL) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
$succ = @ldap_bind($conn, $user['dn'], $auth_password);
|
||||||
|
return !!$succ;
|
||||||
|
}
|
||||||
|
|
66
src/web/index.php
Normal file
66
src/web/index.php
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||||
|
|
||||||
|
ini_set('html_errors', '1');
|
||||||
|
|
||||||
|
$webroot = dirname(__FILE__);
|
||||||
|
$publicroot = realpath(dirname(__FILE__) . '/../public');
|
||||||
|
|
||||||
|
// load stuff
|
||||||
|
require($webroot . '/helpers/ldap.php');
|
||||||
|
require($webroot . '/helpers/auth.php');
|
||||||
|
|
||||||
|
// start session
|
||||||
|
session_set_cookie_params(
|
||||||
|
60 * 60 * 24, // lifetime (seconds),
|
||||||
|
'/', // path
|
||||||
|
NULL, // domain,
|
||||||
|
TRUE, // secure,
|
||||||
|
TRUE // http only
|
||||||
|
);
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
function page($file, $data = array()) {
|
||||||
|
extract($data);
|
||||||
|
$webroot = $GLOBALS['webroot'];
|
||||||
|
require($webroot . '/views/header.php');
|
||||||
|
require($webroot . "/views/$file.php");
|
||||||
|
require($webroot . '/views/footer.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
parse_str(file_get_contents('php://input'), $post);
|
||||||
|
$res = ldap_auth($post['username'], $post['password']);
|
||||||
|
$msg = '';
|
||||||
|
$title = '';
|
||||||
|
if ($res) {
|
||||||
|
$msg = 'Authenticated. You can now go back to your content';
|
||||||
|
$title = 'Success';
|
||||||
|
key_new($post['username']);
|
||||||
|
} else {
|
||||||
|
$msg = 'Invalid Credentials';
|
||||||
|
$title = 'Error';
|
||||||
|
}
|
||||||
|
page('message', array(
|
||||||
|
'title' => $title,
|
||||||
|
'msg' => $msg
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
if (($user = key_auth())) {
|
||||||
|
http_response_code(200);
|
||||||
|
header("X-Webauth-User: $user");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
$host = $_SERVER['HTTP_HOST'];
|
||||||
|
$env = getenv("HTTP_HOST");
|
||||||
|
if ($host != $env) {
|
||||||
|
// we are being forwarded authed
|
||||||
|
// redirect
|
||||||
|
http_response_code(301);
|
||||||
|
header("Location: https://$env");
|
||||||
|
} else {
|
||||||
|
page('login', array(
|
||||||
|
'title' => 'Login'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
4
src/web/views/footer.php
Normal file
4
src/web/views/footer.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
13
src/web/views/header.php
Normal file
13
src/web/views/header.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link href="//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700&subset=latin" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="/public/main.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main id="main" role="main">
|
||||||
|
<div class="heading">
|
||||||
|
<span><?=$title?></span>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
22
src/web/views/login.php
Normal file
22
src/web/views/login.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
|
<form method="post">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
autofocus="true"
|
||||||
|
>
|
||||||
|
<label fot="password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
role="button"
|
||||||
|
id="submit"
|
||||||
|
value="Sign In"
|
||||||
|
>
|
||||||
|
<form>
|
1
src/web/views/message.php
Normal file
1
src/web/views/message.php
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<center><?=$msg?></center>
|
Loading…
Reference in a new issue