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