Compare commits

..

2 commits
v1.2.0 ... main

Author SHA1 Message Date
Gustavo Maronato
4f05b73bec Update README.md
Signed-off-by: Gustavo Maronato <maronato@noreply@maronato.dev>
2024-01-29 19:59:17 -03:00
Gustavo Maronato
8673f0db42 Add missing paths to dockerfile 2024-01-25 02:38:03 -03:00
2 changed files with 288 additions and 272 deletions

View file

@ -1,52 +1,54 @@
# Load golang image # Load golang image
FROM golang:1.21-alpine as builder FROM golang:1.21-alpine as builder
RUN apk add make RUN apk add make
ARG VERSION=undefined ARG VERSION=undefined
WORKDIR /go/src/app WORKDIR /go/src/app
# Set our build environment # Set our build environment
ENV GOCACHE=/tmp/.go-build-cache ENV GOCACHE=/tmp/.go-build-cache
# This variable communicates to the service that it's running inside # This variable communicates to the service that it's running inside
# a docker container. # a docker container.
ENV ENV_DOCKER=true ENV ENV_DOCKER=true
# Copy dockerignore files # Copy dockerignore files
COPY .dockerignore ./ COPY .dockerignore ./
# Install go deps using the cache # Install go deps using the cache
COPY go.mod go.sum ./ COPY go.mod go.sum ./
RUN --mount=type=cache,target=/tmp/.go-build-cache \ RUN --mount=type=cache,target=/tmp/.go-build-cache \
go mod download -x go mod download -x
COPY Makefile ./ COPY Makefile ./
# Copy source files # Copy source files
COPY main.go ./ COPY main.go ./
COPY cmd cmd COPY cmd cmd
COPY internal internal COPY internal internal
COPY webfingers webfingers
# Build it COPY handler handler
RUN --mount=type=cache,target=/tmp/.go-build-cache \
make build VERSION=$VERSION # Build it
RUN --mount=type=cache,target=/tmp/.go-build-cache \
# Now create a new image with just the binary make build VERSION=$VERSION
FROM gcr.io/distroless/static-debian11:nonroot
# Now create a new image with just the binary
WORKDIR /app FROM gcr.io/distroless/static-debian11:nonroot
COPY urns.yml /app/urns.yml WORKDIR /app
# Set our runtime environment COPY urns.yml /app/urns.yml
ENV ENV_DOCKER=true
# Set our runtime environment
COPY --from=builder /go/src/app/finger /usr/local/bin/finger ENV ENV_DOCKER=true
HEALTHCHECK CMD [ "finger", "healthcheck" ] COPY --from=builder /go/src/app/finger /usr/local/bin/finger
EXPOSE 8080 HEALTHCHECK CMD [ "finger", "healthcheck" ]
ENTRYPOINT [ "finger" ] EXPOSE 8080
CMD [ "serve" ]
ENTRYPOINT [ "finger" ]
CMD [ "serve" ]

454
README.md
View file

@ -1,220 +1,234 @@
# Finger # Finger
Webfinger handler / standalone server written in Go. Webfinger handler / standalone server written in Go.
## Features ## Features
- 🍰 Easy YAML configuration - 🍰 Easy YAML configuration
- 🪶 Single 8MB binary / 0% idle CPU / 4MB idle RAM - 🪶 Single 8MB binary / 0% idle CPU / 4MB idle RAM
- ⚡️ Sub millisecond responses at 10,000 request per second - ⚡️ Sub millisecond responses at 10,000 request per second
- 🐳 10MB Docker image - 🐳 10MB Docker image
## In your existing server ## In your existing server
To use Finger in your existing server, download the package as a dependency: To use Finger in your existing server, download the package as a dependency:
```bash ```bash
go get git.maronato.dev/maronato/finger@latest go get git.maronato.dev/maronato/finger@latest
``` ```
Then, use it as a regular `http.Handler`: Then, use it as a regular `http.Handler`:
```go ```go
package main package main
import ( import (
"log" "log"
"net/http" "net/http"
"git.maronato.dev/maronato/finger/handler" "git.maronato.dev/maronato/finger/handler"
"git.maronato.dev/maronato/finger/webfingers" "git.maronato.dev/maronato/finger/webfingers"
) )
func main() { func main() {
// Create the webfingers map that will be served by the handler // Create the webfingers map that will be served by the handler
fingers, err := webfingers.NewWebFingers( fingers, err := webfingers.NewWebFingers(
// Pass a map of your resources (Subject key followed by it's properties and links) // Pass a map of your resources (Subject key followed by it's properties and links)
// the syntax is the same as the fingers.yml file (see below) // the syntax is the same as the fingers.yml file (see below)
webfingers.Resources{ webfingers.Resources{
"user@example.com": { "user@example.com": {
"name": "Example User", "name": "Example User",
}, },
}, },
// Optionally, pass a map of URN aliases (see urns.yml for more) // Optionally, pass a map of URN aliases (see urns.yml for more)
// If nil is provided, no aliases will be used // If nil is provided, no aliases will be used
webfingers.URNAliases{ webfingers.URNAliases{
"name": "http://schema.org/name", "name": "http://schema.org/name",
}, },
) )
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
mux := http.NewServeMux() mux := http.NewServeMux()
// Then use the handler as a regular http.Handler // Then use the handler as a regular http.Handler
mux.Handle("/.well-known/webfinger", handler.WebfingerHandler(fingers)) mux.Handle("/.well-known/webfinger", handler.WebfingerHandler(fingers))
log.Fatal(http.ListenAndServe("localhost:8080", mux)) log.Fatal(http.ListenAndServe("localhost:8080", mux))
} }
``` ```
## As a standalone server ## As a standalone server
If you don't have a server, Finger can also serve itself. You can install it via `go install` or use the Docker image. If you don't have a server, Finger can also serve itself. You can install it via `go install` or use the Docker image.
Via `go install`: Via `go install`:
```bash ```bash
go install git.maronato.dev/maronato/finger@latest go install git.maronato.dev/maronato/finger@latest
``` ```
Via Docker: Via Docker:
```bash ```bash
docker run --name finger / docker run \
-p 8080:8080 / --name finger \
git.maronato.dev/maronato/finger -p 8080:8080 \
``` -v ${PWD}/fingers.yml:/app/fingers.yml \
git.maronato.dev/maronato/finger
## Usage ```
If you installed it using `go install`, run ## Usage
```bash
finger serve If you installed it using `go install`, run
``` ```bash
To start the server on port `8080`. Your resources will be queryable via `locahost:8080/.well-known/webfinger?resource=<your-resource>` finger serve
```
If you're using Docker, the use the same command in the install section. To start the server on port `8080`. Your resources will be queryable via `locahost:8080/.well-known/webfinger?resource=<your-resource>`
By default, no resources will be exposed. You can create resources via a `fingers.yml` file. It should contain a collection of resources as keys and their attributes as their objects. If you're using Docker, the use the same command in the install section.
Some default URN aliases are provided via the built-in mapping ([`urns.yml`](./urns.yml)). You can replace that with your own or use URNs directly in the `fingers.yml` file. By default, no resources will be exposed. You can create resources via a `fingers.yml` file. It should contain a collection of resources as keys and their attributes as their objects.
Here's an example: Some default URN aliases are provided via the built-in mapping ([`urns.yml`](./urns.yml)). You can replace that with your own or use URNs directly in the `fingers.yml` file.
```yaml
# fingers.yml Here's an example:
```yaml
# Resources go in the root of the file. Email address will have the acct: # fingers.yml
# prefix added automatically.
alice@example.com: # Resources go in the root of the file. Email address will have the acct:
# "avatar" is an alias of "http://webfinger.net/rel/avatar" # prefix added automatically.
# (see urns.yml for more) alice@example.com:
avatar: "https://example.com/alice-pic" # "avatar" is an alias of "http://webfinger.net/rel/avatar"
# (see urns.yml for more)
# If the value is a URI, it'll be exposed as a webfinger link avatar: "https://example.com/alice-pic"
openid: "https://sso.example.com/"
# If the value is a URI, it'll be exposed as a webfinger link
# If the value of the attribute is not a URI, it will be exposed as a openid: "https://sso.example.com/"
# webfinger property
name: "Alice Doe" # If the value of the attribute is not a URI, it will be exposed as a
# webfinger property
# You can also specify URN's directly instead of the aliases name: "Alice Doe"
http://webfinger.net/rel/profile-page: "https://example.com/user/alice"
# You can also specify URN's directly instead of the aliases
bob@example.com: http://webfinger.net/rel/profile-page: "https://example.com/user/alice"
name: Bob Foo
openid: "https://sso.example.com/" bob@example.com:
name: Bob Foo
# Resources can also be URIs openid: "https://sso.example.com/"
https://example.com/user/charlie:
name: Charlie Baz # Resources can also be URIs
profile: https://example.com/user/charlie https://example.com/user/charlie:
``` name: Charlie Baz
profile: https://example.com/user/charlie
### Example queries ```
<details>
<summary><b>Query Alice</b><pre>GET http://localhost:8080/.well-known/webfinger?resource=acct:alice@example.com</pre></summary> ### Example queries
<details>
```json <summary><b>Query Alice</b><pre>GET http://localhost:8080/.well-known/webfinger?resource=acct:alice@example.com</pre></summary>
{
"subject": "acct:alice@example.com", ```json
"links": [ {
{ "subject": "acct:alice@example.com",
"rel": "avatar", "links": [
"href": "https://example.com/alice-pic" {
}, "rel": "avatar",
{ "href": "https://example.com/alice-pic"
"rel": "openid", },
"href": "https://sso.example.com/" {
}, "rel": "openid",
{ "href": "https://sso.example.com/"
"rel": "http://webfinger.net/rel/profile-page", },
"href": "https://example.com/user/alice" {
} "rel": "http://webfinger.net/rel/profile-page",
], "href": "https://example.com/user/alice"
"properties": { }
"name": "Alice Doe" ],
} "properties": {
} "name": "Alice Doe"
``` }
</details> }
```
</details>
<details>
<summary><b>Query Bob</b><pre>GET http://localhost:8080/.well-known/webfinger?resource=acct:bob@example.com</pre></summary>
<details>
```json <summary><b>Query Bob</b><pre>GET http://localhost:8080/.well-known/webfinger?resource=acct:bob@example.com</pre></summary>
{
"subject": "acct:bob@example.com", ```json
"links": [ {
{ "subject": "acct:bob@example.com",
"rel": "http://openid.net/specs/connect/1.0/issuer", "links": [
"href": "https://sso.example.com/" {
} "rel": "http://openid.net/specs/connect/1.0/issuer",
], "href": "https://sso.example.com/"
"properties": { }
"http://schema.org/name": "Bob Foo" ],
} "properties": {
} "http://schema.org/name": "Bob Foo"
``` }
</details> }
```
</details>
<details>
<summary><b>Query Charlie</b><pre>GET http://localhost:8080/.well-known/webfinger?resource=https://example.com/user/charlie</pre></summary>
<details>
```JSON <summary><b>Query Charlie</b><pre>GET http://localhost:8080/.well-known/webfinger?resource=https://example.com/user/charlie</pre></summary>
{
"subject": "https://example.com/user/charlie", ```JSON
"links": [ {
{ "subject": "https://example.com/user/charlie",
"rel": "http://webfinger.net/rel/profile-page", "links": [
"href": "https://example.com/user/charlie" {
} "rel": "http://webfinger.net/rel/profile-page",
], "href": "https://example.com/user/charlie"
"properties": { }
"http://schema.org/name": "Charlie Baz" ],
} "properties": {
} "http://schema.org/name": "Charlie Baz"
``` }
</details> }
```
## Commands </details>
Finger exposes two commands: `serve` and `healthcheck`. `serve` is the default command and starts the server. `healthcheck` is used by the Docker healthcheck to check if the server is up. ## Commands
## Configs Finger exposes two commands: `serve` and `healthcheck`. `serve` is the default command and starts the server. `healthcheck` is used by the Docker healthcheck to check if the server is up.
Here are the config options available. You can change them via command line flags or environment variables:
## Configs
| CLI flag | Env variable | Default | Description | Here are the config options available. You can change them via command line flags or environment variables:
| ------------------- | ---------------- | -------------------------------------- | -------------------------------------- |
| `-p, --port` | `WF_PORT` | `8080` | Port where the server listens to | | CLI flag | Env variable | Default | Description |
| `-h, --host` | `WF_HOST` | `localhost` (`0.0.0.0` when in Docker) | Host where the server listens to | | ------------------- | ---------------- | -------------------------------------- | -------------------------------------- |
| `-f, --finger-file` | `WF_FINGER_FILE` | `fingers.yml` | Path to the webfingers definition file | | `-p, --port` | `WF_PORT` | `8080` | Port where the server listens to |
| `-u, --urn-file` | `WF_URN_FILE` | `urns.yml` | Path to the URNs alias file | | `-h, --host` | `WF_HOST` | `localhost` (`0.0.0.0` when in Docker) | Host where the server listens to |
| `-d, --debug` | `WF_DEBUG` | `false` | Enable debug logging | | `-f, --finger-file` | `WF_FINGER_FILE` | `fingers.yml` | Path to the webfingers definition file |
| `-u, --urn-file` | `WF_URN_FILE` | `urns.yml` | Path to the URNs alias file |
## Development | `-d, --debug` | `WF_DEBUG` | `false` | Enable debug logging |
You need to have [Go](https://golang.org/) installed to build the project. ### Docker config
If you're using the Docker image, you can mount your `fingers.yml` file to `/app/fingers.yml` and the `urns.yml` to `/app/urns.yml`.
Clone the repo and run `make build` to build the binary. You can then run `./finger serve` to start the server.
To run the docker image with flags or a different command, specify the command followed by the flags:
A few other commands are: ```bash
- `make run` to run the server # Start the server on port 3030 in debug mode with a different fingers file
- `make test` to run the tests docker run git.maronato.dev/maronato/finger serve --port 3030 --debug --finger-file /app/my-fingers.yml
- `make lint` to run the linter
- `make clean` to clean the build files # or run a healthcheck on a different finger container
docker run git.maronato.dev/maronato/finger healthcheck --host otherhost --port 3030
## License ```
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## Development
You need to have [Go](https://golang.org/) installed to build the project.
Clone the repo and run `make build` to build the binary. You can then run `./finger serve` to start the server.
A few other commands are:
- `make run` to run the server
- `make test` to run the tests
- `make lint` to run the linter
- `make clean` to clean the build files
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.