Compare commits

...

11 Commits

Author SHA1 Message Date
crapStone
16c117cc25 remove domain from cache when scheduled for renewal 2024-04-29 21:06:08 +00:00
crapStone
383a31dc0a format code 2024-04-29 21:06:08 +00:00
crapStone
94ad1edcc5 add issue number 2024-04-29 21:06:08 +00:00
crapStone
d9c3ebcd0e minor improvements 2024-04-29 21:06:08 +00:00
Moritz Marquardt
b5ad34e0af Use hashicorp's LRU cache for DNS & certificates
DNS caching is also limited to 30 seconds now instead of 5 minutes
2024-04-29 21:06:08 +00:00
pat-s
ca9433e0ea
run lint workflow on main branch to unblock build workflow 2024-04-29 14:48:00 +02:00
pat-s
d09c6e1218 Multiarch images (#324)
taken from #214

Configured to only build multiarch when also publishing (when running in `main` or for a release)

## Build time

- amd64 & arm64: 07:42
- amd64 only: 04:26

Build time via kaniko building only amd64: ~ 6/7 min

Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/324
Reviewed-by: crapStone <codeberg@crapstone.dev>
Co-authored-by: pat-s <patrick.schratz@gmail.com>
Co-committed-by: pat-s <patrick.schratz@gmail.com>
2024-04-29 12:46:00 +00:00
pat-s
8cba7f9c8a Add yamllint (#321)
Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/321
Reviewed-by: crapStone <codeberg@crapstone.dev>
Co-authored-by: pat-s <patrick.schratz@gmail.com>
Co-committed-by: pat-s <patrick.schratz@gmail.com>
2024-04-28 21:48:07 +00:00
pat-s
f407fd3ae4 Add prettier config and format files (#319)
Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/319
Reviewed-by: crapStone <codeberg@crapstone.dev>
Co-authored-by: pat-s <patrick.schratz@gmail.com>
Co-committed-by: pat-s <patrick.schratz@gmail.com>
2024-04-28 20:47:04 +00:00
Dependency bot
89800d4f36 chore(deps): update plugins/gitea-release docker tag to v1 (#322)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [plugins/gitea-release](https://github.com/drone-plugins/drone-gitea-release) | major | `0.3.1` -> `1.1.0` |

---

### Release Notes

<details>
<summary>drone-plugins/drone-gitea-release (plugins/gitea-release)</summary>

### [`v1.1.0`](https://github.com/drone-plugins/drone-gitea-release/compare/v1.0.0...v1.1.0)

[Compare Source](https://github.com/drone-plugins/drone-gitea-release/compare/v1.0.0...v1.1.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - "before 4am" (UTC).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMjYuMSIsInVwZGF0ZWRJblZlciI6IjM3LjMyNi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: woodpecker-bot <woodpecker-bot@obermui.de>
Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/322
Reviewed-by: Patrick Schratz <pat-s@noreply.codeberg.org>
Co-authored-by: Dependency bot <renovate-bot@noreply.codeberg.org>
Co-committed-by: Dependency bot <renovate-bot@noreply.codeberg.org>
2024-04-28 08:24:39 +00:00
crapStone
418afb7357 add nix flake for dev env (#320)
Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/320
Co-authored-by: crapStone <me@crapstone.dev>
Co-committed-by: crapStone <me@crapstone.dev>
2024-04-27 21:40:40 +00:00
20 changed files with 270 additions and 98 deletions

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 120,
"tabWidth": 2,
"endOfLine": "lf"
}

View File

@ -29,10 +29,11 @@ steps:
docker-dryrun: docker-dryrun:
depends_on: vendor depends_on: vendor
image: plugins/kaniko:1.8.8 image: woodpeckerci/plugin-docker-buildx:3.2.1
settings: settings:
dockerfile: Dockerfile dockerfile: Dockerfile
no_push: true platforms: linux/amd64
dry-run: true
tags: latest tags: latest
when: when:
- event: [push, pull_request] - event: [push, pull_request]
@ -48,7 +49,7 @@ steps:
- go version - go version
- just build-tag ${CI_COMMIT_TAG##v} - just build-tag ${CI_COMMIT_TAG##v}
when: when:
- event: ["tag"] - event: ['tag']
branch: branch:
- ${CI_REPO_DEFAULT_BRANCH} - ${CI_REPO_DEFAULT_BRANCH}
@ -79,7 +80,7 @@ steps:
release: release:
depends_on: build depends_on: build
image: plugins/gitea-release:0.3.1 image: plugins/gitea-release:1.1.0
settings: settings:
base_url: https://codeberg.org base_url: https://codeberg.org
file_exists: overwrite file_exists: overwrite
@ -92,16 +93,17 @@ steps:
- CI_BUILD_EVENT=${CI_BUILD_EVENT} - CI_BUILD_EVENT=${CI_BUILD_EVENT}
- CI_COMMIT_REF=${CI_COMMIT_REF} - CI_COMMIT_REF=${CI_COMMIT_REF}
when: when:
- event: ["tag"] - event: ['tag']
branch: branch:
- ${CI_REPO_DEFAULT_BRANCH} - ${CI_REPO_DEFAULT_BRANCH}
docker-next: docker-next:
depends_on: vendor depends_on: vendor
image: plugins/kaniko:1.8.8 image: woodpeckerci/plugin-docker-buildx:3.2.1
settings: settings:
registry: codeberg.org registry: codeberg.org
dockerfile: Dockerfile dockerfile: Dockerfile
platforms: linux/amd64,arm64
repo: codeberg.org/codeberg/pages-server repo: codeberg.org/codeberg/pages-server
tags: next tags: next
username: username:
@ -109,21 +111,22 @@ steps:
password: password:
from_secret: bot_token from_secret: bot_token
when: when:
- event: ["push"] - event: ['push']
branch: ${CI_REPO_DEFAULT_BRANCH} branch: ${CI_REPO_DEFAULT_BRANCH}
docker-tag: docker-tag:
depends_on: vendor depends_on: vendor
image: plugins/kaniko:1.8.8 image: woodpeckerci/plugin-docker-buildx:3.2.1
settings: settings:
registry: codeberg.org registry: codeberg.org
dockerfile: Dockerfile dockerfile: Dockerfile
platforms: linux/amd64,arm64
repo: codeberg.org/codeberg/pages-server repo: codeberg.org/codeberg/pages-server
tags: [latest, "${CI_COMMIT_TAG}"] tags: [latest, '${CI_COMMIT_TAG}']
username: username:
from_secret: bot_user from_secret: bot_user
password: password:
from_secret: bot_token from_secret: bot_token
when: when:
- event: ["push"] - event: ['push']
branch: ${CI_REPO_DEFAULT_BRANCH} branch: ${CI_REPO_DEFAULT_BRANCH}

View File

@ -1,7 +1,9 @@
when: when:
- event: pull_request - event: pull_request
- event: push - event: push
branch: renovate/* branch:
- ${CI_REPO_DEFAULT_BRANCH}
- renovate/**
steps: steps:
lint: lint:
@ -24,3 +26,19 @@ steps:
- event: pull_request - event: pull_request
- event: push - event: push
branch: renovate/* branch: renovate/*
yamllint:
image: pipelinecomponents/yamllint:0.31.1
depends_on: []
commands:
- yamllint .
when:
- event: pull_request
- event: push
branch: renovate/*
prettier:
image: docker.io/woodpeckerci/plugin-prettier:0.1.0
depends_on: []
settings:
version: 3.2.5

19
.yamllint.yaml Normal file
View File

@ -0,0 +1,19 @@
extends: default
rules:
comments:
require-starting-space: false
ignore-shebangs: true
min-spaces-from-content: 1
braces:
min-spaces-inside: 1
max-spaces-inside: 1
document-start:
present: false
indentation:
spaces: 2
indent-sequences: true
line-length:
max: 256
new-lines:
type: unix

View File

@ -1,14 +1,35 @@
FROM techknowlogick/xgo as build # Set the default Go version as a build argument
ARG XGO="go-1.21.x"
WORKDIR /workspace # Use xgo (a Go cross-compiler tool) as build image
FROM --platform=$BUILDPLATFORM techknowlogick/xgo:${XGO} as build
COPY . . # Set the working directory and copy the source code
RUN CGO_ENABLED=1 go build -tags 'sqlite sqlite_unlock_notify netgo' -ldflags '-s -w -extldflags "-static" -linkmode external' . WORKDIR /go/src/codeberg.org/codeberg/pages
COPY . /go/src/codeberg.org/codeberg/pages
# Set the target architecture (can be set using --build-arg), buildx set it automatically
ARG TARGETOS TARGETARCH
# Build the binary using xgo
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=1 \
xgo -x -v --targets=${TARGETOS}/${TARGETARCH} -tags='sqlite sqlite_unlock_notify netgo' -ldflags='-s -w -extldflags "-static" -linkmode external' -out pages .
# Use a scratch image as the base image for the final container,
# which will contain only the built binary and the CA certificates
FROM scratch FROM scratch
COPY --from=build /workspace/pages /pages
# Copy the built binary and the CA certificates from the build container to the final container
COPY --from=build /go/src/codeberg.org/codeberg/pages/ /pages
COPY --from=build \ COPY --from=build \
/etc/ssl/certs/ca-certificates.crt \ /etc/ssl/certs/ca-certificates.crt \
/etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
# Expose ports 80 and 443 for the built binary to listen on
EXPOSE 80/tcp
EXPOSE 443/tcp
# Set the entrypoint for the container to the built binary
ENTRYPOINT ["/pages"] ENTRYPOINT ["/pages"]

View File

@ -19,16 +19,16 @@ Redirects can be created with a `_redirects` file with the following format:
from to [status] from to [status]
``` ```
* Lines starting with `#` are ignored - Lines starting with `#` are ignored
* `from` - the path to redirect from (Note: repository and branch names are removed from request URLs) - `from` - the path to redirect from (Note: repository and branch names are removed from request URLs)
* `to` - the path or URL to redirect to - `to` - the path or URL to redirect to
* `status` - status code to use when redirecting (default 301) - `status` - status code to use when redirecting (default 301)
### Status codes ### Status codes
* `200` - returns content from specified path (no external URLs) without changing the URL (rewrite) - `200` - returns content from specified path (no external URLs) without changing the URL (rewrite)
* `301` - Moved Permanently (Permanent redirect) - `301` - Moved Permanently (Permanent redirect)
* `302` - Found (Temporary redirect) - `302` - Found (Temporary redirect)
### Examples ### Examples

View File

@ -3,7 +3,7 @@
[![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL--1.2-blue)](https://opensource.org/license/eupl-1-2/) [![License: EUPL-1.2](https://img.shields.io/badge/License-EUPL--1.2-blue)](https://opensource.org/license/eupl-1-2/)
[![status-badge](https://ci.codeberg.org/api/badges/Codeberg/pages-server/status.svg)](https://ci.codeberg.org/Codeberg/pages-server) [![status-badge](https://ci.codeberg.org/api/badges/Codeberg/pages-server/status.svg)](https://ci.codeberg.org/Codeberg/pages-server)
<a href="https://matrix.to/#/#gitea-pages-server:matrix.org" title="Join the Matrix room at https://matrix.to/#/#gitea-pages-server:matrix.org"> <a href="https://matrix.to/#/#gitea-pages-server:matrix.org" title="Join the Matrix room at https://matrix.to/#/#gitea-pages-server:matrix.org">
<img src="https://img.shields.io/matrix/gitea-pages-server:matrix.org?label=matrix"> <img src="https://img.shields.io/matrix/gitea-pages-server:matrix.org?label=matrix">
</a> </a>
Gitea lacks the ability to host static pages from Git. Gitea lacks the ability to host static pages from Git.
@ -21,19 +21,19 @@ and the [Codeberg Documentation](https://docs.codeberg.org/codeberg-pages/).
This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories. This is the new Codeberg Pages server, a solution for serving static pages from Gitea repositories.
Mapping custom domains is not static anymore, but can be done with DNS: Mapping custom domains is not static anymore, but can be done with DNS:
1) add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The 1. add a `.domains` text file to your repository, containing the allowed domains, separated by new lines. The
first line will be the canonical domain/URL; all other occurrences will be redirected to it. first line will be the canonical domain/URL; all other occurrences will be redirected to it.
2) add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to 2. add a CNAME entry to your domain, pointing to `[[{branch}.]{repo}.]{owner}.codeberg.page` (repo defaults to
"pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else. "pages", "branch" defaults to the default branch if "repo" is "pages", or to "pages" if "repo" is something else.
If the branch name contains slash characters, you need to replace "/" in the branch name to "~"): If the branch name contains slash characters, you need to replace "/" in the branch name to "~"):
`www.example.org. IN CNAME main.pages.example.codeberg.page.` `www.example.org. IN CNAME main.pages.example.codeberg.page.`
3) if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record 3. if a CNAME is set for "www.example.org", you can redirect there from the naked domain by adding an ALIAS record
for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT for "example.org" (if your provider allows ALIAS or similar records, otherwise use A/AAAA), together with a TXT
record that points to your repo (just like the CNAME record): record that points to your repo (just like the CNAME record):
`example.org IN ALIAS codeberg.page.` `example.org IN ALIAS codeberg.page.`
`example.org IN TXT main.pages.example.codeberg.page.` `example.org IN TXT main.pages.example.codeberg.page.`
Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge. Certificates are generated, updated and cleaned up automatically via Let's Encrypt through a TLS challenge.
@ -43,7 +43,8 @@ Certificates are generated, updated and cleaned up automatically via Let's Encry
## Deployment ## Deployment
**Warning: Some Caveats Apply** **Warning: Some Caveats Apply**
> Currently, the deployment requires you to have some knowledge of system administration as well as understanding and building code, > Currently, the deployment requires you to have some knowledge of system administration as well as understanding and building code,
> so you can eventually edit non-configurable and codeberg-specific settings. > so you can eventually edit non-configurable and codeberg-specific settings.
> In the future, we'll try to reduce these and make hosting Codeberg Pages as easy as setting up Gitea. > In the future, we'll try to reduce these and make hosting Codeberg Pages as easy as setting up Gitea.
@ -74,7 +75,7 @@ and especially have a look at [this section of the haproxy.cfg](https://codeberg
- `ACME_API` (default: <https://acme-v02.api.letsencrypt.org/directory>): set this to <https://acme.mock.director> to use invalid certificates without any verification (great for debugging). - `ACME_API` (default: <https://acme-v02.api.letsencrypt.org/directory>): set this to <https://acme.mock.director> to use invalid certificates without any verification (great for debugging).
ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet. ZeroSSL might be better in the future as it doesn't have rate limits and doesn't clash with the official Codeberg certificates (which are using Let's Encrypt), but I couldn't get it to work yet.
- `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders. - `ACME_EMAIL` (default: `noreply@example.email`): Set the email sent to the ACME API server to receive, for example, renewal reminders.
- `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL. - `ACME_EAB_KID` & `ACME_EAB_HMAC` (default: don't use EAB): EAB credentials, for example for ZeroSSL.
- `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider. - `ACME_ACCEPT_TERMS` (default: use self-signed certificate): Set this to "true" to accept the Terms of Service of your ACME provider.
- `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL. - `ACME_USE_RATE_LIMITS` (default: true): Set this to false to disable rate limits, e.g. with ZeroSSL.
- `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80. - `ENABLE_HTTP_SERVER` (default: false): Set this to true to enable the HTTP-01 challenge and redirect all other HTTP requests to HTTPS. Currently only works with port 80.

View File

@ -1,8 +1,9 @@
# HAProxy with SNI & Host-based rules # HAProxy with SNI & Host-based rules
This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), *as well as* to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection). This is a proof of concept, enabling HAProxy to use _either_ SNI to redirect to backends with their own HTTPS certificates (which are then fully exposed to the client; HAProxy only proxies on a TCP level in that case), _as well as_ to terminate HTTPS and use the Host header to redirect to backends that use HTTP (or a new HTTPS connection).
## How it works ## How it works
1. The `http_redirect_frontend` is only there to listen on port 80 and redirect every request to HTTPS. 1. The `http_redirect_frontend` is only there to listen on port 80 and redirect every request to HTTPS.
2. The `https_sni_frontend` listens on port 443 and chooses a backend based on the SNI hostname of the TLS connection. 2. The `https_sni_frontend` listens on port 443 and chooses a backend based on the SNI hostname of the TLS connection.
3. The `https_termination_backend` passes all requests to a unix socket (using the plain TCP data). 3. The `https_termination_backend` passes all requests to a unix socket (using the plain TCP data).
@ -11,6 +12,7 @@ This is a proof of concept, enabling HAProxy to use *either* SNI to redirect to
In the example (see [haproxy.cfg](haproxy.cfg)), the `pages_backend` is listening via HTTPS and is providing its own HTTPS certificates, while the `gitea_backend` only provides HTTP. In the example (see [haproxy.cfg](haproxy.cfg)), the `pages_backend` is listening via HTTPS and is providing its own HTTPS certificates, while the `gitea_backend` only provides HTTP.
## How to test ## How to test
```bash ```bash
docker-compose up & docker-compose up &
./test.sh ./test.sh

View File

@ -1,22 +1,21 @@
version: "3" version: '3'
services: services:
haproxy: haproxy:
image: haproxy image: haproxy
ports: ["443:443"] ports: ['443:443']
volumes: volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
- ./dhparam.pem:/etc/ssl/dhparam.pem:ro - ./dhparam.pem:/etc/ssl/dhparam.pem:ro
- ./haproxy-certificates:/etc/ssl/private/haproxy:ro - ./haproxy-certificates:/etc/ssl/private/haproxy:ro
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
gitea: gitea:
image: caddy image: caddy
volumes: volumes:
- ./gitea-www:/srv:ro - ./gitea-www:/srv:ro
- ./gitea.Caddyfile:/etc/caddy/Caddyfile:ro - ./gitea.Caddyfile:/etc/caddy/Caddyfile:ro
pages: pages:
image: caddy image: caddy
volumes: volumes:
- ./pages-www:/srv:ro - ./pages-www:/srv:ro
- ./pages.Caddyfile:/etc/caddy/Caddyfile:ro - ./pages.Caddyfile:/etc/caddy/Caddyfile:ro

73
flake.lock Normal file
View File

@ -0,0 +1,73 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1714030708,
"narHash": "sha256-JOGPOxa8N6ySzB7SQBsh0OVz+UXZriyahgvfNHMIY0Y=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b0d52b31f7f4d80f8bf38f0253652125579c35ff",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"systems": "systems_2"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"id": "systems",
"type": "indirect"
}
}
},
"root": "root",
"version": 7
}

26
flake.nix Normal file
View File

@ -0,0 +1,26 @@
{
outputs = {
self,
nixpkgs,
flake-utils,
systems,
}:
flake-utils.lib.eachSystem (import systems)
(system: let
pkgs = import nixpkgs {
inherit system;
};
in {
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
gcc
go
gofumpt
gopls
gotools
go-tools
sqlite-interactive
];
};
});
}

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/creasty/defaults v1.7.0 github.com/creasty/defaults v1.7.0
github.com/go-acme/lego/v4 v4.5.3 github.com/go-acme/lego/v4 v4.5.3
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/joho/godotenv v1.4.0 github.com/joho/godotenv v1.4.0
github.com/lib/pq v1.10.7 github.com/lib/pq v1.10.7
github.com/mattn/go-sqlite3 v1.14.16 github.com/mattn/go-sqlite3 v1.14.16

2
go.sum
View File

@ -323,6 +323,8 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=

View File

@ -1,18 +1,12 @@
<!DOCTYPE html> <!doctype html>
<html class="codeberg-design"> <html class="codeberg-design">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<title>{{.StatusText}}</title> <title>{{.StatusText}}</title>
<link <link rel="stylesheet" href="https://design.codeberg.org/design-kit/codeberg.css" />
rel="stylesheet" <link rel="stylesheet" href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css" />
href="https://design.codeberg.org/design-kit/codeberg.css"
/>
<link
rel="stylesheet"
href="https://fonts.codeberg.org/dist/inter/Inter%20Web/inter.css"
/>
<style> <style>
body { body {
@ -34,12 +28,7 @@
</style> </style>
</head> </head>
<body> <body>
<svg <svg xmlns="http://www.w3.org/2000/svg" height="10em" viewBox="0 0 24 24" fill="var(--blue-color)">
xmlns="http://www.w3.org/2000/svg"
height="10em"
viewBox="0 0 24 24"
fill="var(--blue-color)"
>
<path <path
d="M 9 2 C 5.1458514 2 2 5.1458514 2 9 C 2 12.854149 5.1458514 16 9 16 C 10.747998 16 12.345009 15.348024 13.574219 14.28125 L 14 14.707031 L 14 16 L 19.585938 21.585938 C 20.137937 22.137937 21.033938 22.137938 21.585938 21.585938 C 22.137938 21.033938 22.137938 20.137938 21.585938 19.585938 L 16 14 L 14.707031 14 L 14.28125 13.574219 C 15.348024 12.345009 16 10.747998 16 9 C 16 5.1458514 12.854149 2 9 2 z M 9 4 C 11.773268 4 14 6.2267316 14 9 C 14 11.773268 11.773268 14 9 14 C 6.2267316 14 4 11.773268 4 9 C 4 6.2267316 6.2267316 4 9 4 z" d="M 9 2 C 5.1458514 2 2 5.1458514 2 9 C 2 12.854149 5.1458514 16 9 16 C 10.747998 16 12.345009 15.348024 13.574219 14.28125 L 14 14.707031 L 14 16 L 19.585938 21.585938 C 20.137937 22.137937 21.033938 22.137938 21.585938 21.585938 C 22.137938 21.033938 22.137938 20.137938 21.585938 19.585938 L 16 14 L 14.707031 14 L 14.28125 13.574219 C 15.348024 12.345009 16 10.747998 16 9 C 16 5.1458514 12.854149 2 9 2 z M 9 4 C 11.773268 4 14 6.2267316 14 9 C 14 11.773268 11.773268 14 9 14 C 6.2267316 14 4 11.773268 4 9 C 4 6.2267316 6.2267316 4 9 4 z"
/> />
@ -50,18 +39,13 @@
<p><b>"{{.Message}}"</b></p> <p><b>"{{.Message}}"</b></p>
<p> <p>
We hope this isn't a problem on our end ;) - Make sure to check the We hope this isn't a problem on our end ;) - Make sure to check the
<a <a href="https://docs.codeberg.org/codeberg-pages/troubleshooting/" target="_blank"
href="https://docs.codeberg.org/codeberg-pages/troubleshooting/"
target="_blank"
>troubleshooting section in the Docs</a >troubleshooting section in the Docs</a
>! >!
</p> </p>
</h5> </h5>
<small class="text-muted"> <small class="text-muted">
<img <img src="https://design.codeberg.org/logo-kit/icon.svg" class="align-top" />
src="https://design.codeberg.org/logo-kit/icon.svg"
class="align-top"
/>
Static pages made easy - Static pages made easy -
<a href="https://codeberg.page">Codeberg Pages</a> <a href="https://codeberg.page">Codeberg Pages</a>
</small> </small>

View File

@ -14,6 +14,7 @@ import (
"github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/certificate"
"github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/lego"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/reugn/equalizer" "github.com/reugn/equalizer"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
@ -31,11 +32,16 @@ func TLSConfig(mainDomainSuffix string,
giteaClient *gitea.Client, giteaClient *gitea.Client,
acmeClient *AcmeClient, acmeClient *AcmeClient,
firstDefaultBranch string, firstDefaultBranch string,
keyCache, challengeCache, dnsLookupCache, canonicalDomainCache cache.ICache, challengeCache, canonicalDomainCache cache.ICache,
certDB database.CertDB, certDB database.CertDB,
noDNS01 bool, noDNS01 bool,
rawDomain string, rawDomain string,
) *tls.Config { ) *tls.Config {
keyCache, err := lru.New[string, tls.Certificate](32)
if err != nil {
panic(err) // This should only happen if 32 < 0 at the time of writing, which should be reason enough to panic.
}
return &tls.Config{ return &tls.Config{
// check DNS name & get certificate from Let's Encrypt // check DNS name & get certificate from Let's Encrypt
GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
@ -86,7 +92,7 @@ func TLSConfig(mainDomainSuffix string,
} }
} else { } else {
var targetRepo, targetBranch string var targetRepo, targetBranch string
targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) targetOwner, targetRepo, targetBranch = dnsutils.GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch)
if targetOwner == "" { if targetOwner == "" {
// DNS not set up, return main certificate to redirect to the docs // DNS not set up, return main certificate to redirect to the docs
domain = mainDomainSuffix domain = mainDomainSuffix
@ -106,8 +112,13 @@ func TLSConfig(mainDomainSuffix string,
} }
if tlsCertificate, ok := keyCache.Get(domain); ok { if tlsCertificate, ok := keyCache.Get(domain); ok {
// we can use an existing certificate object if shouldRenewCert(&tlsCertificate, 7) {
return tlsCertificate.(*tls.Certificate), nil // if cert is up for renewal remove it from the cache
keyCache.Remove(domain)
} else {
// we can use an existing certificate object
return &tlsCertificate, nil
}
} }
var tlsCertificate *tls.Certificate var tlsCertificate *tls.Certificate
@ -132,9 +143,7 @@ func TLSConfig(mainDomainSuffix string,
} }
} }
if err := keyCache.Set(domain, tlsCertificate, 15*time.Minute); err != nil { keyCache.Add(domain, *tlsCertificate)
return nil, err
}
return tlsCertificate, nil return tlsCertificate, nil
}, },
NextProtos: []string{ NextProtos: []string{
@ -192,7 +201,7 @@ func (c *AcmeClient) retrieveCertFromDB(sni, mainDomainSuffix string, useDnsProv
} }
// renew certificates 7 days before they expire // renew certificates 7 days before they expire
if tlsCertificate.Leaf.NotAfter.Before(time.Now().Add(7 * 24 * time.Hour)) { if shouldRenewCert(&tlsCertificate, 7) {
// TODO: use ValidTill of custom cert struct // TODO: use ValidTill of custom cert struct
if res.CSR != nil && len(res.CSR) > 0 { if res.CSR != nil && len(res.CSR) > 0 {
// CSR stores the time when the renewal shall be tried again // CSR stores the time when the renewal shall be tried again
@ -334,6 +343,11 @@ func SetupMainDomainCertificates(mainDomainSuffix string, acmeClient *AcmeClient
return nil return nil
} }
// shouldRenewCert returns true if the validity date of the cert is less than the given number of days in the future
func shouldRenewCert(cert *tls.Certificate, days uint) bool {
return cert.Leaf.NotAfter.Before(time.Now().Add(time.Duration(days) * 24 * time.Hour))
}
func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) { func MaintainCertDB(ctx context.Context, interval time.Duration, acmeClient *AcmeClient, mainDomainSuffix string, certDB database.CertDB) {
for { for {
// delete expired certs that will be invalid until next clean up // delete expired certs that will be invalid until next clean up

View File

@ -5,22 +5,26 @@ import (
"strings" "strings"
"time" "time"
"codeberg.org/codeberg/pages/server/cache" "github.com/hashicorp/golang-lru/v2/expirable"
) )
// lookupCacheTimeout specifies the timeout for the DNS lookup cache. const (
var lookupCacheTimeout = 15 * time.Minute lookupCacheValidity = 30 * time.Second
defaultPagesRepo = "pages"
)
var defaultPagesRepo = "pages" // TODO(#316): refactor to not use global variables
var lookupCache *expirable.LRU[string, string] = expirable.NewLRU[string, string](4096, nil, lookupCacheValidity)
// GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix. // GetTargetFromDNS searches for CNAME or TXT entries on the request domain ending with MainDomainSuffix.
// If everything is fine, it returns the target data. // If everything is fine, it returns the target data.
func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLookupCache cache.ICache) (targetOwner, targetRepo, targetBranch string) { func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string) (targetOwner, targetRepo, targetBranch string) {
// Get CNAME or TXT // Get CNAME or TXT
var cname string var cname string
var err error var err error
if cachedName, ok := dnsLookupCache.Get(domain); ok {
cname = cachedName.(string) if entry, ok := lookupCache.Get(domain); ok {
cname = entry
} else { } else {
cname, err = net.LookupCNAME(domain) cname, err = net.LookupCNAME(domain)
cname = strings.TrimSuffix(cname, ".") cname = strings.TrimSuffix(cname, ".")
@ -38,7 +42,7 @@ func GetTargetFromDNS(domain, mainDomainSuffix, firstDefaultBranch string, dnsLo
} }
} }
} }
_ = dnsLookupCache.Set(domain, cname, lookupCacheTimeout) _ = lookupCache.Add(domain, cname)
} }
if cname == "" { if cname == "" {
return return

View File

@ -23,7 +23,7 @@ const (
func Handler( func Handler(
cfg config.ServerConfig, cfg config.ServerConfig,
giteaClient *gitea.Client, giteaClient *gitea.Client,
dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, canonicalDomainCache, redirectsCache cache.ICache,
) http.HandlerFunc { ) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) { return func(w http.ResponseWriter, req *http.Request) {
log.Debug().Msg("\n----------------------------------------------------------") log.Debug().Msg("\n----------------------------------------------------------")
@ -108,7 +108,7 @@ func Handler(
trimmedHost, trimmedHost,
pathElements, pathElements,
cfg.PagesBranches[0], cfg.PagesBranches[0],
dnsLookupCache, canonicalDomainCache, redirectsCache) canonicalDomainCache, redirectsCache)
} }
} }
} }

View File

@ -19,10 +19,10 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
trimmedHost string, trimmedHost string,
pathElements []string, pathElements []string,
firstDefaultBranch string, firstDefaultBranch string,
dnsLookupCache, canonicalDomainCache, redirectsCache cache.ICache, canonicalDomainCache, redirectsCache cache.ICache,
) { ) {
// Serve pages from custom domains // Serve pages from custom domains
targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch, dnsLookupCache) targetOwner, targetRepo, targetBranch := dns.GetTargetFromDNS(trimmedHost, mainDomainSuffix, firstDefaultBranch)
if targetOwner == "" { if targetOwner == "" {
html.ReturnErrorPage(ctx, html.ReturnErrorPage(ctx,
"could not obtain repo owner from custom domain", "could not obtain repo owner from custom domain",
@ -53,7 +53,7 @@ func handleCustomDomain(log zerolog.Logger, ctx *context.Context, giteaClient *g
return return
} else if canonicalDomain != trimmedHost { } else if canonicalDomain != trimmedHost {
// only redirect if the target is also a codeberg page! // only redirect if the target is also a codeberg page!
targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, firstDefaultBranch, dnsLookupCache) targetOwner, _, _ = dns.GetTargetFromDNS(strings.SplitN(canonicalDomain, "/", 2)[0], mainDomainSuffix, firstDefaultBranch)
if targetOwner != "" { if targetOwner != "" {
ctx.Redirect("https://"+canonicalDomain+"/"+targetOpt.TargetPath, http.StatusTemporaryRedirect) ctx.Redirect("https://"+canonicalDomain+"/"+targetOpt.TargetPath, http.StatusTemporaryRedirect)
return return

View File

@ -29,7 +29,7 @@ func TestHandlerPerformance(t *testing.T) {
AllowedCorsDomains: []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"}, AllowedCorsDomains: []string{"raw.codeberg.org", "fonts.codeberg.org", "design.codeberg.org"},
PagesBranches: []string{"pages"}, PagesBranches: []string{"pages"},
} }
testHandler := Handler(serverCfg, giteaClient, cache.NewInMemoryCache(), cache.NewInMemoryCache(), cache.NewInMemoryCache()) testHandler := Handler(serverCfg, giteaClient, cache.NewInMemoryCache(), cache.NewInMemoryCache())
testCase := func(uri string, status int) { testCase := func(uri string, status int) {
t.Run(uri, func(t *testing.T) { t.Run(uri, func(t *testing.T) {

View File

@ -70,12 +70,9 @@ func Serve(ctx *cli.Context) error {
} }
defer closeFn() defer closeFn()
keyCache := cache.NewInMemoryCache()
challengeCache := cache.NewInMemoryCache() challengeCache := cache.NewInMemoryCache()
// canonicalDomainCache stores canonical domains // canonicalDomainCache stores canonical domains
canonicalDomainCache := cache.NewInMemoryCache() canonicalDomainCache := cache.NewInMemoryCache()
// dnsLookupCache stores DNS lookups for custom domains
dnsLookupCache := cache.NewInMemoryCache()
// redirectsCache stores redirects in _redirects files // redirectsCache stores redirects in _redirects files
redirectsCache := cache.NewInMemoryCache() redirectsCache := cache.NewInMemoryCache()
// clientResponseCache stores responses from the Gitea server // clientResponseCache stores responses from the Gitea server
@ -108,7 +105,7 @@ func Serve(ctx *cli.Context) error {
giteaClient, giteaClient,
acmeClient, acmeClient,
cfg.Server.PagesBranches[0], cfg.Server.PagesBranches[0],
keyCache, challengeCache, dnsLookupCache, canonicalDomainCache, challengeCache, canonicalDomainCache,
certDB, certDB,
cfg.ACME.NoDNS01, cfg.ACME.NoDNS01,
cfg.Server.RawDomain, cfg.Server.RawDomain,
@ -134,7 +131,7 @@ func Serve(ctx *cli.Context) error {
} }
// Create ssl handler based on settings // Create ssl handler based on settings
sslHandler := handler.Handler(cfg.Server, giteaClient, dnsLookupCache, canonicalDomainCache, redirectsCache) sslHandler := handler.Handler(cfg.Server, giteaClient, canonicalDomainCache, redirectsCache)
// Start the ssl listener // Start the ssl listener
log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr()) log.Info().Msgf("Start SSL server using TCP listener on %s", listener.Addr())