mirror of
https://github.com/go-i2p/go-gitlooseleaf.git
synced 2025-08-19 17:45:23 -04:00
Compare commits
41 Commits
nightly
...
forgejo-ni
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5a20dc9121 | ||
![]() |
e1985661aa | ||
![]() |
a89fc7a6a5 | ||
![]() |
36c21192f0 | ||
![]() |
cad60aac25 | ||
![]() |
283233c534 | ||
![]() |
fe561e9e99 | ||
![]() |
a25b108fe9 | ||
![]() |
697ee54bef | ||
![]() |
d81b6c906c | ||
![]() |
960383c1c7 | ||
![]() |
7713312fe3 | ||
![]() |
7c354f9b52 | ||
![]() |
4f9dcde18a | ||
![]() |
57ee6746ef | ||
![]() |
3d31be0215 | ||
![]() |
5e71999e5a | ||
![]() |
54691e3cbe | ||
![]() |
19025e2f10 | ||
![]() |
2cc6f51329 | ||
![]() |
a046854c58 | ||
![]() |
199ece6a13 | ||
![]() |
228ab09cf8 | ||
![]() |
fe0c790937 | ||
![]() |
4f9ed824f5 | ||
![]() |
b59cdadcde | ||
![]() |
7c97f29d63 | ||
![]() |
cb0ab3e597 | ||
![]() |
d341180dbe | ||
![]() |
1c5aeb87cd | ||
![]() |
69d267bab5 | ||
![]() |
f89e9dee28 | ||
![]() |
1902034f2c | ||
![]() |
16b5f3b7c9 | ||
![]() |
aae3a5f571 | ||
![]() |
08e6728356 | ||
![]() |
6d90b7e40c | ||
![]() |
b669c9c6c2 | ||
![]() |
3daa81b6b1 | ||
![]() |
4611687449 | ||
![]() |
75d49f8d6f |
127
.github/workflows/forgejo-build.yml
vendored
Normal file
127
.github/workflows/forgejo-build.yml
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
name: Forgejo Build Pipeline
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Runs daily at midnight UTC
|
||||
workflow_dispatch: # Allow manual triggers
|
||||
|
||||
permissions:
|
||||
contents: write # Required for creating releases
|
||||
|
||||
jobs:
|
||||
check-release:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
new_tag: ${{ steps.get_latest_tag.outputs.tag }}
|
||||
should_build: ${{ steps.check_existing.outputs.should_build }}
|
||||
steps:
|
||||
- name: Get Latest Forgejo Tag
|
||||
id: get_latest_tag
|
||||
run: |
|
||||
LATEST_TAG=$(curl -s 'https://codeberg.org/api/v1/repos/forgejo/forgejo/releases?limit=1' | jq -r '.[0].tag_name')
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
echo "Latest Forgejo tag: $LATEST_TAG"
|
||||
|
||||
- name: Check Existing Release
|
||||
id: check_existing
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
try {
|
||||
const tag = '${{ steps.get_latest_tag.outputs.tag }}';
|
||||
const release = await github.rest.repos.getReleaseByTag({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag: tag + '-forgejo'
|
||||
});
|
||||
core.setOutput('should_build', 'false');
|
||||
} catch (error) {
|
||||
core.setOutput('should_build', 'true');
|
||||
}
|
||||
|
||||
build:
|
||||
needs: check-release
|
||||
if: needs.check-release.outputs.should_build == 'true'
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 120 # 2-hour timeout
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24.3'
|
||||
cache: true
|
||||
|
||||
- name: Setup node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Checkout Forgejo Source
|
||||
run: |
|
||||
git clone https://codeberg.org/forgejo/forgejo.git forgejo-source
|
||||
cd forgejo-source
|
||||
git checkout ${{ needs.check-release.outputs.new_tag }}
|
||||
|
||||
- name: Build Forgejo
|
||||
working-directory: forgejo-source
|
||||
run: |
|
||||
make clean
|
||||
cp -v ../net_mirror.go modules/graceful/net_mirror.go
|
||||
cp -v ../net_mirror_dialer.go modules/graceful/net_mirror_dialer.go
|
||||
cp -v ../net_mirror_unix.go modules/graceful/net_mirror_unix.go
|
||||
cp -v ../net_mirror_windows.go modules/graceful/net_mirror_windows.go
|
||||
go mod tidy
|
||||
make build
|
||||
env:
|
||||
TAGS: bindata sqlite sqlite_unlock_notify netgo osusergo
|
||||
GOFLAGS: -ldflags="-extldflags=-static"
|
||||
CO_ENABLED: 0
|
||||
|
||||
- name: Prepare Artifact
|
||||
shell: bash
|
||||
run: |
|
||||
cd forgejo-source
|
||||
ARTIFACT_NAME="gitea-${{ runner.os }}"
|
||||
if [ "${{ runner.os }}" = "Windows" ]; then
|
||||
mv gitea.exe "${ARTIFACT_NAME}.exe"
|
||||
else
|
||||
mv gitea "${ARTIFACT_NAME}"
|
||||
fi
|
||||
|
||||
- name: Upload Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gitea-${{ runner.os }}
|
||||
path: |
|
||||
forgejo-source/gitea-${{ runner.os }}*
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
needs: [check-release, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
tag_name: ${{ needs.check-release.outputs.new_tag }}-forgejo
|
||||
name: "Forgejo ${{ needs.check-release.outputs.new_tag }}"
|
||||
body: "Automated build of Forgejo ${{ needs.check-release.outputs.new_tag }}"
|
||||
files: |
|
||||
forgejo-Linux/*
|
||||
forgejo-Windows/*
|
||||
forgejo-macOS/*
|
||||
draft: false
|
||||
prerelease: false
|
97
.github/workflows/forgejo-nightly.yml
vendored
Normal file
97
.github/workflows/forgejo-nightly.yml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
name: Forgejo Nightly Build
|
||||
on:
|
||||
push: # Run on any push
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Runs daily at midnight UTC
|
||||
workflow_dispatch: # Allow manual triggers
|
||||
|
||||
permissions:
|
||||
contents: write # Required for creating releases
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 120 # 2-hour timeout
|
||||
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.24.3'
|
||||
cache: true
|
||||
|
||||
- name: Setup node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Checkout Forgejo Source
|
||||
run: |
|
||||
git clone https://codeberg.org/forgejo/forgejo.git forgejo-source
|
||||
cd forgejo-source
|
||||
git checkout forgejo
|
||||
|
||||
- name: Build Forgejo
|
||||
working-directory: forgejo-source
|
||||
run: |
|
||||
make clean
|
||||
cp -v ../net_mirror.go modules/graceful/net_mirror.go
|
||||
cp -v ../net_mirror_dialer.go modules/graceful/net_mirror_dialer.go
|
||||
cp -v ../net_mirror_unix.go modules/graceful/net_mirror_unix.go
|
||||
cp -v ../net_mirror_windows.go modules/graceful/net_mirror_windows.go
|
||||
go mod tidy
|
||||
make build
|
||||
env:
|
||||
TAGS: bindata sqlite sqlite_unlock_notify netgo osusergo
|
||||
GOFLAGS: -ldflags="-extldflags=-static"
|
||||
CO_ENABLED: 0
|
||||
|
||||
- name: Prepare Artifact
|
||||
shell: bash
|
||||
run: |
|
||||
cd forgejo-source
|
||||
ARTIFACT_NAME="gitea-${{ runner.os }}"
|
||||
if [ "${{ runner.os }}" = "Windows" ]; then
|
||||
mv gitea.exe "${ARTIFACT_NAME}.exe"
|
||||
else
|
||||
mv gitea "${ARTIFACT_NAME}"
|
||||
fi
|
||||
|
||||
- name: Upload Build Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gitea-${{ runner.os }}
|
||||
path: |
|
||||
forgejo-source/gitea-${{ runner.os }}*
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Get Current Date
|
||||
id: date
|
||||
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create or Update Nightly Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: forgejo-nightly
|
||||
name: "Forgejo Nightly Build (${{ steps.date.outputs.date }})"
|
||||
body: "Automated nightly build of Forgejo from main branch, built on ${{ steps.date.outputs.date }}"
|
||||
artifacts: "forgejo-Linux/*, forgejo-Windows/*, forgejo-macOS/*"
|
||||
draft: false
|
||||
prerelease: true
|
||||
allowUpdates: true
|
||||
removeArtifacts: false
|
||||
replacesArtifacts: true
|
2
.github/workflows/gitea-build.yml
vendored
2
.github/workflows/gitea-build.yml
vendored
@@ -61,7 +61,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.24'
|
||||
cache: true
|
||||
|
||||
- name: Checkout Gitea Source
|
||||
|
2
.github/workflows/gitea-nightly.yml
vendored
2
.github/workflows/gitea-nightly.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
go-version: '1.24'
|
||||
cache: true
|
||||
|
||||
- name: Checkout Gitea Source
|
||||
|
17
DOCKER.md
17
DOCKER.md
@@ -1,17 +0,0 @@
|
||||
Docker Instructions:
|
||||
====================
|
||||
|
||||
The Dockerfile in this repo assumes a completely self-contained setup, where I2P resides in the same container as gitea.
|
||||
This is purely for simplicity's sake.
|
||||
In order to build it, use:
|
||||
|
||||
```sh
|
||||
docker build -t go-i2p/go-gittisane .
|
||||
```
|
||||
|
||||
then in order to run it, use:
|
||||
|
||||
```sh
|
||||
docker run --name i2p-gittisane -d go-i2p/go-gittisane
|
||||
docker log i2p-gittinsane
|
||||
```
|
34
Dockerfile
34
Dockerfile
@@ -1,34 +0,0 @@
|
||||
# Stage 1: Download gittisane
|
||||
FROM alpine:latest as downloader
|
||||
RUN apk add --no-cache curl bash grep
|
||||
WORKDIR /download/
|
||||
COPY download.sh .
|
||||
RUN /download/download.sh
|
||||
RUN ls /download/downloads
|
||||
RUN cp /download/downloads/gitea-Linux /download/gittisane-linux-amd64
|
||||
|
||||
# Stage 2: Runtime
|
||||
FROM geti2p/i2p:latest
|
||||
|
||||
# Copy gittisane binary
|
||||
COPY --from=downloader /download/gittisane-linux-amd64 /usr/local/bin/gittisane
|
||||
RUN chmod +x /usr/local/bin/gittisane
|
||||
|
||||
# Create data directories
|
||||
RUN mkdir -p /data/gitea
|
||||
|
||||
# Configure I2P for SAM
|
||||
RUN echo "i2cp.tcp.host=127.0.0.1\n\
|
||||
i2cp.tcp.port=7654\n\
|
||||
sam.enabled=true\n\
|
||||
sam.host=127.0.0.1\n\
|
||||
sam.port=7656" >> /i2p/router.config
|
||||
|
||||
# Setup volumes
|
||||
VOLUME ["/data/gitea", "/i2p/.i2p"]
|
||||
WORKDIR /data/gitea
|
||||
|
||||
# Create startup script
|
||||
COPY start.sh /usr/local/bin/start.sh
|
||||
|
||||
ENTRYPOINT ["start.sh"]
|
105
Makefile
Normal file
105
Makefile
Normal file
@@ -0,0 +1,105 @@
|
||||
.PHONY: all download setup-user install-binary install-systemd enable disable uninstall clean help
|
||||
|
||||
# Installation paths
|
||||
BINARY_PATH = /usr/local/bin/gitea
|
||||
SYSTEMD_PATH = /etc/systemd/system
|
||||
CONFIG_PATH = /etc/gitea
|
||||
DATA_PATH = /var/lib/gitea
|
||||
|
||||
# Default target
|
||||
all: help
|
||||
|
||||
help:
|
||||
@echo "GitLooseLeaf - Modified Gitea with multi-protocol support"
|
||||
@echo ""
|
||||
@echo "Usage:"
|
||||
@echo " make download - Download the latest gitea binary"
|
||||
@echo " make setup-user - Create git user and required directories"
|
||||
@echo " make install-binary - Install Gitea binary"
|
||||
@echo " make install-systemd - Install systemd service files"
|
||||
@echo " make enable - Enable and start Gitea service"
|
||||
@echo " make disable - Disable and stop Gitea service"
|
||||
@echo " make install - Complete installation (all above steps)"
|
||||
@echo " make uninstall - Remove Gitea"
|
||||
@echo " make clean - Clean up downloaded files"
|
||||
@echo ""
|
||||
@echo "Note: Many commands require root privileges (use sudo)"
|
||||
|
||||
# Download latest Gitea binary
|
||||
download:
|
||||
@echo "Downloading latest Gitea binary..."
|
||||
mkdir -p downloads
|
||||
GITEA_URL="https://github.com/go-i2p/go-gitlooseleaf/releases/download/nightly/gitea-Linux"; \
|
||||
wget -O downloads/gitea "$$GITEA_URL" || curl -L -o downloads/gitea "$$GITEA_URL"
|
||||
chmod +x downloads/gitea
|
||||
|
||||
# Setup git user and directories
|
||||
setup-user:
|
||||
@echo "Setting up git user and directories..."
|
||||
id -u git &>/dev/null || adduser \
|
||||
--system \
|
||||
--shell /bin/bash \
|
||||
--gecos 'Git Version Control' \
|
||||
--group \
|
||||
--disabled-password \
|
||||
--home /home/git \
|
||||
git
|
||||
mkdir -p $(DATA_PATH)/{custom,data,log}
|
||||
mkdir -p $(CONFIG_PATH)
|
||||
chown -R git:git $(DATA_PATH)/
|
||||
chmod -R 750 $(DATA_PATH)/
|
||||
chown root:git $(CONFIG_PATH)
|
||||
chmod 770 $(CONFIG_PATH)
|
||||
|
||||
# Install Gitea binary
|
||||
install-binary: download
|
||||
@echo "Installing Gitea binary..."
|
||||
cp downloads/gitea $(BINARY_PATH)
|
||||
chmod +x $(BINARY_PATH)
|
||||
setcap CAP_NET_BIND_SERVICE=+eip $(BINARY_PATH)
|
||||
|
||||
# Install systemd service files
|
||||
install-systemd:
|
||||
@echo "Installing systemd service files..."
|
||||
mkdir -p $(SYSTEMD_PATH)/gitea.service.d
|
||||
cp etc/systemd/system/gitea.service $(SYSTEMD_PATH)/
|
||||
cp etc/systemd/system/gitea.service.d/user-config.conf $(SYSTEMD_PATH)/gitea.service.d/
|
||||
systemctl daemon-reload
|
||||
|
||||
# Enable and start Gitea service
|
||||
enable:
|
||||
@echo "Enabling and starting Gitea service..."
|
||||
systemctl enable gitea.service
|
||||
systemctl start gitea.service
|
||||
@echo "Gitea service started successfully!"
|
||||
@echo "Please configure your email in $(SYSTEMD_PATH)/gitea.service.d/user-config.conf"
|
||||
@echo "Then restart with: systemctl restart gitea.service"
|
||||
|
||||
# Disable and stop Gitea service
|
||||
disable:
|
||||
@echo "Disabling and stopping Gitea service..."
|
||||
systemctl disable gitea.service
|
||||
systemctl stop gitea.service
|
||||
|
||||
# Complete installation
|
||||
install: setup-user install-binary install-systemd enable
|
||||
@echo "Installation complete!"
|
||||
@echo "You can now access Gitea at:"
|
||||
@echo "- HTTPS: https://$(shell hostname):3000"
|
||||
@echo "- I2P/Tor: Check logs for actual addresses: journalctl -u gitea"
|
||||
|
||||
# Uninstall Gitea
|
||||
uninstall: disable
|
||||
@echo "Uninstalling Gitea..."
|
||||
rm -f $(BINARY_PATH)
|
||||
rm -f $(SYSTEMD_PATH)/gitea.service
|
||||
rm -rf $(SYSTEMD_PATH)/gitea.service.d
|
||||
systemctl daemon-reload
|
||||
@echo "Gitea has been uninstalled."
|
||||
@echo "Note: User and data directories were not removed."
|
||||
@echo "To completely remove, delete: $(CONFIG_PATH) and $(DATA_PATH)"
|
||||
|
||||
# Clean up
|
||||
clean:
|
||||
@echo "Cleaning up..."
|
||||
rm -rf downloads
|
129
README.md
129
README.md
@@ -1,80 +1,87 @@
|
||||
# go-gitlooseleaf
|
||||
A soft-fork of gitea with support for running as a multi-protocol service. Just the mod and the CI files.
|
||||
|
||||
How it works:
|
||||
=============
|
||||
A soft-fork of Gitea that enables simultaneous multi-protocol access via standard TLS, I2P, and Tor onion services. This repository contains only the network interface modules and CI configuration needed to build custom Gitea binaries.
|
||||
|
||||
This uses GitHub CI to continuously build a version of Gitea that can simultaneously run as a regular TLS service, an I2P service, and a Tor onion service, based on the latest release of Gitea.
|
||||
We can do this without requiring a patch to the Gitea source code.
|
||||
This is because Gitea encapsulates its "Listening" and "Dialing" into functions, which can easily be substituted for alternative versions.
|
||||
For instance, the network listener is set up by a function, `graceful.GetListener() (net.Listener, error)` in the file `modules/graceful/server.go`.
|
||||
The default implementation of the `GetListener() (net.Listener, error)` function, `DefaultGetListener() (net.Listener, error)` is defined in the `modules/graceful/net_unix.go` for Unix-like systems and `modules/graceful/net_windows.go` for Windows-like systems.
|
||||
A developer who wishes to "Mod" gitea to listen on another kind of connection do so by creating a new file which implements a `GetListener() (net.Listener, error)` function using alternate listener implementations.
|
||||
## How It Works
|
||||
|
||||
On the client side, the same thing is possible because Go allows you to substitute the underlying transports used for the default HTTP Client.
|
||||
So, in the absence of overriding settings, we can configure it to use TLS, SAMv3 (for I2P), and the Tor SOCKS proxy to build HTTP connections using the appropriate transport for each service.
|
||||
This project leverages GitHub Actions to automatically build a modified version of Gitea that can simultaneously serve content over multiple protocols:
|
||||
- Standard HTTPS/TLS connections
|
||||
- I2P (Invisible Internet Project) network
|
||||
- Tor onion services
|
||||
|
||||
Finally, if you need to include additional libraries, run `go mod tidy` in the root of the gitea checkout to include them.
|
||||
The beauty of this approach is that it requires no changes to Gitea's core codebase, as Gitea intelligently encapsulates network operations through abstraction:
|
||||
|
||||
Here is a complete working example mod:
|
||||
1. **Network Listeners**: Gitea uses `graceful.GetListener()` (defined in `modules/graceful/server.go`) for all incoming connections
|
||||
2. **Network Clients**: Gitea's HTTP client connections can be configured with custom transport implementations
|
||||
|
||||
```Go
|
||||
// copy this file to modules/graceful/net_multi.go before building gitea
|
||||
package graceful
|
||||
We take advantage of these abstractions by replacing the default implementations with our multi-protocol versions during the build process.
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
## Implementation Details
|
||||
|
||||
"github.com/go-i2p/onramp"
|
||||
"github.com/cretz/bine/tor"
|
||||
)
|
||||
The network listener replacement works because Gitea's default `GetListener()` implementations (`DefaultGetListener()`) are defined in platform-specific files:
|
||||
- `modules/graceful/net_unix.go` for Unix-like systems
|
||||
- `modules/graceful/net_windows.go` for Windows
|
||||
|
||||
// Set up the I2P Garlic API
|
||||
var garlic, i2perr = onramp.NewGarlic("gitea-i2p", "127.0.0.1:7656", onramp.OPT_DEFAULTS)
|
||||
Our implementation introduces a `MultiGetListener()` function that handles TLS, I2P, and Tor connections using the `go-meta-listener` package, while still supporting Unix sockets for internal functions.
|
||||
|
||||
// Set up the Tor onion service
|
||||
var torInstance, torerr = tor.Start(nil, nil)
|
||||
var onion, onionerr = torInstance.Listen(nil, 80)
|
||||
Similarly, we replace the default HTTP client with a version that can route traffic through the appropriate network (TLS, I2P, or Tor) based on the destination.
|
||||
|
||||
// This implements the GetListener function for I2P. Note the exemption for Unix sockets.
|
||||
## Current Implementation
|
||||
|
||||
The current implementation in `net_mirror.go` uses:
|
||||
- `go-meta-listener/mirror` for listening on multiple protocols
|
||||
- Rate limiting through `go-i2p/go-limit`
|
||||
- Environment variables (`EMAIL`, `HOSTNAME`) for configuration
|
||||
|
||||
```go
|
||||
// This implements the GetListener function for TLS, I2P, and Onion
|
||||
func MultiGetListener(network, address string) (net.Listener, error) {
|
||||
// Add a deferral to say that we've tried to grab a listener
|
||||
defer GetManager().InformCleanup()
|
||||
switch network {
|
||||
case "unix", "unixpacket":
|
||||
// I2P isn't really a replacement for the stuff you use Unix sockets for and it's also not an anonymity risk, so treat them normally
|
||||
unixAddr, err := ResolveUnixAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetListenerUnixWrapper(network, unixAddr)
|
||||
default:
|
||||
return mirror.Listen("tcp", address, "./certs", true)
|
||||
}
|
||||
// Support for Unix sockets remains unchanged
|
||||
if network == "unix" || network == "unixpacket" {
|
||||
unixAddr, err := ResolveUnixAddr(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return GetListenerUnixWrapper(network, unixAddr)
|
||||
}
|
||||
|
||||
// For TCP connections, create a multi-protocol mirror listener
|
||||
ml, err := mirrorListener.Listen(address, os.Getenv("EMAIL"), "./certs", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply rate limiting
|
||||
return limitedlistener.NewLimitedListener(ml,
|
||||
limitedlistener.WithMaxConnections(500), // concurrent connections
|
||||
limitedlistener.WithRateLimit(24), // connections per second
|
||||
), nil
|
||||
}
|
||||
|
||||
// We use `init() to ensure that the appropriate Listeners and Dialers are correctly placed at runtime
|
||||
func init() {
|
||||
GetListener = MultiGetListener
|
||||
/*Dialer not shown here*/
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Caveats
|
||||
-------
|
||||
## Usage Caveats
|
||||
|
||||
Gitea makes a few other kinds of connections, besides `HTTP`, if instructed to do so in the config file.
|
||||
For instance, there is an SMTP client.
|
||||
Not all of these connections are automatically routed through the appropriate anonymity networks in this configuration.
|
||||
For I2P, you might need to use `127.0.0.1:7659/7660` for SMTP. For Tor, you would configure SOCKS proxy settings.
|
||||
Similarly, SSH client connections need additional configuration to properly route through these networks.
|
||||
Additional adjustments to the configuration can be made to also route these services across the appropriate networks but aren't fully documented here at this time.
|
||||
While the HTTP interface works seamlessly across all three protocols, other Gitea communication channels require additional configuration:
|
||||
|
||||
License
|
||||
-------
|
||||
1. **SMTP Client**: If configured, email connections from Gitea will need proper routing:
|
||||
- For I2P: Use local ports like `127.0.0.1:7659/7660`
|
||||
- For Tor: Configure appropriate SOCKS proxy settings
|
||||
|
||||
Both this mod and gitea are licensed under the MIT license.
|
||||
See LICENSE for net_multi*.go in this repository.
|
||||
LICENSE-gitea.md is a copy of the Gitea license from https://github.com/go-gitea/gitea
|
||||
2. **SSH Connections**: Git operations over SSH require additional configuration to properly route through anonymity networks. These settings depend on your specific deployment environment.
|
||||
|
||||
3. **Environment Variables**:
|
||||
- `EMAIL`: Used for TLS certificate generation (required for HTTPS)
|
||||
- `HOSTNAME`: Server hostname (defaults to local machine name if not set)
|
||||
|
||||
## Installation
|
||||
|
||||
You can:
|
||||
1. Download prebuilt binaries from the [releases page](https://github.com/go-i2p/go-gitlooseleaf/releases)
|
||||
2. Use the included `install.sh` script to set up a system service
|
||||
3. Build from source using the GitHub Actions workflows as a reference
|
||||
|
||||
## License
|
||||
|
||||
Both this modification and Gitea itself are licensed under the MIT license.
|
||||
- See [LICENSE](LICENSE) for this project's license
|
||||
- See [LICENSE-gitea.md](LICENSE-gitea.md) for the Gitea license from https://github.com/go-gitea/gitea
|
||||
|
29
etc/systemd/system/gitea.service
Normal file
29
etc/systemd/system/gitea.service
Normal file
@@ -0,0 +1,29 @@
|
||||
[Unit]
|
||||
Description=Gitea (Modified with multi-protocol TLS/I2P/Tor support)
|
||||
Documentation=https://github.com/go-i2p/go-gitlooseleaf
|
||||
After=network.target postgresql.service mysql.service mariadb.service
|
||||
Wants=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=git
|
||||
Group=git
|
||||
WorkingDirectory=/home/git
|
||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
||||
|
||||
# Hardening measures
|
||||
ProtectSystem=full
|
||||
PrivateTmp=true
|
||||
PrivateDevices=true
|
||||
NoNewPrivileges=true
|
||||
ReadWritePaths=/var/lib/gitea /etc/gitea /home/git
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
# Load user-modifiable configuration from drop-in directory
|
||||
# This will automatically include all .conf files in gitea.service.d/
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
22
etc/systemd/system/gitea.service.d/user-config.conf
Normal file
22
etc/systemd/system/gitea.service.d/user-config.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
[Service]
|
||||
# User-configurable environment variables for multi-protocol support
|
||||
|
||||
# Required for TLS certificate generation - CHANGE THIS!
|
||||
Environment="EMAIL=your-email@example.com"
|
||||
|
||||
# Optional: Set explicit hostname (defaults to system hostname if not set)
|
||||
# Environment="HOSTNAME=your-hostname"
|
||||
|
||||
# Optional: Performance tuning
|
||||
# Environment="MAX_CONNECTIONS=500"
|
||||
# Environment="RATE_LIMIT=24"
|
||||
|
||||
# Optional: Certificate directory
|
||||
# Environment="CERT_DIR=/var/lib/gitea/certs"
|
||||
|
||||
# Optional: Additional environment variables for database, etc.
|
||||
# Environment="GITEA_DATABASE_TYPE=postgres"
|
||||
# Environment="GITEA_DATABASE_HOST=localhost:5432"
|
||||
# Environment="GITEA_DATABASE_NAME=gitea"
|
||||
# Environment="GITEA_DATABASE_USER=gitea"
|
||||
# Environment="GITEA_DATABASE_PASSWD=gitea"
|
9
go.mod
9
go.mod
@@ -1,16 +1,18 @@
|
||||
module github.com/go-i2p/go-gittisane
|
||||
|
||||
go 1.23.5
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/go-i2p/go-meta-listener v0.0.0-20250419155249-feebe95fe95a
|
||||
github.com/go-i2p/onramp v0.33.92
|
||||
github.com/go-i2p/go-limit v0.0.0-20250203203118-210616857c15
|
||||
github.com/go-i2p/go-meta-dialer v0.0.0-20250501024057-715e91be3cfe
|
||||
github.com/go-i2p/go-meta-listener v0.0.4-0.20250505194551-6b30e62ee419
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cretz/bine v0.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708 // indirect
|
||||
github.com/go-i2p/onramp v0.33.92 // indirect
|
||||
github.com/go-i2p/sam3 v0.33.9 // indirect
|
||||
github.com/opd-ai/wileedot v0.0.0-20241217172720-521d4175e624 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
@@ -21,4 +23,5 @@ require (
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
)
|
||||
|
10
go.sum
10
go.sum
@@ -4,8 +4,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-i2p/go-meta-listener v0.0.0-20250419155249-feebe95fe95a h1:oKKNMSDp73q31UoAiFQD//xV4EeP70/TqsBzQAVKTAU=
|
||||
github.com/go-i2p/go-meta-listener v0.0.0-20250419155249-feebe95fe95a/go.mod h1:wF/MCCfB40gZyT9WtuYWQkUOPrnoTzA+NG0zpsy3s4M=
|
||||
github.com/go-i2p/go-limit v0.0.0-20250203203118-210616857c15 h1:ASjMbwlepoDQfrhv+H2B5ICBPJU5ES1JzmOxzPDx3YQ=
|
||||
github.com/go-i2p/go-limit v0.0.0-20250203203118-210616857c15/go.mod h1:4jjmVRhvKj47sQ6B6wdDhN1IrEZunE6KwkYLQx/BeVE=
|
||||
github.com/go-i2p/go-meta-dialer v0.0.0-20250501024057-715e91be3cfe h1:9Rxw2KtMCRKZHI4WavUAaatzKmc64V6kiYvcyTMHjeU=
|
||||
github.com/go-i2p/go-meta-dialer v0.0.0-20250501024057-715e91be3cfe/go.mod h1:++xHSOvnGymRSyFbi9A9hztcfwKfU6/nJAtVxrNo8Zo=
|
||||
github.com/go-i2p/go-meta-listener v0.0.4-0.20250505194551-6b30e62ee419 h1:zMq0nZ6wmJktnRwy1hFXzDTSKlZ4Cah2jdzvtp9tH2k=
|
||||
github.com/go-i2p/go-meta-listener v0.0.4-0.20250505194551-6b30e62ee419/go.mod h1:wF/MCCfB40gZyT9WtuYWQkUOPrnoTzA+NG0zpsy3s4M=
|
||||
github.com/go-i2p/i2pkeys v0.0.0-20241108200332-e4f5ccdff8c4/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
|
||||
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708 h1:Tiy9IBwi21maNpK74yCdHursJJMkyH7w87tX1nXGWzg=
|
||||
github.com/go-i2p/i2pkeys v0.33.10-0.20241113193422-e10de5e60708/go.mod h1:m5TlHjPZrU5KbTd7Lr+I2rljyC6aJ88HdkeMQXV0U0E=
|
||||
@@ -44,6 +48,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
26
install.sh
Normal file
26
install.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#! /usr/bin/env sh
|
||||
|
||||
# AS ROOT
|
||||
|
||||
adduser \
|
||||
--system \
|
||||
--shell /bin/bash \
|
||||
--gecos 'Git Version Control' \
|
||||
--group \
|
||||
--disabled-password \
|
||||
--home /home/git \
|
||||
git
|
||||
|
||||
mkdir -p /var/lib/gitea/{custom,data,log}
|
||||
chown -R git:git /var/lib/gitea/
|
||||
chmod -R 750 /var/lib/gitea/
|
||||
mkdir /etc/gitea
|
||||
chown root:git /etc/gitea
|
||||
chmod 770 /etc/gitea
|
||||
killall gitea
|
||||
sleep 3s
|
||||
GITEA_URL=https://github.com/go-i2p/go-gitlooseleaf/releases/download/nightly/gitea-Linux
|
||||
wget -O /usr/local/bin/gitea "$GITEA_URL"
|
||||
chmod +x /usr/local/bin/gitea
|
||||
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/gitea
|
||||
su - git
|
@@ -9,6 +9,8 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/go-i2p/go-meta-listener/mirror"
|
||||
|
||||
limitedlistener "github.com/go-i2p/go-limit"
|
||||
)
|
||||
|
||||
func hostname() string {
|
||||
@@ -51,8 +53,16 @@ func MultiGetListener(network, address string) (net.Listener, error) {
|
||||
return nil, err
|
||||
}
|
||||
return GetListenerUnixWrapper(network, unixAddr)
|
||||
|
||||
default:
|
||||
return mirrorListener.Listen(address, EMAIL, "./certs", true)
|
||||
ml, err := mirrorListener.Listen(address, EMAIL, "./certs", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return limitedlistener.NewLimitedListener(ml,
|
||||
limitedlistener.WithMaxConnections(500), // max concurrent
|
||||
limitedlistener.WithRateLimit(24), // per second
|
||||
), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,43 +2,13 @@ package graceful
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/go-i2p/onramp"
|
||||
)
|
||||
|
||||
var (
|
||||
Garlic, GarlicErr = onramp.NewGarlic("git-looseleaf", "127.0.0.1:7656", onramp.OPT_WIDE)
|
||||
Onion, OnionErr = onramp.NewOnion("git-looseleaf")
|
||||
metadialer "github.com/go-i2p/go-meta-dialer"
|
||||
)
|
||||
|
||||
func Dial(network, addr string) (net.Conn, error) {
|
||||
// convert the addr to a URL
|
||||
url, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// get the domain name
|
||||
domain := url.Hostname()
|
||||
// get the top-level domain
|
||||
fr := strings.Split(domain, ".")
|
||||
tld := fr[len(fr)-1]
|
||||
switch tld {
|
||||
case "i2p":
|
||||
if GarlicErr != nil {
|
||||
return nil, GarlicErr
|
||||
}
|
||||
// I2P is a special case, we need to use the garlic dialer
|
||||
return Garlic.Dial("i2p", addr)
|
||||
case "onion":
|
||||
if OnionErr != nil {
|
||||
return nil, OnionErr
|
||||
}
|
||||
// Onion is a special case, we need to use the onion dialer
|
||||
return Onion.Dial("onion", addr)
|
||||
default:
|
||||
// For everything else, we can use the default dialer
|
||||
return net.Dial(network, addr)
|
||||
if metadialer.ANON {
|
||||
metadialer.ANON = false
|
||||
}
|
||||
return metadialer.Dial(network, addr)
|
||||
}
|
||||
|
0
package-lock.json
generated
Normal file
0
package-lock.json
generated
Normal file
Reference in New Issue
Block a user