|
1 | 1 | [](https://github.com/coopernetes/jgit-proxy/actions/workflows/ci.yml) |
2 | 2 | [](https://github.com/coopernetes/jgit-proxy/actions/workflows/cve.yml) |
3 | 3 |
|
4 | | -# git-proxy in Java |
5 | | -This is a simple implementation of a git proxy in Java. This is a possible successor to [finos/git-proxy](https://github.com/finos/git-proxy) which is written in Node. |
| 4 | +# jgit-proxy |
6 | 5 |
|
7 | | -## Project Structure |
| 6 | +A Java-based git proxy implementing the same compliance and security controls as [finos/git-proxy](https://github.com/finos/git-proxy). Designed for OSS contribution gateways (employees in regulated industries contributing code to public upstream repos) and private-to-private code exchange (M&A scenarios between two or more git providers). Built on [JGit](https://github.com/eclipse-jgit/jgit), [Jetty](https://github.com/jetty/jetty.project) and [Spring](https://spring.io/). |
| 7 | + |
| 8 | +## Getting Started |
| 9 | + |
| 10 | +### Prerequisites |
| 11 | + |
| 12 | +- Java 21+ |
| 13 | +- Gradle (wrapper included) |
| 14 | + |
| 15 | +### Clone and build |
| 16 | + |
| 17 | +```shell |
| 18 | +git clone https://github.com/coopernetes/jgit-proxy.git |
| 19 | +cd jgit-proxy |
| 20 | +./gradlew build |
| 21 | +``` |
| 22 | + |
| 23 | +### Run the proxy server |
| 24 | + |
| 25 | +The standalone proxy server (no dashboard, no management API) listens on port 8080 by default: |
| 26 | + |
| 27 | +```shell |
| 28 | +./gradlew :jgit-proxy-server:run |
| 29 | +``` |
| 30 | + |
| 31 | +Logs are written to `jgit-proxy-server/logs/application.log`. Stop with: |
8 | 32 |
|
9 | | -This project is a multi-module Gradle project: |
| 33 | +```shell |
| 34 | +./gradlew :jgit-proxy-server:stop |
| 35 | +``` |
10 | 36 |
|
11 | | -### jgit-proxy-core |
12 | | -Shared library containing all proxy logic: |
13 | | -- Servlet filters for the transparent proxy path (`/proxy/...`) |
14 | | -- Store-and-forward push pipeline using JGit ReceivePack hooks (`/push/...`) |
15 | | -- Provider interfaces and implementations (GitHub, GitLab, Bitbucket) |
16 | | -- Push audit store abstraction with JDBC and MongoDB backends |
17 | | -- Git protocol utilities and commit inspection |
| 37 | +### Run the dashboard application |
18 | 38 |
|
19 | | -### jgit-proxy-jetty |
20 | | -Standalone Jetty server — the primary runnable module. No Spring dependency. |
| 39 | +The dashboard module adds a Spring MVC web UI and REST API for reviewing and approving blocked pushes: |
21 | 40 |
|
22 | 41 | ```shell |
23 | | -./gradlew :jgit-proxy-jetty:run |
24 | | -./gradlew :jgit-proxy-jetty:stop |
| 42 | +./gradlew :jgit-proxy-dashboard:run |
25 | 43 | ``` |
26 | 44 |
|
27 | | -### jgit-proxy-spring |
28 | | -Placeholder for a future Spring Boot variant. |
| 45 | +Open `http://localhost:8080/` in a browser to access the approval dashboard. Stop with: |
| 46 | + |
| 47 | +```shell |
| 48 | +./gradlew :jgit-proxy-dashboard:stop |
| 49 | +``` |
| 50 | + |
| 51 | +### Configure the proxy |
| 52 | + |
| 53 | +Configuration is YAML-based. Copy `git-proxy.yml` from `jgit-proxy-server/src/main/resources/` and create `git-proxy-local.yml` in the same directory (or `/app/conf/` for Docker). The local file takes priority over the bundled defaults. |
| 54 | + |
| 55 | +Minimal example — allow pushes to a specific GitHub repo: |
| 56 | + |
| 57 | +```yaml |
| 58 | +server: |
| 59 | + port: 8080 |
| 60 | + |
| 61 | +database: |
| 62 | + type: h2-mem # default; data lost on restart |
| 63 | + |
| 64 | +git-proxy: |
| 65 | + providers: |
| 66 | + github: |
| 67 | + enabled: true |
| 68 | + |
| 69 | + filters: |
| 70 | + whitelists: |
| 71 | + - enabled: true |
| 72 | + order: 1100 |
| 73 | + operations: [FETCH, PUSH] |
| 74 | + providers: [github] |
| 75 | + slugs: |
| 76 | + - owner/repo |
| 77 | +``` |
| 78 | +
|
| 79 | +Environment variable overrides use the `GITPROXY_` prefix: |
| 80 | +- `GITPROXY_SERVER_PORT=9090` |
| 81 | +- `GITPROXY_PROVIDERS_GITHUB_ENABLED=false` |
29 | 82 |
|
30 | 83 | ## Proxy Modes |
31 | 84 |
|
| 85 | +### URLs |
| 86 | + |
| 87 | +jgit-proxy is capable of proxying arbitrary & multiple upstream Git repositories over HTTPS. For each upstream provider (for example, https://github.com & https://gitlab.com), a distinct URL is mapped for proxying by hostname. The remainder of the URL is the specific git repository you wish to connect to. For example: |
| 88 | + |
| 89 | +- Original repository: https://github.com/finos/git-proxy |
| 90 | +- Proxy: http[s]://{jgit-proxy-server}/{proxy,push*}/github.com/finos/git-proxy |
| 91 | + |
| 92 | +This makes it simple for a developer to simply add a new [git remote](https://git-scm.com/docs/git-remote) and start pushing code through jgit-proxy. |
| 93 | + |
| 94 | +``` |
| 95 | +git clone https://github.com/finos/git-proxy && cd git-proxy |
| 96 | +git remote add proxy http://localhost:8080/push/github.com/finos/git-proxy |
| 97 | +``` |
| 98 | +
|
| 99 | +> \*Note: the base URL determines which proxying mode is in use. See below for details. |
| 100 | +
|
32 | 101 | ### Transparent proxy (`/proxy/<host>/...`) |
33 | | -HTTP request is forwarded to the upstream Git server. Servlet filters validate commits inline and reject the push before it reaches the upstream. Client receives a git sideband error message. |
| 102 | +
|
| 103 | +HTTP requests are forwarded to the upstream Git server via Jetty's `ProxyServlet`. A servlet filter chain validates commits inline and rejects the push with a git client error before it reaches the upstream. It is designed for simple proxying usage where immediate feedback is preferred and clients re-push upon resolving any validation failures. |
34 | 104 |
|
35 | 105 | ```shell |
36 | 106 | git clone http://localhost:8080/proxy/github.com/owner/repo.git |
37 | 107 | git push http://localhost:8080/proxy/github.com/owner/repo.git |
38 | 108 | ``` |
39 | 109 |
|
40 | | -### Store-and-forward push (`/push/<host>/...`) |
41 | | -Push objects are received locally using JGit's `ReceivePack`. A hook chain validates the commits and streams real-time feedback via git sideband before forwarding to the upstream. Each state transition is persisted as an event-log entry. |
| 110 | +### Store-and-forward (`/push/<host>/...`) |
| 111 | + |
| 112 | +Push objects are received locally using JGit's `ReceivePack`. A hook chain validates commits and streams real-time progress via git sideband (with real-time feedback and a nicer UX in the terminal with emoji & ANSI color support) before forwarding to the upstream. Each state transition is persisted as an event-log entry in the configured database. |
42 | 113 |
|
43 | 114 | ```shell |
44 | 115 | git clone http://localhost:8080/push/github.com/owner/repo.git |
45 | 116 | git push http://localhost:8080/push/github.com/owner/repo.git |
46 | 117 | ``` |
47 | 118 |
|
| 119 | +## Validation Features |
| 120 | + |
| 121 | +Both proxy modes enforce the same set of configurable validation rules: |
| 122 | + |
| 123 | +| Feature | Status | |
| 124 | +|---------|--------| |
| 125 | +| Repository allowlist (owner/slug) | Implemented | |
| 126 | +| Author email domain allow/block list | Implemented | |
| 127 | +| Commit message validation (literal + regex) | Implemented | |
| 128 | +| Diff generation | Implemented | |
| 129 | +| Diff content scanning | Implemented | |
| 130 | +| Aggregate failure reporting (all errors at once) | Implemented | |
| 131 | +| GPG/SSH commit signature verification | Implemented | |
| 132 | +| Approval gate with full lifecycle (RECEIVED → APPROVED → FORWARDED) | Implemented | |
| 133 | +| Real-time sideband progress with ANSI color | Implemented | |
| 134 | + |
| 135 | +## Configuration |
| 136 | + |
| 137 | +This is currently a work in progress. Similar to git-proxy, it's intended that jgit-proxy's validation & filtering functionality is fully configurable to match each organization's requirements. While certain functions are not configurable (such as parsing git payloads, enforcing git clients & setting request attributes), all the "security business logic" is intended to be fully customizable. |
| 138 | + |
| 139 | +It is part of the roadmap that these features can be externalized in multiple ways such as integrating with external APIs for approval flows, configurable and pluggable sources for permitted users/repositories/providers and other core features. |
| 140 | + |
| 141 | +You can find the [current documentation here](jgit-proxy-server/CONFIGURATION.md) but just know it is still under active development. |
| 142 | + |
48 | 143 | ## Push Audit Database |
49 | 144 |
|
50 | | -All pushes through the store-and-forward path are recorded as an event log. Each state transition (RECEIVED → APPROVED → FORWARDED, or BLOCKED/ERROR) is written as a separate row, enabling full push history and reporting. |
| 145 | +All pushes through the store-and-forward path are recorded as an event log. Each state transition (RECEIVED → APPROVED → FORWARDED, or BLOCKED/ERROR) is written as a separate row, enabling full push history and audit reporting. |
51 | 146 |
|
52 | 147 | ### Supported backends |
53 | 148 |
|
54 | 149 | | Type | Config value | Notes | |
55 | 150 | |------|-------------|-------| |
56 | | -| In-memory (simple) | `memory` | No SQL schema, data lost on restart | |
| 151 | +| In-memory | `memory` | No SQL schema, data lost on restart | |
57 | 152 | | H2 in-memory | `h2-mem` | SQL schema, data lost on restart. Default. | |
58 | 153 | | H2 file | `h2-file` | Persistent, zero external dependencies | |
59 | 154 | | SQLite | `sqlite` | Persistent, zero external dependencies | |
60 | 155 | | PostgreSQL | `postgres` | Production-grade | |
61 | | -| MongoDB | `mongo` | Compatible with git-proxy Node.js data model | |
| 156 | +| MongoDB | `mongo` | Compatible with finos/git-proxy data model | |
62 | 157 |
|
63 | 158 | ### Database configuration |
64 | 159 |
|
65 | | -Set in `git-proxy.yml` (or override in `git-proxy-local.yml`): |
66 | | - |
67 | 160 | ```yaml |
68 | 161 | # H2 in-memory (default) |
69 | 162 | database: |
@@ -102,76 +195,59 @@ docker compose up -d postgres # port 5432, Adminer on 8082 |
102 | 195 | docker compose up -d mongo # port 27017, Mongo Express on 8081 |
103 | 196 | ``` |
104 | 197 |
|
105 | | -## Configuration |
106 | | - |
107 | | -### Jetty Module |
108 | | -YAML-based configuration loaded from `git-proxy.yml` and `git-proxy-local.yml` (local file takes priority): |
109 | | - |
110 | | -```yaml |
111 | | -server: |
112 | | - port: 8080 |
| 198 | +## Project Structure |
113 | 199 |
|
114 | | -database: |
115 | | - type: h2-mem |
| 200 | +This is a multi-module Gradle project: |
116 | 201 |
|
117 | | -git-proxy: |
118 | | - providers: |
119 | | - github: |
120 | | - enabled: true |
121 | | - gitlab: |
122 | | - enabled: true |
123 | | - bitbucket: |
124 | | - enabled: true |
| 202 | +| Module | Purpose | |
| 203 | +|--------|---------| |
| 204 | +| `jgit-proxy-core` | Shared library: filter chain, JGit hooks, push store, provider model, approval abstraction | |
| 205 | +| `jgit-proxy-server` | Standalone proxy-only server — no dashboard, no Spring | |
| 206 | +| `jgit-proxy-dashboard` | Dashboard + REST API — Spring MVC, approval UI, depends on `jgit-proxy-server` | |
125 | 207 |
|
126 | | - filters: |
127 | | - whitelists: |
128 | | - - enabled: true |
129 | | - order: 1100 |
130 | | - operations: [FETCH, PUSH] |
131 | | - providers: [github] |
132 | | - slugs: |
133 | | - - owner/repo |
134 | | -``` |
| 208 | +## Roadmap |
135 | 209 |
|
136 | | -Environment variable overrides use the `GITPROXY_` prefix: |
137 | | -- `GITPROXY_SERVER_PORT=9090` |
138 | | -- `GITPROXY_PROVIDERS_GITHUB_ENABLED=false` |
| 210 | +The following gists track the project's direction and open design questions: |
139 | 211 |
|
140 | | -See [`jgit-proxy-jetty/CONFIGURATION.md`](jgit-proxy-jetty/CONFIGURATION.md) for full configuration reference. |
| 212 | +| Document | Description | |
| 213 | +|----------|-------------| |
| 214 | +| [Project vision & design](https://gist.github.com/coopernetes/d02d48efa759282ff8187da0d5dcae64) | High-level goals and priority tracks: sideband streaming UX, checkpoint-based resumption, lifecycle hooks, DAG pipeline execution, SCM OAuth integration, SSH support | |
| 215 | +| [Implementation progress](https://gist.github.com/coopernetes/3a6c83690164a8a60a10524ef24e35eb) | Feature-by-feature comparison against finos/git-proxy with current status (implemented / in-progress / gap) | |
| 216 | +| [Framework rationale](https://gist.github.com/coopernetes/626541b83a148f4ae21ae2c62c57edea) | Why Java/Jetty + JGit over Node.js/Express: native git protocol handling, in-process pack inspection, sideband streaming | |
| 217 | +| [JGit server-side abstractions](https://gist.github.com/coopernetes/96ce03ca5795ca9dc78367f064c20596) | Reference guide for `RepositoryResolver`, `ReceivePackFactory`, and pre/post-receive hooks — the building blocks of the store-and-forward pipeline | |
141 | 218 |
|
142 | 219 | ## Development |
143 | 220 |
|
144 | | -### Building |
145 | | -```shell |
146 | | -./gradlew build |
147 | | -./gradlew :jgit-proxy-jetty:build |
148 | | -``` |
| 221 | +### Build |
149 | 222 |
|
150 | | -### Running |
151 | 223 | ```shell |
152 | | -./gradlew :jgit-proxy-jetty:run # starts server (PID tracked for stop) |
153 | | -./gradlew :jgit-proxy-jetty:stop # graceful stop via PID file |
| 224 | +./gradlew build # compile + unit tests |
| 225 | +./gradlew spotlessApply # fix formatting (palantir-java-format) |
154 | 226 | ``` |
155 | 227 |
|
156 | 228 | ### Integration tests |
157 | 229 |
|
158 | | -Test scripts in the repo root exercise both proxy modes end-to-end. They require a running server and a `~/.github-pat` file with a GitHub personal access token. |
| 230 | +Test scripts in the repo root exercise both proxy modes end-to-end against a running server. They require a `~/.github-pat` file with a GitHub personal access token. |
159 | 231 |
|
160 | 232 | ```shell |
161 | 233 | # Store-and-forward mode |
162 | 234 | bash test-push-pass.sh # pushes that should succeed |
163 | | -bash test-push-fail.sh # pushes that should be rejected by validation |
| 235 | +bash test-push-fail.sh # pushes that should be rejected |
164 | 236 |
|
165 | 237 | # Transparent proxy mode |
166 | 238 | bash test-proxy-pass.sh |
167 | 239 | bash test-proxy-fail.sh |
168 | 240 | ``` |
169 | 241 |
|
170 | | -## Demo |
| 242 | +### E2E tests (Docker/Podman required) |
171 | 243 |
|
172 | | - |
| 244 | +```shell |
| 245 | +./gradlew e2eTest |
| 246 | +``` |
173 | 247 |
|
174 | | - |
| 248 | +### Docker Compose |
175 | 249 |
|
176 | | -Running the server |
177 | | - |
| 250 | +```shell |
| 251 | +docker compose up -d # jgit-proxy + Gitea (h2-mem database) |
| 252 | +bash docker/setup.sh # one-time: create admin user + test repo in Gitea |
| 253 | +``` |
0 commit comments