Skip to content

Commit

Permalink
Docs: new documentation site (#2723)
Browse files Browse the repository at this point in the history
This commit adds a VitePress build to the main repository,
aiming to ditch GitHub Wiki. Moving further, we're going to
host our own documentation site eithor on GitHub Pages or
something alike.
  • Loading branch information
BirkhoffLee authored May 15, 2023
1 parent 10dcb7a commit ca42ca2
Show file tree
Hide file tree
Showing 30 changed files with 2,477 additions and 0 deletions.
42 changes: 42 additions & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Deploy
on:
workflow_dispatch: {}
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20]
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: pnpm/action-setup@v2
with:
version: latest
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
working-directory: docs
run: pnpm install --frozen-lockfile=false
- name: Build
working-directory: docs
run: pnpm run docs:build
- uses: actions/configure-pages@v2
- uses: actions/upload-pages-artifact@v1
with:
path: docs/.vitepress/dist
- name: Deploy
id: deployment
uses: actions/deploy-pages@v1
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,14 @@ vendor

# test suite
test/config/cache*

# docs site generator
node_modules
package-lock.json
pnpm-lock.yaml

# docs site cache
docs/.vitepress/cache

# docs site build files
docs/.vitepress/dist
104 changes: 104 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { defineConfig } from 'vitepress'
import directoryTree from 'directory-tree'
import fs from 'fs'
import metadataParser from 'markdown-yaml-metadata-parser'

function getMetadataFromDoc(path: string): { sidebarTitle?: string, sidebarOrder?: number } {
const fileContents = fs.readFileSync(path, 'utf8')

return metadataParser(fileContents).metadata
}

function generateSidebarChapter(chapterDirName: string): any {
const chapterPath = `./docs/${chapterDirName}`
const tree = directoryTree(chapterPath)

if (!tree || !tree.children) {
console.error(tree)
throw new Error(`Could not genereate sidebar: invalid chapter at ${chapterPath}`)
}

let items: { sidebarOrder: number, text: string, link: string }[] = []

// Look into files in the chapter
for (const doc of tree.children) {
// make sure it's a .md file
if (doc.children || !doc.name.endsWith('.md'))
continue

const { sidebarOrder, sidebarTitle } = getMetadataFromDoc(doc.path)

if (!sidebarOrder)
throw new Error('Cannot find sidebarOrder in doc metadata: ' + doc.path)

if (!sidebarTitle)
throw new Error('Cannot find sidebarTitle in doc metadata: ' + doc.path)

items.push({
sidebarOrder,
text: sidebarTitle,
link: doc.path.replace(/^docs/, '')
})
}

items = items.sort((a, b) => a.sidebarOrder - b.sidebarOrder)

// remove dash and capitalize first character of each word as chapter title
const text = chapterDirName.split('-').join(' ').replace(/\b\w/g, l => l.toUpperCase())

return {
text,
items,
}
}

const chapters = [
'introduction',
'configuration',
'premium',
'runtime',
'advanced-usages',
].map(generateSidebarChapter)

// Override index page link
chapters[0]['items'][0]['link'] = '/'

// https://vitepress.dev/reference/site-config
export default defineConfig({
title: "Clash",
description: "Rule-based Tunnel",

themeConfig: {
outline: 'deep',

search: {
provider: 'local'
},

editLink: {
pattern: 'https://github.com/Dreamacro/clash/edit/master/docs/:path',
text: 'Edit this page on GitHub'
},

logo: '/logo.png',

// https://vitepress.dev/reference/default-theme-config
nav: [
{ text: 'Home', link: '/' },
{ text: 'Configuration', link: '/configuration/configuration-reference.md' },
{
text: 'Download',
items: [
{ text: 'Open-source Edition', link: 'https://github.com/Dreamacro/clash/releases/' },
{ text: 'Premium Edition', link: 'https://github.com/Dreamacro/clash/releases/tag/premium' },
]
}
],

socialLinks: [
{ icon: 'github', link: 'https://github.com/Dreamacro/clash' },
],

sidebar: chapters
}
})
59 changes: 59 additions & 0 deletions docs/advanced-usages/golang-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
sidebarTitle: Integrating Clash in Golang Programs
sidebarOrder: 3
---

# Integrating Clash in Golang Programs

If clash does not fit your own usage, you can use Clash in your own Golang code.

There is already basic support:

```go
package main

import (
"context"
"fmt"
"io"
"net"

"github.com/Dreamacro/clash/adapter/outbound"
"github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/listener/socks"
)

func main() {
in := make(chan constant.ConnContext, 100)
defer close(in)

l, err := socks.New("127.0.0.1:10000", in)
if err != nil {
panic(err)
}
defer l.Close()

println("listen at:", l.Address())

direct := outbound.NewDirect()

for c := range in {
conn := c
metadata := conn.Metadata()
fmt.Printf("request incoming from %s to %s\n", metadata.SourceAddress(), metadata.RemoteAddress())
go func () {
remote, err := direct.DialContext(context.Background(), metadata)
if err != nil {
fmt.Printf("dial error: %s\n", err.Error())
return
}
relay(remote, conn.Conn())
}()
}
}

func relay(l, r net.Conn) {
go io.Copy(l, r)
io.Copy(r, l)
}
```
93 changes: 93 additions & 0 deletions docs/advanced-usages/openconnect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
sidebarTitle: Rule-based OpenConnect
sidebarOrder: 2
---

# Rule-based OpenConnect

OpenConnect supports Cisco AnyConnect SSL VPN, Juniper Network Connect, Palo Alto Networks (PAN) GlobalProtect SSL VPN, Pulse Connect Secure SSL VPN, F5 BIG-IP SSL VPN, FortiGate SSL VPN and Array Networks SSL VPN.

For example, there would be a use case where your company uses Cisco AnyConnect for internal network access. Here I'll show you how you can use OpenConnect with policy routing powered by Clash.

First, [install vpn-slice](https://github.com/dlenski/vpn-slice#requirements). This tool overrides default routing table behaviour of OpenConnect. Simply saying, it stops the VPN from overriding your default routes.

Next you would have a script (let's say `tun0.sh`) similar to this:

```sh
#!/bin/bash
ANYCONNECT_HOST="vpn.example.com"
ANYCONNECT_USER="john"
ANYCONNECT_PASSWORD="foobar"
ROUTING_TABLE_ID="6667"
TUN_INTERFACE="tun0"

# Add --no-dtls if the server is in mainland China. UDP in China is choppy.
echo "$ANYCONNECT_PASSWORD" | \
openconnect \
--non-inter \
--passwd-on-stdin \
--protocol=anyconnect \
--interface $TUN_INTERFACE \
--script "vpn-slice
if [ \"\$reason\" = 'connect' ]; then
ip rule add from \$INTERNAL_IP4_ADDRESS table $ROUTING_TABLE_ID
ip route add default dev \$TUNDEV scope link table $ROUTING_TABLE_ID
elif [ \"\$reason\" = 'disconnect' ]; then
ip rule del from \$INTERNAL_IP4_ADDRESS table $ROUTING_TABLE_ID
ip route del default dev \$TUNDEV scope link table $ROUTING_TABLE_ID
fi" \
--user $ANYCONNECT_USER \
https://$ANYCONNECT_HOST
```

After that, we configure it as a systemd service. Create `/etc/systemd/system/tun0.service`:

```ini
[Unit]
Description=Cisco AnyConnect VPN
After=network-online.target
Conflicts=shutdown.target sleep.target

[Service]
Type=simple
ExecStart=/path/to/tun0.sh
KillSignal=SIGINT
Restart=always
RestartSec=3
StartLimitIntervalSec=0

[Install]
WantedBy=multi-user.target
```

Then we enable & start the service.

```shell
chmod +x /path/to/tun0.sh
systemctl daemon-reload
systemctl enable tun0
systemctl start tun0
```

From here you can look at the logs to see if it's running properly. Simple way is to look at if `tun0` interface has been created.

Similar to the Wireguard one, having an outbound to a TUN device is simple as adding a proxy group:

```yaml
proxy-groups:
- name: Cisco AnyConnect VPN
type: select
interface-name: tun0
proxies:
- DIRECT
```
... and it's ready to use! Add the desired rules:
```yaml
rules:
- DOMAIN-SUFFIX,internal.company.com,Cisco AnyConnect VPN
```
You should look at the debug level logs when something does not seem right.
40 changes: 40 additions & 0 deletions docs/advanced-usages/wireguard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
sidebarTitle: Rule-based Wireguard
sidebarOrder: 1
---

# Rule-based Wireguard

Suppose your kernel supports Wireguard and you have it enabled. The `Table` option stops _wg-quick_ from overriding default routes.

Example `wg0.conf`:

```ini
[Interface]
PrivateKey = ...
Address = 172.16.0.1/32
MTU = ...
Table = off
PostUp = ip rule add from 172.16.0.1/32 table 6666

[Peer]
AllowedIPs = 0.0.0.0/0
AllowedIPs = ::/0
PublicKey = ...
Endpoint = ...
```

Then in Clash you would only need to have a DIRECT proxy group that has a specific outbound interface:

```yaml
proxy-groups:
- name: Wireguard
type: select
interface-name: wg0
proxies:
- DIRECT
rules:
- DOMAIN,google.com,Wireguard
```
This should perform better than whereas if Clash implemented its own userspace Wireguard client. Wireguard is supported in the kernel.
Binary file added docs/assets/connection-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit ca42ca2

Please sign in to comment.