Commit ad6c44cc authored by Geoff Simmons's avatar Geoff Simmons

Initial commit

parents
Pipeline #471 failed with stages
See LICENSE for details.
Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
# Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
# All rights reserved
#
# Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
all: build doc
# Requires gogitversion: https://github.com/slimhazard/gogitversion
http-faccess: main.go
go fmt
go generate
go build
build: http-faccess
# Requires goreadme: https://github.com/dmjones/goreadme
README.md: doc.go
goreadme README.md
doc: README.md
check: build
golint
go vet
test: check
clean:
go clean
rm -f main_version.go
rm -f http-faccess
install: all
go install
VERSION=`git describe --always 2>/dev/null || echo unknown`
container:
docker build -t http-faccess:${VERSION} -f Dockerfile .
# http-faccess
[![GoDoc](https://godoc.org/code.uplex.de/testing/http-faccess?status.svg)](https://godoc.org/code.uplex.de/testing/http-faccess)
http-faccess -- HTTP server that reports whether the server's process
owner can read files relative to a base directory.
### SYNOPSIS
.
```go
http-faccess [-address addr] [-base dir] [-access log] [-error log]
http-faccess -version
http-faccess -help
```
### DESCRIPTION
http-faccess is an HTTP server that reports, through response
status codes, whether the file or directory represented by the URL
path, interpreted as a path on the server's host relative to a
given base directory, is readable by the server's process
owner. http-faccess is meant to be used as a tool within trusted
networks, and should not be exposed to the public internet (see
SECURITY).
*base* is a directory on the host on which http-faccess runs (by
default, the current working directory when http-faccess was
invoked). The process owner is the effective user running
http-faccess. *base* MUST be a directory, and it MUST be readable
by the process owner when http-faccess is invoked.
The URL path of a request, without the leading '/', is interpeted
as a path relative to the base directory. The only permitted
request methods are GET and HEAD. Response bodies are always empty;
the information in the response is defined solely by its status
code. Responses from http-faccess are not cacheable.
The response status codes are:
```go
204 No Content: the file or directory represented
by the URL path is readable
301 Moved Permanently: redirect to a path without repeated
slashes (see below)
403 Forbidden: the file or directory exists, but is
not readable
404 Not Found: the file or directory does not exist
405 Method Not Allowed: the request method was neither of GET
or HEAD
500 Internal Server Error: an error was encountered while
attempting to determine readability
```
If the URL path ends with '/', then it denotes a directory,
otherwise a file. If the URL in a request denotes a file (there is
no trailing '/'), but there is a directory at the corresponding
path on the host, then the response is 404 Not Found. Similarly,
the response is 404 if the request denotes a directory but there is
a file at the corresponding path.
Requests for the root document -- in which the path consists only
of '/', or when the request URI has no trailing slash, such as
<a href="http://example.com">http://example.com</a> -- always result in status 204. The rationale is
that these requests designate the base directory, which had to be
readable when http-faccess started.
If there is a symbolic link at the path corresponding to the URL
path, the link is followed to its target, possibly over a chain of
links, and results are reported for the target. Dangling links
result in a 500 error response.
If the URL path contains sequences of slashes, such as
'//foo/bar//baz', http-faccess returns a 301 redirect response, in
which the Location contains the path without the duplicate slashes.
If any component of the URL path is a single dot ('/./'), then it
is transparently removed from the path. '/../' has the usual
meaning of designating the parent directory, except that it goes no
higher than the document root, so it is not possible to explore the
filesystem outside of the base directory. Paths containing '/./' or
'/../' are modified transparently before the path is evaluated for
readability; there is no redirect response in such cases, unless
there are also repeated slashes.
For example:
```go
URL path: is evaluated as:
/./foo /foo
/foo/./bar /foo/bar
/.. /
/../foo /foo
/../dir/ /dir/
/dir/../foo /foo
/dir1/dir2/../foo /dir1/foo
```
### EXAMPLES
Suppose that http-faccess is invoked as user *smith* with:
```go
$ http-faccess -address :7357 -base /foo/
```
The server then listens at port 7357, and reports the readability
of files and directories in /foo/ for user *smith*.
Suppose further that /foo/ has these contents, where *jones* is
another user, and *smith* belongs to the group *smithgrp*, but not to
the group *jonesgrp*:
```go
$ ls -l /foo/
-rw-r--r-- smith smithgrp bar.txt
-rw-r----- jones jonesgrp baz.html
-rw-r----- jones smithgrp quux.jpg
drwxr-xr-x smith smithgrp subdir
```
Then http-faccess returns the following status codes for requests
with these URL paths:
```go
URL path: /
Status: 204
The root path is always readable.
URL path: /bar.txt
Status: 204
/foo/bar.txt is readable for smith.
URL path: /baz.html
Status: 403
/foo/baz.html exists, but is not readable for smith.
URL path: /quux.jpg
Status: 204
/foo/quux.jpg is readable for smith.
URL path: /subdir
Status: 404
/foo/subdir/ is a directory, but the URL path designates a file.
URL path: /subdir/
Status: 204
Directory /foo/subdir/ is readable for smith.
URL path: /../
Status: 204
Evaluated as the root path.
URL path: /subdir/../bar.txt
Status: 204
Interpreted as /bar.txt
URL path: /././quux.jpg
Status: 204
Interpreted as /quux.jpg
URL path: //bar/baz//quux
Status: 301
Location: /bar/baz/quux
Redirect to the path without repeated slashes.
```
### OPTIONS
http-faccess may be invoked with these command-line flags:
```go
-base dir
```
Evaluate URL paths in requests as paths relative to the directory
*dir*. *dir* MUST be a directory, and it MUST be readable by the
process owner at invocation. http-faccess always evaluates paths
relative to *dir* as it was at invocation time, even if its name or
permissions change after the server was started. By default, *dir*
is the current working directory at invocation.
```go
-address addr
```
Listen for requests at *addr*. *addr* can have one of the forms:
```go
[ip-address]:port
unix@/path/to/unix.sock
```
The first form specifies an IP address. If only :port is specified,
http-faccess listens at that port on all IP network interfaces. The
second form specifies a Unix domain socket at the path after
"unix@". The socket file is created when the server starts; if any
file already exists at that path, then http-faccess fails to
start. By default, http-faccess listens at port :7357.
```go
-access logfile
```
Write the server access log (in Common Log Format) to *logfile*, or
to standard output if *logfile* is "-". To suppress logging, set
*logfile* to /dev/null. By default, http-faccess logs to stdout.
```go
-error logfile
```
Write error logs, which detail the errors that cause 500 responses,
to *logfile*, or to stdout if *logfile* is "-". Suppress error logs
by setting *logfile* to /dev/null. By default, http-faccess writes
error logs to stderr.
```go
-version
```
Print the version and exit.
```go
-help
```
Print a usage message and exit.
### LIMITATIONS
http-faccess is platform-specific -- it only runs on Unix systems with
a POSIX-compliant implementation of faccessat(2).
There is always a TOCTOU race between the server's reports about
the readability of a file or directory and subsequent attempts by
the process owner to read that file or directory. The file may be
changed or deleted at any time after http-faccess reports a result.
Requests that lead to redirect responses (to paths without repeated
slashes) are not logged in the access log.
### SECURITY
Depending on the contents of the base directory, http-faccess can
expose sensitive information about the host's file system, which
could be exploited by an attacker. http-faccess does not reveal the
contents of any file, does not allow directory listings, does not
report the causes of 500 errors in the response (these are only
reported in the server's error log), and does not permit the use of
'/../' to navigate outside the base directory. But an attacker may
draw inferences by testing hypotheses about paths on the host, by
discovering what is readable or not readable and what exists or
does not exist, or by determining that errors can be caused.
It is **strongly** advised:
- don't expose http-faccess on the public internet.
- don't run http-faccess as root, as an administrative user, as any
user corresponding to a human user, or any highly privileged user.
- don't set the base directory so that sensitive information on the
host can be explored, such as the root directory ('/'), /etc, /usr
or anyone's home directory.
For best results, limit the use of http-faccess to private
networks, run it as a user with limited privileges, and set the
base directory so that a narrowly restricted set of paths are
accessible. If http-faccess only needs to be accessed by other
components on the same host, consider using a Unix domain socket as
the listener, so that it is not exposed on the network at all.
Be aware that http-faccess follows symbolic links, which may make
it possible to explore the file system outside of the base
directory. Consider eliminating symbolic links altogether from
paths that can be reached from the base directory. If links are
used, ensure that they are limited in scope, just as you would for
the base directory.
### INSTALLATION
Building the binary requires an installation of the gogitversion
tool. Re-building README.md requires goreadme. See the links in SEE
ALSO below.
To install the binary:
```go
make install
```
See the Makefile for additional targets, useful for development.
### SEE ALSO
* source code repository: <a href="https://code.uplex.de/testing/http-faccess">https://code.uplex.de/testing/http-faccess</a>
* gogitversion: <a href="https://github.com/slimhazard/gogitversion">https://github.com/slimhazard/gogitversion</a>
* goreadme: <a href="https://github.com/dmjones/goreadme">https://github.com/dmjones/goreadme</a>
### COPYRIGHT
Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung.
All rights reserved
Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
See LICENSE
<sub>*generated with [goreadme](https://github.com/dmjones/goreadme)*</sub>
\ No newline at end of file
/*
* Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
// http-faccess -- HTTP server that reports whether the server's process
// owner can read files relative to a base directory.
//
// SYNOPSIS
//
// .
// http-faccess [-address addr] [-base dir] [-access log] [-error log]
//
// http-faccess -version
//
// http-faccess -help
//
// DESCRIPTION
//
// http-faccess is an HTTP server that reports, through response
// status codes, whether the file or directory represented by the URL
// path, interpreted as a path on the server's host relative to a
// given base directory, is readable by the server's process
// owner. http-faccess is meant to be used as a tool within trusted
// networks, and should not be exposed to the public internet (see
// SECURITY).
//
// *base* is a directory on the host on which http-faccess runs (by
// default, the current working directory when http-faccess was
// invoked). The process owner is the effective user running
// http-faccess. *base* MUST be a directory, and it MUST be readable
// by the process owner when http-faccess is invoked.
//
// The URL path of a request, without the leading '/', is interpeted
// as a path relative to the base directory. The only permitted
// request methods are GET and HEAD. Response bodies are always empty;
// the information in the response is defined solely by its status
// code. Responses from http-faccess are not cacheable.
//
// The response status codes are:
//
// 204 No Content: the file or directory represented
// by the URL path is readable
//
// 301 Moved Permanently: redirect to a path without repeated
// slashes (see below)
//
// 403 Forbidden: the file or directory exists, but is
// not readable
//
// 404 Not Found: the file or directory does not exist
//
// 405 Method Not Allowed: the request method was neither of GET
// or HEAD
//
// 500 Internal Server Error: an error was encountered while
// attempting to determine readability
//
// If the URL path ends with '/', then it denotes a directory,
// otherwise a file. If the URL in a request denotes a file (there is
// no trailing '/'), but there is a directory at the corresponding
// path on the host, then the response is 404 Not Found. Similarly,
// the response is 404 if the request denotes a directory but there is
// a file at the corresponding path.
//
// Requests for the root document -- in which the path consists only
// of '/', or when the request URI has no trailing slash, such as
// http://example.com -- always result in status 204. The rationale is
// that these requests designate the base directory, which had to be
// readable when http-faccess started.
//
// If there is a symbolic link at the path corresponding to the URL
// path, the link is followed to its target, possibly over a chain of
// links, and results are reported for the target. Dangling links
// result in a 500 error response.
//
// If the URL path contains sequences of slashes, such as
// '//foo/bar//baz', http-faccess returns a 301 redirect response, in
// which the Location contains the path without the duplicate slashes.
//
// If any component of the URL path is a single dot ('/./'), then it
// is transparently removed from the path. '/../' has the usual
// meaning of designating the parent directory, except that it goes no
// higher than the document root, so it is not possible to explore the
// filesystem outside of the base directory. Paths containing '/./' or
// '/../' are modified transparently before the path is evaluated for
// readability; there is no redirect response in such cases, unless
// there are also repeated slashes.
//
// For example:
//
// URL path: is evaluated as:
// /./foo /foo
// /foo/./bar /foo/bar
// /.. /
// /../foo /foo
// /../dir/ /dir/
// /dir/../foo /foo
// /dir1/dir2/../foo /dir1/foo
//
// EXAMPLES
//
// Suppose that http-faccess is invoked as user *smith* with:
//
// $ http-faccess -address :7357 -base /foo/
//
// The server then listens at port 7357, and reports the readability
// of files and directories in /foo/ for user *smith*.
//
// Suppose further that /foo/ has these contents, where *jones* is
// another user, and *smith* belongs to the group *smithgrp*, but not to
// the group *jonesgrp*:
//
// $ ls -l /foo/
// -rw-r--r-- smith smithgrp bar.txt
// -rw-r----- jones jonesgrp baz.html
// -rw-r----- jones smithgrp quux.jpg
// drwxr-xr-x smith smithgrp subdir
//
// Then http-faccess returns the following status codes for requests
// with these URL paths:
//
// URL path: /
// Status: 204
// The root path is always readable.
//
// URL path: /bar.txt
// Status: 204
// /foo/bar.txt is readable for smith.
//
// URL path: /baz.html
// Status: 403
// /foo/baz.html exists, but is not readable for smith.
//
// URL path: /quux.jpg
// Status: 204
// /foo/quux.jpg is readable for smith.
//
// URL path: /subdir
// Status: 404
// /foo/subdir/ is a directory, but the URL path designates a file.
//
// URL path: /subdir/
// Status: 204
// Directory /foo/subdir/ is readable for smith.
//
// URL path: /../
// Status: 204
// Evaluated as the root path.
//
// URL path: /subdir/../bar.txt
// Status: 204
// Interpreted as /bar.txt
//
// URL path: /././quux.jpg
// Status: 204
// Interpreted as /quux.jpg
//
// URL path: //bar/baz//quux
// Status: 301
// Location: /bar/baz/quux
// Redirect to the path without repeated slashes.
//
// OPTIONS
//
// http-faccess may be invoked with these command-line flags:
//
// -base dir
//
// Evaluate URL paths in requests as paths relative to the directory
// *dir*. *dir* MUST be a directory, and it MUST be readable by the
// process owner at invocation. http-faccess always evaluates paths
// relative to *dir* as it was at invocation time, even if its name or
// permissions change after the server was started. By default, *dir*
// is the current working directory at invocation.
//
// -address addr
//
// Listen for requests at *addr*. *addr* can have one of the forms:
//
// [ip-address]:port
// unix@/path/to/unix.sock
//
// The first form specifies an IP address. If only :port is specified,
// http-faccess listens at that port on all IP network interfaces. The
// second form specifies a Unix domain socket at the path after
// "unix@". The socket file is created when the server starts; if any
// file already exists at that path, then http-faccess fails to
// start. By default, http-faccess listens at port :7357.
//
// -access logfile
//
// Write the server access log (in Common Log Format) to *logfile*, or
// to standard output if *logfile* is "-". To suppress logging, set
// *logfile* to /dev/null. By default, http-faccess logs to stdout.
//
// -error logfile
//
// Write error logs, which detail the errors that cause 500 responses,
// to *logfile*, or to stdout if *logfile* is "-". Suppress error logs
// by setting *logfile* to /dev/null. By default, http-faccess writes
// error logs to stderr.
//
// -version
//
// Print the version and exit.
//
// -help
//
// Print a usage message and exit.
//
// LIMITATIONS
//
// http-faccess is platform-specific -- it only runs on Unix systems with
// a POSIX-compliant implementation of faccessat(2).
//
// There is always a TOCTOU race between the server's reports about
// the readability of a file or directory and subsequent attempts by
// the process owner to read that file or directory. The file may be
// changed or deleted at any time after http-faccess reports a result.
//
// Requests that lead to redirect responses (to paths without repeated
// slashes) are not logged in the access log.
//
// SECURITY
//
// Depending on the contents of the base directory, http-faccess can
// expose sensitive information about the host's file system, which
// could be exploited by an attacker. http-faccess does not reveal the
// contents of any file, does not allow directory listings, does not
// report the causes of 500 errors in the response (these are only
// reported in the server's error log), and does not permit the use of
// '/../' to navigate outside the base directory. But an attacker may
// draw inferences by testing hypotheses about paths on the host, by
// discovering what is readable or not readable and what exists or
// does not exist, or by determining that errors can be caused.
//
// It is **strongly** advised:
//
// - don't expose http-faccess on the public internet.
//
// - don't run http-faccess as root, as an administrative user, as any
// user corresponding to a human user, or any highly privileged user.
//
// - don't set the base directory so that sensitive information on the
// host can be explored, such as the root directory ('/'), /etc, /usr
// or anyone's home directory.
//
// For best results, limit the use of http-faccess to private
// networks, run it as a user with limited privileges, and set the
// base directory so that a narrowly restricted set of paths are
// accessible. If http-faccess only needs to be accessed by other
// components on the same host, consider using a Unix domain socket as
// the listener, so that it is not exposed on the network at all.
//
// Be aware that http-faccess follows symbolic links, which may make
// it possible to explore the file system outside of the base
// directory. Consider eliminating symbolic links altogether from
// paths that can be reached from the base directory. If links are
// used, ensure that they are limited in scope, just as you would for
// the base directory.
//
// INSTALLATION
//
// Building the binary requires an installation of the gogitversion
// tool. Re-building README.md requires goreadme. See the links in SEE
// ALSO below.
//
// To install the binary:
//
// make install
//
// See the Makefile for additional targets, useful for development.
//
// SEE ALSO
//
// * source code repository: https://code.uplex.de/testing/http-faccess
//
// * gogitversion: https://github.com/slimhazard/gogitversion
//
// * goreadme: https://github.com/dmjones/goreadme
//
// COPYRIGHT
//
// Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung.
// All rights reserved
//
// Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
//
// See LICENSE
package main
module code.uplex.de/testing/http-faccess
require golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
/*
* Copyright (c) 2020 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
package main
//go:generate gogitversion -p main
import (
"flag"
"fmt"
"log"
"net"
"net/http"
"os"
"strings"
"syscall"
"time"
"golang.org/x/sys/unix"
)
var (
dirfd = unix.AT_FDCWD
logger, errLogger *log.Logger
address = flag.String("address", ":7357",
"listen address, [addr]:port for IP,\n"+
"unix@/path for unix socket")
base = flag.String("base", "", "base directory (default cwd)")
access = flag.String("access", "", "access log file "+
"(default stdout)")
errLog = flag.String("error", "", "error log file "+
"(default stderr, - for stdout)")
gid = flag.Int("gid", -1, "group id of the unix socket listener, -1 to"+
" leave unchanged,\nignored for IP listeners")
mode = flag.Int("mode", -1, "permissions of the unix socket listener, "+
"-1 to leave unchanged,\nignored for IP listeners")
versionF = flag.Bool("version", false, "show version and exit")
allowed = map[string]struct{}{
http.MethodGet: struct{}{},
http.MethodHead: struct{}{},
}
)
// Date format for Common Log Format
const common = "02/Jan/2006:15:04:05 -0700"
func reqLog(req *http.Request, now time.Time, status int) {
clientIP := req.RemoteAddr
if colon := strings.LastIndex(clientIP, ":"); colon != -1 {
clientIP = clientIP[:colon]
}
logger.Printf("%s - - [%s] \"%s %s %s\" %d 0\n", clientIP,
now.Format(common), req.Method, req.RequestURI, req.Proto,
status)
}
func handle(resp http.ResponseWriter, req *http.Request) {
now := time.Now()
resp.Header().Set("Content-Type", "text/plain")
resp.Header().Set("Content-Length", "0")
resp.Header().Set("Cache-Control", "no-store")
status := http.StatusTeapot
if _, ok := allowed[req.Method]; !ok {
status = http.StatusMethodNotAllowed
} else if path := req.URL.Path[1:]; path == "" {
status = http.StatusNoContent
} else {
switch err := unix.Faccessat(dirfd, path, unix.R_OK,
unix.AT_EACCESS); err {
case nil:
status = http.StatusNoContent
case syscall.ENOENT, syscall.ENOTDIR:
status = http.StatusNotFound
case syscall.EACCES:
status = http.StatusForbidden
default:
status = http.StatusInternalServerError
errLogger.Printf("%s \"%s %s %s\": %s (%d)\n",
req.RemoteAddr, req.Method,
req.RequestURI, req.Proto, err,
int(err.(syscall.Errno)))
}
}
defer reqLog(req, now, status)
resp.WriteHeader(status)
}
func main() {
var err error
flag.Parse()
if *versionF {
fmt.Println("http-faccess version", version)
os.Exit(0)
}
out := os.Stdout
if *access != "" && *access != "-" {
if out, err = os.OpenFile(*access,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err != nil {
log.Fatalf(*access, ": ", err)
}
}
logger = log.New(out, "", log.LstdFlags)
logger.Println("starting http-faccess version", version)
logger.SetPrefix("[" + os.Args[0] + "] ")
out = os.Stderr
if *errLog != "" {
if *errLog != "-" {
out = os.Stdout
} else if out, err = os.OpenFile(*errLog,
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644); err != nil {
logger.Fatal(*errLog, ": ", err)
}
}
errLogger = log.New(out, "", log.Ldate|log.Lmicroseconds)
if *base != "" {
if info, err := os.Stat(*base); err != nil {
errLogger.Fatal(*base, ": ", err)
} else if !info.IsDir() {
errLogger.Fatal(*base, ": not a directory")
}
if dirfd, err = unix.Open(*base, os.O_RDONLY, 0); err != nil {
errLogger.Fatal(*base, ": ", err)
}
defer unix.Close(dirfd)
}
if *base == "" {
if cwd, err := os.Getwd(); err != nil {
// Not fatal because this is for logging, and
// AT_FDCWD might work.
errLogger.Println("Cannot get cwd:", err)
} else {
logger.Println("base directory:", cwd)
}
} else {
logger.Println("base directory:", *base)
}
mux := http.NewServeMux()
mux.HandleFunc("/", handle)
server := http.Server{Handler: mux}
network := "tcp"
if strings.HasPrefix(*address, "unix@") {
*address = (*address)[5:]
if _, err = os.Stat(*address); err == nil {
logger.Println("deleting", *address)
if err = os.Remove(*address); err != nil {
errLogger.Fatal("Cannot delete ", *address,
": ", err)
}
} else if !os.IsNotExist(err) {
errLogger.Fatal(*address, ": ", err)
}
network = "unix"
}
lsnr, err := net.Listen(network, *address)
if err != nil {
errLogger.Fatal(*address, ": ", err)
}
if network == "unix" {
if *gid != -1 {
if err = os.Chown(*address, -1, *gid); err != nil {
errLogger.Fatalf("Cannot set gid %d for %s: %v",
*gid, *address, err)
}
}
if *mode != -1 {
if err = os.Chmod(
*address, os.FileMode(*mode)); err != nil {
errLogger.Fatalf("Cannot set mode %0o for %s: "+
"%v", *mode, *address, err)
}
}
}
logger.Println("listening at:", *address)
logger.SetFlags(0)
logger.Fatal(server.Serve(lsnr))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment