diff options
| author | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2024-11-21 15:48:24 +0100 |
|---|---|---|
| committer | jwijenbergh <jeroenwijenbergh@protonmail.com> | 2024-11-26 10:54:24 +0100 |
| commit | d6987c06f48b61352289181954f5a436c5f2379f (patch) | |
| tree | c1bfd0e08480fbb083db54d81d16ba1330d9e3af /docs | |
| parent | b701d19e8b8b45226f6cb1090dd160eea989ca7e (diff) | |
Docs: Move to Mkdocs & Codeberg pages
Diffstat (limited to 'docs')
31 files changed, 1161 insertions, 710 deletions
diff --git a/docs/book.toml b/docs/book.toml deleted file mode 100644 index b071f9b..0000000 --- a/docs/book.toml +++ /dev/null @@ -1,10 +0,0 @@ -[book] -authors = ["Jeroen Wijenbergh"] -language = "en" -multilingual = false -src = "src" -title = "eduVPN-common documentation" - -[output.html] -site-url = "/eduvpn-common/" -additional-css = ["custom.css"] diff --git a/docs/custom.css b/docs/custom.css deleted file mode 100644 index fe5455e..0000000 --- a/docs/custom.css +++ /dev/null @@ -1,6 +0,0 @@ -.statemachine { - position: relative; - width: 80vw; - left: 50%; - transform: translateX(-50%); -} diff --git a/docs/eduvpn_theme/css/screen.css b/docs/eduvpn_theme/css/screen.css new file mode 100644 index 0000000..5dedb9d --- /dev/null +++ b/docs/eduvpn_theme/css/screen.css @@ -0,0 +1,26 @@ +:root { + --accent: #e24301; +} + +div.page-footer { + text-align: center; + font-size: small; +} + +@media screen and (min-width: 800px) { + body > header { + background-image: url("../img/eduvpn_logo.png"); + background-repeat: no-repeat; + background-position: 5% 50%; + } + + @media (prefers-color-scheme: dark) { + body > header { + background-image: url("../img/eduvpn_logo_dark.png"); + } + } +} + +main a.headerlink { + margin-left: 0.15em; +} diff --git a/docs/eduvpn_theme/css/simple.css b/docs/eduvpn_theme/css/simple.css new file mode 100644 index 0000000..fd0fbdd --- /dev/null +++ b/docs/eduvpn_theme/css/simple.css @@ -0,0 +1,685 @@ +/* Global variables. */ +:root, +::backdrop { + /* Set sans-serif & mono fonts */ + --sans-font: -apple-system, BlinkMacSystemFont, "Avenir Next", Avenir, + "Nimbus Sans L", Roboto, "Noto Sans", "Segoe UI", Arial, Helvetica, + "Helvetica Neue", sans-serif; + --mono-font: Consolas, Menlo, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + --standard-border-radius: 5px; + + /* Default (light) theme */ + --bg: #fff; + --accent-bg: #f5f7ff; + --text: #212121; + --text-light: #585858; + --border: #898EA4; + --accent: #0d47a1; + --code: #d81b60; + --preformatted: #444; + --marked: #ffdd33; + --disabled: #efefef; +} + +/* Dark theme */ +@media (prefers-color-scheme: dark) { + :root, + ::backdrop { + color-scheme: dark; + --bg: #212121; + --accent-bg: #2b2b2b; + --text: #dcdcdc; + --text-light: #ababab; + --accent: #ffb300; + --code: #f06292; + --preformatted: #ccc; + --disabled: #111; + } + /* Add a bit of transparency so light media isn't so glaring in dark mode */ + img, + video { + opacity: 0.8; + } +} + +/* Reset box-sizing */ +*, *::before, *::after { + box-sizing: border-box; +} + +/* Reset default appearance */ +textarea, +select, +input, +progress { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; +} + +html { + /* Set the font globally */ + font-family: var(--sans-font); + scroll-behavior: smooth; +} + +/* Make the body a nice central block */ +body { + color: var(--text); + background-color: var(--bg); + font-size: 1.15rem; + line-height: 1.5; + display: grid; + grid-template-columns: 1fr min(60rem, 90%) 1fr; + margin: 0; +} +body > * { + grid-column: 2; +} + +/* Make the header bg full width, but the content inline with body */ +body > header { + background-color: var(--accent-bg); + border-bottom: 1px solid var(--border); + text-align: center; + padding: 0 0.5rem 2rem 0.5rem; + grid-column: 1 / -1; +} + +body > header h1 { + max-width: 1200px; + margin: 1rem auto; +} + +body > header p { + max-width: 40rem; + margin: 1rem auto; +} + +/* Add a little padding to ensure spacing is correct between content and header > nav */ +main { + padding-top: 1.5rem; +} + +body > footer { + margin-top: 4rem; + padding: 2rem 1rem 1.5rem 1rem; + color: var(--text-light); + font-size: 0.9rem; + text-align: center; + border-top: 1px solid var(--border); +} + +/* Format headers */ +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2.6rem; + margin-top: 3rem; +} + +h3 { + font-size: 2rem; + margin-top: 3rem; +} + +h4 { + font-size: 1.44rem; +} + +h5 { + font-size: 1.15rem; +} + +h6 { + font-size: 0.96rem; +} + +p { + margin: 1.5rem 0; +} + +/* Prevent long strings from overflowing container */ +p, h1, h2, h3, h4, h5, h6 { + overflow-wrap: break-word; +} + +/* Fix line height when title wraps */ +h1, +h2, +h3 { + line-height: 1.1; +} + +/* Reduce header size on mobile */ +@media only screen and (max-width: 720px) { + h1 { + font-size: 2.5rem; + } + + h2 { + font-size: 2.1rem; + } + + h3 { + font-size: 1.75rem; + } + + h4 { + font-size: 1.25rem; + } +} + +/* Format links & buttons */ +a, +a:visited { + color: var(--accent); +} + +a:hover { + text-decoration: none; +} + +button, +.button, +a.button, /* extra specificity to override a */ +input[type="submit"], +input[type="reset"], +input[type="button"], +label[type="button"] { + border: 1px solid var(--accent); + background-color: var(--accent); + color: var(--bg); + padding: 0.5rem 0.9rem; + text-decoration: none; + line-height: normal; +} + +.button[aria-disabled="true"], +input:disabled, +textarea:disabled, +select:disabled, +button[disabled] { + cursor: not-allowed; + background-color: var(--disabled); + border-color: var(--disabled); + color: var(--text-light); +} + +input[type="range"] { + padding: 0; +} + +/* Set the cursor to '?' on an abbreviation and style the abbreviation to show that there is more information underneath */ +abbr[title] { + cursor: help; + text-decoration-line: underline; + text-decoration-style: dotted; +} + +button:enabled:hover, +.button:not([aria-disabled="true"]):hover, +input[type="submit"]:enabled:hover, +input[type="reset"]:enabled:hover, +input[type="button"]:enabled:hover, +label[type="button"]:hover { + filter: brightness(1.4); + cursor: pointer; +} + +.button:focus-visible, +button:focus-visible:where(:enabled), +input:enabled:focus-visible:where( + [type="submit"], + [type="reset"], + [type="button"] +) { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +/* Format navigation */ +header > nav { + font-size: 1rem; + line-height: 2; + padding: 1rem 0 0 0; +} + +/* Use flexbox to allow items to wrap, as needed */ +header > nav ul, +header > nav ol { + align-content: space-around; + align-items: center; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + list-style-type: none; + margin: 0; + padding: 0; +} + +/* List items are inline elements, make them behave more like blocks */ +header > nav ul li, +header > nav ol li { + display: inline-block; +} + +header > nav a, +header > nav a:visited { + margin: 0 0.5rem 1rem 0.5rem; + border: 1px solid var(--border); + border-radius: var(--standard-border-radius); + color: var(--text); + display: inline-block; + padding: 0.1rem 1rem; + text-decoration: none; +} + +header > nav a:hover, +header > nav a.current, +header > nav a[aria-current="page"] { + border-color: var(--accent); + color: var(--accent); + cursor: pointer; +} + +/* Reduce nav side on mobile */ +@media only screen and (max-width: 720px) { + header > nav a { + border: none; + padding: 0; + text-decoration: underline; + line-height: 1; + } +} + +/* Consolidate box styling */ +aside, details, pre, progress { + background-color: var(--accent-bg); + border: 1px solid var(--border); + border-radius: var(--standard-border-radius); + margin-bottom: 1rem; +} + +aside { + font-size: 1rem; + width: 30%; + padding: 0 15px; + margin-inline-start: 15px; + float: right; +} +*[dir="rtl"] aside { + float: left; +} + +/* Make aside full-width on mobile */ +@media only screen and (max-width: 720px) { + aside { + width: 100%; + float: none; + margin-inline-start: 0; + } +} + +article, fieldset, dialog { + border: 1px solid var(--border); + padding: 1rem; + border-radius: var(--standard-border-radius); + margin-bottom: 1rem; +} + +article h2:first-child, +section h2:first-child { + margin-top: 1rem; +} + +section { + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); + padding: 2rem 1rem; + margin: 3rem 0; +} + +/* Don't double separators when chaining sections */ +section + section, +section:first-child { + border-top: 0; + padding-top: 0; +} + +section:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +details { + padding: 0.7rem 1rem; +} + +summary { + cursor: pointer; + font-weight: bold; + padding: 0.7rem 1rem; + margin: -0.7rem -1rem; + word-break: break-all; +} + +details[open] > summary + * { + margin-top: 0; +} + +details[open] > summary { + margin-bottom: 0.5rem; +} + +details[open] > :last-child { + margin-bottom: 0; +} + +/* Format tables */ +table { + border-collapse: collapse; + margin: 1.5rem 0; +} + +td, +th { + border: 1px solid var(--border); + text-align: start; + padding: 0.5rem; +} + +th { + background-color: var(--accent-bg); + font-weight: bold; +} + +tr:nth-child(even) { + /* Set every other cell slightly darker. Improves readability. */ + background-color: var(--accent-bg); +} + +table caption { + font-weight: bold; + margin-bottom: 0.5rem; +} + +/* Format forms */ +textarea, +select, +input, +button, +.button { + font-size: inherit; + font-family: inherit; + padding: 0.5rem; + margin-bottom: 0.5rem; + border-radius: var(--standard-border-radius); + box-shadow: none; + max-width: 100%; + display: inline-block; +} +textarea, +select, +input { + color: var(--text); + background-color: var(--bg); + border: 1px solid var(--border); +} +label { + display: block; +} +textarea:not([cols]) { + width: 100%; +} + +/* Add arrow to drop-down */ +select:not([multiple]) { + background-image: linear-gradient(45deg, transparent 49%, var(--text) 51%), + linear-gradient(135deg, var(--text) 51%, transparent 49%); + background-position: calc(100% - 15px), calc(100% - 10px); + background-size: 5px 5px, 5px 5px; + background-repeat: no-repeat; + padding-inline-end: 25px; +} +*[dir="rtl"] select:not([multiple]) { + background-position: 10px, 15px; +} + +/* checkbox and radio button style */ +input[type="checkbox"], +input[type="radio"] { + vertical-align: middle; + position: relative; + width: min-content; +} + +input[type="checkbox"] + label, +input[type="radio"] + label { + display: inline-block; +} + +input[type="radio"] { + border-radius: 100%; +} + +input[type="checkbox"]:checked, +input[type="radio"]:checked { + background-color: var(--accent); +} + +input[type="checkbox"]:checked::after { + /* Creates a rectangle with colored right and bottom borders which is rotated to look like a check mark */ + content: " "; + width: 0.18em; + height: 0.32em; + border-radius: 0; + position: absolute; + top: 0.05em; + left: 0.17em; + background-color: transparent; + border-right: solid var(--bg) 0.08em; + border-bottom: solid var(--bg) 0.08em; + font-size: 1.8em; + transform: rotate(45deg); +} +input[type="radio"]:checked::after { + /* creates a colored circle for the checked radio button */ + content: " "; + width: 0.25em; + height: 0.25em; + border-radius: 100%; + position: absolute; + top: 0.125em; + background-color: var(--bg); + left: 0.125em; + font-size: 32px; +} + +/* Makes input fields wider on smaller screens */ +@media only screen and (max-width: 720px) { + textarea, + select, + input { + width: 100%; + } +} + +/* Set a height for color input */ +input[type="color"] { + height: 2.5rem; + padding: 0.2rem; +} + +/* do not show border around file selector button */ +input[type="file"] { + border: 0; +} + +/* Misc body elements */ +hr { + border: none; + height: 1px; + background: var(--border); + margin: 1rem auto; +} + +mark { + padding: 2px 5px; + border-radius: var(--standard-border-radius); + background-color: var(--marked); + color: black; +} + +mark a { + color: #0d47a1; +} + +img, +video { + max-width: 100%; + height: auto; + border-radius: var(--standard-border-radius); +} + +figure { + margin: 0; + display: block; + overflow-x: auto; +} + +figcaption { + text-align: center; + font-size: 0.9rem; + color: var(--text-light); + margin-bottom: 1rem; +} + +blockquote { + margin-inline-start: 2rem; + margin-inline-end: 0; + margin-block: 2rem; + padding: 0.4rem 0.8rem; + border-inline-start: 0.35rem solid var(--accent); + color: var(--text-light); + font-style: italic; +} + +cite { + font-size: 0.9rem; + color: var(--text-light); + font-style: normal; +} + +dt { + color: var(--text-light); +} + +/* Use mono font for code elements */ +code, +pre, +pre span, +kbd, +samp { + font-family: var(--mono-font); + color: var(--code); +} + +kbd { + color: var(--preformatted); + border: 1px solid var(--preformatted); + border-bottom: 3px solid var(--preformatted); + border-radius: var(--standard-border-radius); + padding: 0.1rem 0.4rem; +} + +pre { + padding: 1rem 1.4rem; + max-width: 100%; + overflow: auto; + color: var(--preformatted); +} + +/* Fix embedded code within pre */ +pre code { + color: var(--preformatted); + background: none; + margin: 0; + padding: 0; +} + +/* Progress bars */ +/* Declarations are repeated because you */ +/* cannot combine vendor-specific selectors */ +progress { + width: 100%; +} + +progress:indeterminate { + background-color: var(--accent-bg); +} + +progress::-webkit-progress-bar { + border-radius: var(--standard-border-radius); + background-color: var(--accent-bg); +} + +progress::-webkit-progress-value { + border-radius: var(--standard-border-radius); + background-color: var(--accent); +} + +progress::-moz-progress-bar { + border-radius: var(--standard-border-radius); + background-color: var(--accent); + transition-property: width; + transition-duration: 0.3s; +} + +progress:indeterminate::-moz-progress-bar { + background-color: var(--accent-bg); +} + +dialog { + max-width: 40rem; + margin: auto; +} + +dialog::backdrop { + background-color: var(--bg); + opacity: 0.8; +} + +@media only screen and (max-width: 720px) { + dialog { + max-width: 100%; + margin: auto 1em; + } +} + +/* Superscript & Subscript */ +/* Prevent scripts from affecting line-height. */ +sup, sub { + vertical-align: baseline; + position: relative; +} + +sup { + top: -0.4em; +} + +sub { + top: 0.3em; +} + +/* Classes for notices */ +.notice { + background: var(--accent-bg); + border: 2px solid var(--border); + border-radius: 5px; + padding: 1.5rem; + margin: 2rem 0; +} diff --git a/docs/eduvpn_theme/img/eduvpn_logo.png b/docs/eduvpn_theme/img/eduvpn_logo.png Binary files differnew file mode 100644 index 0000000..839addf --- /dev/null +++ b/docs/eduvpn_theme/img/eduvpn_logo.png diff --git a/docs/eduvpn_theme/img/eduvpn_logo_dark.png b/docs/eduvpn_theme/img/eduvpn_logo_dark.png Binary files differnew file mode 100644 index 0000000..f1cd43d --- /dev/null +++ b/docs/eduvpn_theme/img/eduvpn_logo_dark.png diff --git a/docs/eduvpn_theme/main.html b/docs/eduvpn_theme/main.html new file mode 100644 index 0000000..7e2adaa --- /dev/null +++ b/docs/eduvpn_theme/main.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <link rel="stylesheet" href="css/simple.css"> + <link rel="stylesheet" href="css/screen.css"> + <title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title> +</head> +<body> + <header> + <h1>{{ config.site_name }}</h1> +{% if config.site_description %} + <p>{{ config.site_description }}</p> +{% endif %} +{% if nav|length>0 %} + <nav> + {% for nav_item in nav %} + <a href="{{ nav_item.url|url }}">{{ nav_item.title }}</a> + {% endfor %} + </nav> +{% endif %} + </header> + <main> +{% if page.toc %} + <aside> + {% macro toc_item(item) %} + {%- if item.level <= config.theme.navigation_depth %} + <li class="nav-item" data-level="{{ item.level }}"><a href="{{ item.url }}" class="nav-link">{{ item.title }}</a> + <ul class="nav flex-column"> + {%- for child in item.children %} + {{- toc_item(child) }} + {%- endfor %} + </ul> + </li> + {%- endif %} + {%- endmacro %} + <ul> + {%- for item in page.toc %} + {{ toc_item(item) }} + {%- endfor %} + </ul> + </aside> +{% endif %} + {{ page.content }} + </main> + + <footer> + <p>{{ config.site_name }} - {{ config.site_description }}</p> + </footer> +</body> +</html> diff --git a/docs/src/api/typicalflow.md b/docs/flow.md index 2260352..2260352 100644 --- a/docs/src/api/typicalflow.md +++ b/docs/flow.md diff --git a/docs/src/api/functiondocs.md b/docs/md/apidocs.md index 501c8f7..a6f8b16 100644 --- a/docs/src/api/functiondocs.md +++ b/docs/md/apidocs.md @@ -1,35 +1,5 @@ This document was automatically generated from the exports/exports.go file -# Table of contents -- [About the API](#about-the-api) -- [Functions](#functions) - * [AddServer](#addserver) - * [CalculateGateway](#calculategateway) - * [Cleanup](#cleanup) - * [CookieCancel](#cookiecancel) - * [CookieDelete](#cookiedelete) - * [CookieNew](#cookienew) - * [CookieReply](#cookiereply) - * [CurrentServer](#currentserver) - * [Deregister](#deregister) - * [DiscoOrganizations](#discoorganizations) - * [DiscoServers](#discoservers) - * [DiscoveryStartup](#discoverystartup) - * [ExpiryTimes](#expirytimes) - * [FreeString](#freestring) - * [GetConfig](#getconfig) - * [InState](#instate) - * [Register](#register) - * [RemoveServer](#removeserver) - * [RenewSession](#renewsession) - * [ServerList](#serverlist) - * [SetProfileID](#setprofileid) - * [SetSecureLocation](#setsecurelocation) - * [SetState](#setstate) - * [SetTokenHandler](#settokenhandler) - * [StartFailover](#startfailover) - * [StartProxyguard](#startproxyguard) - # About the API package main implements the main exported API to be used by other languages @@ -57,9 +27,11 @@ the whole UI around it. The SetState and InState functions are useful for this # Functions ## AddServer Signature: - ```go + +```go func AddServer(c C.uintptr_t, _type C.int, id *C.char, ot *C.longlong) *C.char ``` + AddServer adds a server to the eduvpn-common server list `c` is the cookie that is used for cancellation. Create a cookie first with CookieNew. This same cookie is also used for replying to state transitions. @@ -114,9 +86,11 @@ Example Output: ## CalculateGateway Signature: - ```go + +```go func CalculateGateway(subnet *C.char) (*C.char, *C.char) ``` + CalculateGateway calculates the gateway for a subnet, it can take IPv4 or IPv6 networks with CIDR notation as inputs and returns the gateway address. @@ -131,9 +105,11 @@ Example Output: ```"10.10.0.1", null``` ## Cleanup Signature: - ```go + +```go func Cleanup(c C.uintptr_t) *C.char ``` + Cleanup sends a `/disconnect` to cleanup the connection. Additionally, if ProxyGuard is active it cancels the running process @@ -156,9 +132,11 @@ Example Output: ## CookieCancel Signature: - ```go + +```go func CookieCancel(c C.uintptr_t) *C.char ``` + CookieCancel cancels the cookie. This means that functions which take this as first argument, @@ -173,9 +151,11 @@ Example Output: null ## CookieDelete Signature: - ```go + +```go func CookieDelete(c C.uintptr_t) *C.char ``` + CookieDelete deletes the cookie by cancelling it and deleting the underlying cgo handle. @@ -186,10 +166,13 @@ Example Output: null ## CookieNew Signature: - ```go + +```go func CookieNew() C.uintptr_t ``` -CookieNew creates a new cookie and returns it. + +CookieNew creates a new cookie and returns it. Functions that take a cookie +have it as the first argument. This value should not be parsed or converted somehow by the client. This value is simply to pass back to the Go library. This value has two purposes: @@ -197,17 +180,17 @@ value is simply to pass back to the Go library. This value has two purposes: - Cancel a long running function - Send a reply to a state transition (ASK_PROFILE and ASK_LOCATION) -# Functions that take a cookie have it as the first argument - Example Input: ```CookieNew()``` Example Output: ```5``` ## CookieReply Signature: - ```go + +```go func CookieReply(c C.uintptr_t, data *C.char) *C.char ``` + CookieReply replies to a state transition using the cookie. - `c` is the Cookie @@ -219,9 +202,11 @@ Example Output: ```null``` ## CurrentServer Signature: - ```go + +```go func CurrentServer() (*C.char, *C.char) ``` + CurrentServer gets the current server from eduvpn-common In eduvpn-common, a server is marked as 'current' if you have gotten a VPN @@ -275,9 +260,11 @@ Example Output: ## Deregister Signature: - ```go + +```go func Deregister() *C.char ``` + Deregister cleans up the state for the client. This function SHOULD be called when the application exits such that the @@ -301,9 +288,11 @@ Example Output: ## DiscoOrganizations Signature: - ```go + +```go func DiscoOrganizations(c C.uintptr_t, search *C.char) (*C.char, *C.char) ``` + DiscoOrganizations gets the organizations from discovery, returned as `types/discovery/discovery.go Organizations` marshalled as JSON. @@ -367,9 +356,11 @@ Example Output: ## DiscoServers Signature: - ```go + +```go func DiscoServers(c C.uintptr_t, search *C.char) (*C.char, *C.char) ``` + DiscoServers gets the servers from discovery, returned as `types/discovery/discovery.go Servers` marshalled as JSON @@ -424,9 +415,11 @@ Example Output: ## DiscoveryStartup Signature: - ```go + +```go func DiscoveryStartup(refresh C.RefreshList) *C.char ``` + DiscoveryStartup does a discovery request in the background. - The `refresh` argument is a callback that is called when the refreshing @@ -438,9 +431,11 @@ called after calling `Register`. ## ExpiryTimes Signature: - ```go + +```go func ExpiryTimes() (*C.char, *C.char) ``` + ExpiryTimes gets the expiry times for the current server Expiry times are just fields that represent unix timestamps at which to @@ -467,9 +462,11 @@ Example Output (1...4 are unix timestamps): ## FreeString Signature: - ```go + +```go func FreeString(addr *C.char) ``` + FreeString frees a string that was allocated by the eduvpn-common Go library. @@ -481,9 +478,11 @@ Example Input: ```FreeString(strPtr)``` ## GetConfig Signature: - ```go + +```go func GetConfig(c C.uintptr_t, _type C.int, id *C.char, pTCP C.int, startup C.int) (*C.char, *C.char) ``` + GetConfig gets a configuration for the server. It returns additional information in case WireGuard over Proxyguard is used (see the last example) @@ -607,18 +606,72 @@ Example Output (3=WireGuard + Proxyguard): ## InState Signature: - ```go + +```go func InState(fsmState C.int) (C.int, *C.char) ``` + InState checks if the FSM is in `fsmState`. Example Input: ```InState(5)``` Example Output: ```1, null``` +## NewProxyguard +Signature: + +```go +func NewProxyguard(c C.uintptr_t, lp C.int, tcpsp C.int, peer *C.char, proxySetup C.ProxySetup) (C.uintptr_t, *C.char) +``` + +NewProxyguard creates the 'proxyguard' procedure in eduvpn-common. +eduvpn-common currently also cleans up the running ProxyGuard process in +`cleanup`. If the proxy cannot be created it returns an error. + +This function proxies WireGuard UDP connections over HTTP: [ProxyGuard on +Codeberg](https://codeberg.org/eduvpn/proxyguard). + +These input variables can be gotten from the configuration that is retrieved +using the `proxy` JSON key + + - `c` is the cookie. Note that if you cancel/delete the cookie, + ProxyGuard gets cleaned up. Common automatically cleans up ProxyGuard + when `Cleanup` is called, but it is good to cleanup yourself too. + - `lp` is the `port` of the local udp ProxyGuard connection, this is what + is set to the WireGuard endpoint + - `tcpsp` is the TCP source port. Pass 0 if you do not route based on + source port, so far only the Linux client has to pass non-zero. + - `peer` is the `ip:port` of the remote server + - `proxySetup` is a callback which is called when the socket is setting + up, this can be used for configuring routing in the client. It takes + two arguments: the file descriptor (integer) and a JSON list of IPs the + client connects to + +Example Input: ```StartProxyGuard(myCookie, 1337, 0, "5.5.5.5:51820", +proxySetupCB)``` + +Example Output: ```null``` + +## ProxyguardPeerIPs +Signature: + +```go +func ProxyguardPeerIPs(proxyH C.uintptr_t) (*C.char, *C.char) +``` + + +## ProxyguardTunnel +Signature: + +```go +func ProxyguardTunnel(c C.uintptr_t, proxyH C.uintptr_t, wglisten C.int) *C.char +``` + + ## Register Signature: - ```go + +```go func Register( name *C.char, version *C.char, @@ -627,6 +680,7 @@ func Register( debug C.int, ) *C.char ``` + Register creates a new client and also registers the FSM to go to the initial state @@ -670,9 +724,11 @@ Example Output: ## RemoveServer Signature: - ```go + +```go func RemoveServer(_type C.int, id *C.char) *C.char ``` + RemoveServer removes a server from the eduvpn-common server list `_type` is the type of server that needs to be added. This type is defined @@ -700,9 +756,11 @@ Example Output: ## RenewSession Signature: - ```go + +```go func RenewSession(c C.uintptr_t) *C.char ``` + RenewSession renews the session of the VPN This essentially means that the OAuth tokens are deleted. And it also @@ -723,9 +781,11 @@ Example Output: ## ServerList Signature: - ```go + +```go func ServerList() (*C.char, *C.char) ``` + ServerList gets the list of servers that are currently added This is NOT the discovery list, but the servers that have previously been @@ -759,9 +819,11 @@ Example Output (current profile here is empty as none has been chosen yet): ## SetProfileID Signature: - ```go + +```go func SetProfileID(data *C.char) *C.char ``` + SetProfileID sets the profile ID of the current serrver. This MUST only be called if the user/client wishes to manually set a profile @@ -783,9 +845,11 @@ Example Output: ## SetSecureLocation Signature: - ```go + +```go func SetSecureLocation(orgID *C.char, cc *C.char) *C.char ``` + SetSecureLocation sets the location for the secure internet server if it exists. @@ -809,9 +873,11 @@ Example Output: ## SetState Signature: - ```go + +```go func SetState(fsmState C.int) *C.char ``` + SetState sets the state of the state machine. Note: this transitions the FSM into the new state without passing any data @@ -821,9 +887,11 @@ Example Output: ```null``` ## SetTokenHandler Signature: - ```go + +```go func SetTokenHandler(getter C.TokenGetter, setter C.TokenSetter) *C.char ``` + SetTokenHandler sets the token getters and token setters for OAuth. Because the data that is saved does not contain OAuth tokens for server, @@ -856,9 +924,11 @@ Example Output: ```null``` ## StartFailover Signature: - ```go + +```go func StartFailover(c C.uintptr_t, gateway *C.char, mtu C.int, readRxBytes C.ReadRxBytes) (C.int, *C.char) ``` + StartFailover starts the 'failover' procedure in eduvpn-common. Failover has one primary goal: check if the VPN can reach the gateway. @@ -883,40 +953,3 @@ myRxBytesReader)``` Example Output: ```1, null``` -## StartProxyguard -Signature: - ```go -func StartProxyguard(c C.uintptr_t, listen *C.char, tcpsp C.int, peer *C.char, proxySetup C.ProxySetup, proxyReady C.ProxyReady) *C.char -``` -StartProxyguard starts the 'proxyguard' procedure in eduvpn-common. -eduvpn-common currently also cleans up the running ProxyGuard process in -`cleanup`. If the proxy cannot be started it returns an error. - -This function proxies WireGuard UDP connections over HTTP: [ProxyGuard on -Codeberg](https://codeberg.org/eduvpn/proxyguard). - -These input variables can be gotten from the configuration that is retrieved -using the `proxy` JSON key - - - `c` is the cookie. Note that if you cancel/delete the cookie, - ProxyGuard gets cleaned up. Common automatically cleans up ProxyGuard - when `Cleanup` is called, but it is good to cleanup yourself too. - - `listen` is the `ip:port` of the local udp connection, this is what is - set to the WireGuard endpoint - - `tcpsp` is the TCP source port. Pass 0 if you do not route based on - source port, so far only the Linux client has to pass non-zero. - - `peer` is the `ip:port` of the remote server - - `proxySetup` is a callback which is called when the socket is setting - up, this can be used for configuring routing in the client. It takes - two arguments: the file descriptor (integer) and a JSON list of IPs the - client connects to - - `proxyReady` is a callback when the proxy is ready to be used. This is - only called when the client is not connected yet. Use this to determine - when the actual wireguard connection can be started. This callback - returns and takes no arguments - -Example Input: ```StartProxyGuard(myCookie, "127.0.0.1:1337", 0, -"5.5.5.5:51820", proxySetupCB, proxyReadyCB)``` - -Example Output: ```null``` - diff --git a/docs/md/building-client.md b/docs/md/building-client.md new file mode 100644 index 0000000..5c01872 --- /dev/null +++ b/docs/md/building-client.md @@ -0,0 +1,160 @@ +# Building a client + +This chapter is a high-level overview on how to use eduvpn-common and build your own eduVPN/Let's Connect! client. In this chapter, we go over the basics of how the interop between Go and language x works, say something about the architecture, explain where to find detailed API documentation, explain the state machine, give a typical flow for a client and give a follow along tutorial on building an eduVPN client using Python code. At last, we will also have a few code examples that can be used as a short reference. + +## Go <-> language X interop +Because this library is meant to be a *general* library for other clients to use that are written in different programming languages, we need to find a way to make this Go library available on each platform and codebase. The approach that we take is to build a C library from the Go library using Cgo. Cgo can have its disadvantages with performance and the constant conversion between Go and C types. To overcome those barriers, this library has the following goals (with some others noted here): + +- **Be high-level**. Functions should do as much as possible in Go. The exported API should fit in one file. Lots of low-level functions would be a constant conversion between C and Go which adds overhead +- **Move as much state to Go as possible**. For example, Go keeps track of the servers you have configured and discovery. This makes the arguments to functions simple, clients should pass simple identifiers that Go can look up in the state +- **Easy type conversion**: to convert between C and Go types, JSON is used. Whereas Protobuf, Cap'n'proto or flatbuffers are more performant, they are harder to debug, add thousands of lines of autogenerated code and are not human friendly. Using JSON, the clients can approach it the same way they would use with a server using a REST API. Another approach is to just convert from Go -> C types -> language types. This was tried in version 1 of the library, but this ended up being too much work and manual memory management +- **Make it as easy as possible for clients to manage UI and internal state**: we use a state machine that gives the clients information in which state the Go library is in, e.g. we're selecting a server profile, we're loading the server endpoints. This library is not only a layer to talk to eduVPN servers, but the whole engine for a client +- **Implement features currently not present in existing clients**: WireGuard to OpenVPN failover, WireGuard over TCP +- **Follow the official eduVPN specification** and also contribute changes when needed +- **Secure**: We aim to follow the latest OAuth recommendations, to not store secret data and e.g. disable OpenVPN scripts from being ran by default + +And finally the most important goal: + +- **The advantages that this library brings for clients should outweigh the cost of incorporating it into the codebase**. Initial versions would take more work than we get out of it. However, when each eduVPN/Let's Connect! client uses this library we should expect a net gain. New features should be easier to implement for clients by simply requiring a new eduvpn-common version and using the necessary functions + +## Architecture +In the previous section, we have already hinted a bit on the exact architecture. This section will expand upon it by giving a figure of the basic structure + +```mermaid +graph TD; +A[Go]-- Compiles to -->B[C shared library .so/.dll]; +C[Language wrapper]-- Loads -->B +Client -- Uses --> C; +``` + +As can be seen by this architecture, there is an intermediate layer between the client and the *shared* library. This wrapper eases the way of loading this library and then defining a more language specific API for it. In the eduvpn-common repo, we currently only support a Python wrapper. Clients themselves can define their own wrapper + +## Typical flow for a client +> **_NOTE:_** This uses the function names that are defined in the exports file in Go. For your own wrapper/the Python wrapper they are different. But the general flow is the same + +1. The client starts up. It calls the `Register` function that communicates with the library that it has initialized +2. It gets the list of servers using `ServerList` +3. When the user selects a server to connect to in the UI, it calls the `GetConfig` to get a VPN configuration for this server. This function transitions the state machine multiple times. The client uses these state transitions for logging or even updating the UI. The client then connects + + - New feature in eduvpn-common: Check if the VPN can reach the gateway after the client is connected by calling `StartFailover` + +4. If the client has no servers, or it wants to add a new server, the client calls `DiscoOrganizations` and `DiscoServers` to get the discovery files from the library. This even returns cached copies if the organizations or servers should not have been updated [according to the documentation](https://docs.eduvpn.org/server/v3/server-discovery.html) + + - From this discovery list, it calls `AddServer` to add the server to the internal server list of eduvpn-common. This also calls necessary state transitions, e.g. for authorizing the server. The next call to `ServerList` then has this server included + - It can then get a configuration for this server like we have explained in *step 3* + +5. When a configuration has been obtained, the internal state has changed and the client can get the current server that was configured using `CurrentServer`. `CurrentServer` can also be called after startup if a server was previously set as the current server +6. When the VPN disconnects, the client calls `Cleanup` so that the server resources are cleaned up by calling the `/disconnect` endpoint +7. A server can be removed with the `RemoveServer` function +8. When the client is done, it calls `Deregister` such that the most up to date internal state is saved to disk. Note that eduvpn-common also saves the internal state .e.g. after obtaining a VPN configuration + +## Finite state machine + +The eduvpn-common library uses a finite state machine internally to keep track of which state the client is in and to communicate data callbacks (e.g. to communicate the Authorization URL in the OAuth process to the client). + +### FSM example +The following is an example of the FSM when the client has obtained a Wireguard/OpenVPN configuration from an eduVPN server + +```mermaid +graph TD + +style Deregistered fill:cyan +Deregistered(Deregistered) -->|Register| Main + +style Main fill:white +Main(Main) -->|Deregister| Deregistered + +style Main fill:white +Main(Main) -->|Add a server| AddingServer + +style Main fill:white +Main(Main) -->|Get a VPN config| GettingConfig + +style Main fill:white +Main(Main) -->|Already connected| Connected + +style AddingServer fill:white +AddingServer(AddingServer) -->|Authorize| OAuthStarted + +style OAuthStarted fill:white +OAuthStarted(OAuthStarted) -->|Authorized| Main + +style GettingConfig fill:white +GettingConfig(GettingConfig) -->|Invalid location| AskLocation + +style GettingConfig fill:white +GettingConfig(GettingConfig) -->|Invalid or no profile| AskProfile + +style GettingConfig fill:white +GettingConfig(GettingConfig) -->|Successfully got a configuration| GotConfig + +style GettingConfig fill:white +GettingConfig(GettingConfig) -->|Authorize| OAuthStarted + +style AskLocation fill:white +AskLocation(AskLocation) -->|Location chosen| GettingConfig + +style AskProfile fill:white +AskProfile(AskProfile) -->|Profile chosen| GettingConfig + +style GotConfig fill:white +GotConfig(GotConfig) -->|Get a VPN config again| GettingConfig + +style GotConfig fill:white +GotConfig(GotConfig) -->|VPN is connecting| Connecting + +style Connecting fill:white +Connecting(Connecting) -->|VPN is connected| Connected + +style Connecting fill:white +Connecting(Connecting) -->|Cancel connecting| Disconnecting + +style Connected fill:white +Connected(Connected) -->|VPN is disconnecting| Disconnecting + +style Disconnecting fill:white +Disconnecting(Disconnecting) -->|VPN is disconnected| Disconnected + +style Disconnecting fill:white +Disconnecting(Disconnecting) -->|Cancel disconnecting| Connected + +style Disconnected fill:white +Disconnected(Disconnected) -->|Connect again| GettingConfig + +style Disconnected fill:white +Disconnected(Disconnected) -->|Renew| OAuthStarted +``` + +The current state is highlighted in the <span style="color:cyan">cyan</span> color. + +### State explanation + +For the explanation of what all the different states mean, see the [client documentation](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/client/fsm.go#L22-L58) + +### States that ask data + +In eduvpn-common, there are certain states that require attention from the client. + +- OAuth Started: A state that must be handled by the client. How a client can 'handle' this state, we will see in the next section. In this state, the client must open the webbrowser with the authorization URL to complete to OAuth process. Note that on mobile platforms, you also need to reply with the authorization URI as these platforms do not support a local callback server using 127.0.0.1 +- Ask Profile: The state that asks for a profile selection to the client. Reply to this state by using a "cookie" and the CookieReply function. What this means will be discussed in the Python client example too +- Ask Location: Same for ask profile but for selecting a secure internet location. Only called if one must be chosen, e.g. due to a selection that is no longer valid + +The rest of the states are miscellaneous states, meaning that the client can handle them however it wants to. However, it can be useful to handle most state transitions to e.g. show loading screens or for logging and debugging purposes. + +## Code examples +This chapter contains code examples that use the API + +### Go command line client +The following is an example [in the repository](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/cmd/cli/main.go). It is a command line client with the following flags +``` + -get-custom string + The url of a custom server to connect to + -get-institute string + The url of an institute to connect to + -get-secure string + Gets secure internet servers +``` +```go +{{!cmd/cli/main.go!}} +``` + diff --git a/docs/md/building.md b/docs/md/building.md new file mode 100644 index 0000000..1afd0e6 --- /dev/null +++ b/docs/md/building.md @@ -0,0 +1,80 @@ +# Building +This section contains the instruction on how to build the library and associated wrappers. We first explain how to build the Go library and then further explain the wrapper specific building process. As the Python wrapper is the only wrapper at the moment, this only consists of this wrapper language for now. + +## Building the Go library +To build the Go library, you need the dependencies for your system installed. We will go over the needed dependencies for Linux. Afterwards, we explain the basic commands to build the library. + +### Dependencies +#### Linux +To build the Go shared library using Linux you need the following dependencies: + +- [Go](https://go.dev/doc/install) 1.18 or later +- [Gcc](https://gcc.gnu.org/) +- [GNU Make](https://www.gnu.org/software/make/) +- Dependencies for the Python wrapper if you want to build that as well + +### Commands +Before we can begin building the wrapper code, we need to build the Go code as a shared library. This section will tell you how to do so. + +To build the shared library for the current platform issue the following command in the root directory: + +```bash +make +``` + +The shared library will be output in `lib/`. + +#### Cleaning +To clean build the library and wrapper, issue the following command in the root directory: + +```bash +make clean +``` + +### Note on releases +Releases are build with the go tag "release" (add flag "-tags=release") to bundle the discovery JSON files and embed them in the shared library. See the [make_release](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/make_release.sh) script on how we bundle the files. A full command without the Makefile to build this library is: + +```bash +go build -o lib/libeduvpn_common-${VERSION}.so -tags=release -buildmode=c-shared ./exports +``` + +## Python wrapper + +To build the python wrapper issue the following command (in the root directory of the eduvpn-common project): + +```bash +make -C wrappers/python +``` + +This uses the makefile in `wrappers/python/Makefile` to build the python file into a wheel placed in `wrappers/python/dist/eduvpncommon-[version]-py3-none-[platform].whl`. Where version is the version of the library and platform is your current platform. + +The wheel can be installed with `pip`: + +```bash +pip install ./wrappers/python/dist/eduvpncommon-[version]-py3-none-[platform].whl +``` + +## Notes on building for release + +To build for release, make sure you obtain the tarball artifacts in the release (ending with `.tar.xz`) at <https://codeberg.org/eduVPN/eduvpn-common/releases>. + +These are signed with minisign and gpg keys, make sure to verify these signatures using the public keys available here: <https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/keys>, they are also available externally: +- <https://app.eduvpn.org/linux/v4/deb/app+linux@eduvpn.org.asc> +- <https://git.sr.ht/~jwijenbergh/python3-eduvpn-common.rpm/tree/main/item/SOURCES/minisign-CA9409316AC93C07.pub> + +To build for release, make sure to extract the tarball, and then add `-tags=release` to the `GOFLAGS` environment variable: + +```bash +GOFLAGS="-tags=release" make +``` + +### Package formats + +We support the following additional package formats: RPM (Linux, Fedora) and Deb (Linux, Debian derivatives) + +#### Linux: RPM +The RPM files can be found on a [SourceHut Repo](https://git.sr.ht/~jwijenbergh/python3-eduvpn-common.rpm).These are then build with [builder.rpm](https://codeberg.org/eduVPN/builder.rpm). + +#### Linux: Deb +The RPM files can be found on a [SourceHut Repo](https://git.sr.ht/~jwijenbergh/python3-eduvpn-common.deb). These are then build with [nbuilder.deb](https://codeberg.org/eduVPN/nbuilder.deb). +Proceed the build like normally. diff --git a/docs/src/api/codeexamples.md b/docs/md/examples.md index f47e99f..28bfb45 100644 --- a/docs/src/api/codeexamples.md +++ b/docs/md/examples.md @@ -2,7 +2,7 @@ This chapter contains code examples that use the API ## Go command line client -The following is an example [in the repository](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/cmd/cli/main.go). It is a command line client with the following flags +The following is an example [in the repository](https://codeberg.org/eduvpn/eduvpn-common/src/branch/main/cmd/cli/main.go). It is a command line client with the following flags ``` -get-custom string The url of a custom server to connect to @@ -12,5 +12,5 @@ The following is an example [in the repository](https://codeberg.org/eduVPN/eduv Gets secure internet servers ``` ```go -{{#include ../../../cmd/cli/main.go}} +{!cmd/cli/main.go!} ``` diff --git a/docs/src/godifferences.png b/docs/md/godifferences.png Binary files differindex 916afd2..916afd2 100644 --- a/docs/src/godifferences.png +++ b/docs/md/godifferences.png diff --git a/docs/src/about.md b/docs/md/index.md index 2066dde..cca7f5b 100644 --- a/docs/src/about.md +++ b/docs/md/index.md @@ -3,7 +3,7 @@ This chapter contains background information for the library. We give a general ## EduVPN introduction eduVPN-common is a library for [eduVPN](https://www.eduvpn.org/), which is a VPN by [SURF](https://www.surf.nl) and a project by [GÉANT](https://geant.org/), for research institutes and universities. Each institute that uses eduVPN has its own server. To discover these servers and establish a VPN connection with them, eduVPN clients are used. eduVPN has clients for each common platform: -- [Android](https://github.com/eduvpn/android) +- [Android](https://codeberg.org/eduVPN/android) - [Linux](https://github.com/eduvpn/python-eduvpn-client) - [MacOS/iOS](https://github.com/eduvpn/apple) - [Windows](https://github.com/Amebis/eduVPN) diff --git a/docs/src/gettingstarted/testing.md b/docs/md/testing.md index a3ed87d..5e85131 100644 --- a/docs/src/gettingstarted/testing.md +++ b/docs/md/testing.md @@ -15,21 +15,10 @@ SERVER_URI="eduvpn.example.com" PORTAL_USER="example" PORTAL_PASS="example" make This needs [python3-selenium](https://selenium-python.readthedocs.io/) and [geckodriver](https://github.com/mozilla/geckodriver/releases) (extract and put in your `$PATH`). Note that testing with a server assumes it uses a default portal, due to it needing to click on buttons on the web page. You can add your own portal by customizing the [called Selenium script](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/selenium_eduvpn.py). -If you have [Docker](https://www.docker.com/get-started/) installed and [Docker-compose](https://docs.docker.com/compose/install/) you can use a convenient helper script which starts up two containers -- An eduVPN server for testing -- A Go container that builds and runs the test-suite - -```bash -PORTAL_USER="example" PORTAL_PASS="example" ./ci/startcompose.sh -``` -Note that this helper script also assumes you have the [OpenSSL](https://www.openssl.org/) command line tool installed. This is used to install the self-signed certificates for testing. - -This script is also used in the continuous integration, so we recommend to run this before you submit any changes. - There are other environment variables that can be used: - `OAUTH_EXPIRED_TTL`: Use this for a server which has a low OAuth access token expiry time, e.g. 10 seconds. You would then set this variable to `"10"` so that a test is ran which waits for 10 seconds for the OAuth tokens to expire -- `EDUVPN_PODCOMP`: Set this to 1 to instruct the `./ci/startcompose.sh` script to use [podman-compose](https://github.com/containers/podman-compose) if you prefer this over using docker-compose. + ## Testing the Python code To test the Python code, issue the following command in a shell (you will need dependencies for all wrappers if you do this[^1]): diff --git a/docs/mermaid-init.js b/docs/mermaid-init.js deleted file mode 100644 index faadc62..0000000 --- a/docs/mermaid-init.js +++ /dev/null @@ -1 +0,0 @@ -mermaid.initialize({startOnLoad:true, theme: 'neutral'}); diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 0000000..c723a13 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,26 @@ +site_name: Documentation +site_description: eduVPN for Linux +docs_dir: md +site_dir: site +theme: + name: null + custom_dir: eduvpn_theme + navigation_depth: 3 +nav: + - 'About': index.md + - 'Building': building.md + - 'Testing': testing.md + - 'Building a client': building-client.md + - 'API Docs': apidocs.md + - 'Examples': examples.md +use_directory_urls: False +plugins: + - mermaid2 +markdown_extensions: + - markdown_include.include: + - smarty + - sane_lists + - toc: + permalink: "#" + baselevel: 1 + toc_depth: 2-3 diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md deleted file mode 100644 index c2da7e1..0000000 --- a/docs/src/SUMMARY.md +++ /dev/null @@ -1,20 +0,0 @@ -# Summary - -- [About](./about.md) -- [Getting Started](./gettingstarted/README.md) - - [Building](./gettingstarted/building/README.md) - - [Go library](./gettingstarted/building/go.md) - - [Python wrapper](./gettingstarted/building/python.md) - - [Example from scratch](./gettingstarted/building/example.md) - - [Package Formats](./gettingstarted/building/packageformats.md) - - [Building for release](./gettingstarted/building/release.md) - - [Testing](./gettingstarted/testing.md) -- [API](./api/README.md) - - [Language Interop](./api/languageinterop.md) - - [Architecture](./api/architecture.md) - - [Typical flow](./api/typicalflow.md) - - [Where to find docs](./api/wheretofinddocs.md) - - [State Machine](./api/statemachine.md) - - [Let's build a client](./api/letsbuildaclient.md) - - [Code examples](./api/codeexamples.md) - - [Function docs](./api/functiondocs.md) diff --git a/docs/src/api/README.md b/docs/src/api/README.md deleted file mode 100644 index 64e7ee7..0000000 --- a/docs/src/api/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# API -This chapter is an introduction to the eduvpn-common API. It is meant as a high-level overview on how to use API and build your own eduVPN/Let's Connect! client. In this chapter, we *go* over the basics of how the interop between Go and language x works, say something about the architecture, explain where to find detailed API documentation, explain the state machine, give a typical flow for a client and give a follow along tutorial on building an eduVPN client using Python code. At last, we will also have a few code examples that can be used as a short reference. diff --git a/docs/src/api/architecture.md b/docs/src/api/architecture.md deleted file mode 100644 index 72f63ef..0000000 --- a/docs/src/api/architecture.md +++ /dev/null @@ -1,11 +0,0 @@ -# Architecture -In the previous section, we have already hinted a bit on the exact architecture. This section will expand upon it by giving a figure of the basic structure - -```mermaid -graph TD; -A[Go]-- Compiles to -->B[C shared library .so/.dll]; -C[Language wrapper]-- Loads -->B -Client -- Uses --> C; -``` - -As can be seen by this architecture, there is an intermediate layer between the client and the *shared* library. This wrapper eases the way of loading this library and then defining a more language specific API for it. In the eduvpn-common repo, we currently only support a Python wrapper. Clients themselves can define their own wrapper diff --git a/docs/src/api/languageinterop.md b/docs/src/api/languageinterop.md deleted file mode 100644 index 7321874..0000000 --- a/docs/src/api/languageinterop.md +++ /dev/null @@ -1,12 +0,0 @@ -# Go <-> language X interop -Because this library is meant to be a *general* library for other clients to use that are written in different programming languages, we need to find a way to make this Go library available on each platform and codebase. The approach that we take is to build a C library from the Go library using Cgo. Cgo can have its disadvantages with performance and the constant conversion between Go and C types. To overcome those barriers, this library has the following goals (with some others noted here): -- **Be high-level**. Functions should do as much as possible in Go. The exported API should fit in one file. Lots of low-level functions would be a constant conversion between C and Go which adds overhead -- **Move as much state to Go as possible**. For example, Go keeps track of the servers you have configured and discovery. This makes the arguments to functions simple, clients should pass simple identifiers that Go can look up in the state -- **Easy type conversion**: to convert between C and Go types, JSON is used. Whereas Protobuf, Cap'n'proto or flatbuffers are more performant, they are harder to debug, add thousands of lines of autogenerated code and are not human friendly. Using JSON, the clients can approach it the same way they would use with a server using a REST API. Another approach is to just convert from Go -> C types -> language types. This was tried in version 1 of the library, but this ended up being too much work and manual memory management -- **Make it as easy as possible for clients to manage UI and internal state**: we use a state machine that gives the clients information in which state the Go library is in, e.g. we're selecting a server profile, we're loading the server endpoints. This library is not only a layer to talk to eduVPN servers, but the whole engine for a client -- **Implement features currently not present in existing clients**: WireGuard to OpenVPN failover, WireGuard over TCP -- **Follow the official eduVPN specification** and also contribute changes when needed -- **Secure**: We aim to follow the latest OAuth recommendations, to not store secret data and e.g. disable OpenVPN scripts from being ran by default - -And finally the most important goal: -- **The advantages that this library brings for clients should outweigh the cost of incorporating it into the codebase**. Initial versions would take more work than we get out of it. However, when each eduVPN/Let's Connect! client uses this library we should expect a net gain. New features should be easier to implement for clients by simply requiring a new eduvpn-common version and using the necessary functions diff --git a/docs/src/api/letsbuildaclient.md b/docs/src/api/letsbuildaclient.md deleted file mode 100644 index e513157..0000000 --- a/docs/src/api/letsbuildaclient.md +++ /dev/null @@ -1,317 +0,0 @@ -# Let's build a client using Python -To begin, let's follow the flow and see if we can figure out how it works. - -## Registering - -> The client starts up. It calls the Register function that communicates with the library that it has initialized - -In Python, this works like the following: -- First import the library -```python -import eduvpn_common.main as edu - -class Transitions: - def __init__(self, common): - self.common = common - -# These arguments can be found in the docstring -# But also in the exports.go file -# For Python it's a bit different, we have split the arguments into the constructor and register -# Here we pass the client ID for OAuth, the version of the client and the directory where config files should be found -common=edu.EduVPN("org.eduvpn.app.linux", "0.0.1", "/tmp/test") - -common.register(debug=True) - -# we will come back to this later -transitions = Transitions(common) -common.register_class_callbacks(transitions) -``` - -Now after registering, we know that we have no servers configured (unless you're following this tutorial again with an existing `/tmp/test`). So we continue with step 4 - -## Discovery - -> If the client has no servers, or it wants to add a new server, the client calls `DiscoOrganizations` and `DiscoServers` to get the discovery files from the library. - -```python -# Let's get them and print them -print(common.get_disco_organizations()) -print(common.get_disco_servers()) -``` - -We get a big JSON blob, so which format is this? From the Go documentation: - -> DiscoOrganizations gets the organizations from discovery, returned as types/discovery/discovery.go Organizations marshalled as JSON - -> DiscoServers gets the servers from discovery, returned as types/discovery/discovery.go Servers marshalled as JSON - -If you follow these files, you see two structs, Servers and Organizations. These structs have json tags associated with them. You can use this structure to figure out how to parse the returned data. In case of discovery, it's very similar to the [JSON files from the discovery server](https://disco.eduvpn.org/v2). - -## Adding a server - -The next bullet point that we implement is the following: - -> From this discovery list, it calls AddServer to add the server to the internal server list of eduvpn-common. This also calls necessary state transitions, e.g. for authorizing the server. The next call to ServerList then has this server included - -The discovery servers contains a server called the demo server. Let's try to add it. To add it we need to pass the type of server we're adding. From discovery we can deduce that this is an institute access server as the JSON looks like the following: - -```json -{ - "authentication_url_template": "", - "base_url": "https://demo.eduvpn.nl/", - "display_name": { - "en": "Demo" - }, - "server_type": "institute_access", # this is why we know it is Institute Access - "support_contact": [ - "mailto:eduvpn@surf.nl" - ] -}, -``` - -From the Go documentation, we know that the identifier must be the Base URL: - -> id is the identifier of the string -> - In case of secure internet: The organization ID -> - In case of custom server: The base URL -> - In case of institute access: The base URL - - -```python -# Compare this to the Go version, the non-interactive field is optional here as it is default False -common.add_server(edu.ServerType.INSTITUTE_ACCESS, "https://demo.eduvpn.nl/") -``` - -But we get an error! -```bash -eduvpn_common.main.WrappedError: fsm failed transition from 'Chosen_Server' to 'OAuth_Started', is this required transition handled? -``` - -This is the state machine we briefly mentioned before. Some functions require that you handle certain transitions. From the Go documentation, we can find this in the documentation as well that you must handle this transition. Let's handle it in Python to open the webbrowser for the OAuth process. - -We do this with the python wrapper by defining a class of state transitions. This class was already added and registered with `register_class_callbacks`. However, there was no transition added. Let's add it -```python -import webbrowser -from eduvpn_common.event import class_state_transition -from eduvpn_common.state import State, StateType - -class Transitions: - def __init__(self, common): - self.common = common - - @class_state_transition(State.OAUTH_STARTED, StateType.ENTER) - def enter_oauth(self, old_state: State, url: str): - webbrowser.open(url) -``` - -Now if you re-rerun the whole code with this transition added, your webbrowser should open. - -Note that this state transition is essentially the same as the following code: - -```python --def handler(old: int, new: int, data: str): -- # it's 6 because https://codeberg.org/eduVPN/eduvpn-common/src/commit/b660911b5db000b43970f3754b5767bb50741360/client/fsm.go#L33 -- if new == 6: -- webbrowser.open(data) -- return True -- return False -``` - -This is the code that is passed to the Go library. It handles certain states and returns `False` (zero) if a state is not handled, `True` (non-zero) if it is. If you define your own wrapper you should build an abstraction layer that resolves to a handler similar as above. This handler should be passed as a C function to the Go library when registering. - -After you have authorized the application through the portal using the webbrowser, the server should have been added: - -```python -print(common.get_servers()) -``` - -Returns: - -```json -{ - "institute_access_servers": [ - { - "display_name": { - "en": "Demo" - }, - "identifier": "https://demo.eduvpn.nl/", - "profiles": { - "current": "" - }, - "delisted": false - } - ] -} -``` - -The format of this JSON is specified in the Go documentation: - -`(in exports/exports.go)` -> It returns the server list as a JSON string defined in types/server/server.go List - -## Obtaining a VPN configuration from the server - -The next part of the flow is: - -> When the user selects a server to connect to in the UI, it calls the GetConfig to get a VPN configuration for this server. This function transitions the state machine multiple times. The client uses these state transitions for logging or even updating the UI. The client then connects - -Let's try it, the required arguments are the same for adding a config in the Python wrapper: - -```python -print(common.get_config(edu.ServerType.INSTITUTE_ACCESS, "https://demo.eduvpn.nl")) -``` - -However, this gives an exception: - -```bash -eduvpn_common.main.WrappedError: fsm failed transition from 'Request_Config' to 'Ask_Profile', is this required transition handled? -``` - -A similar error to the OAuth error we had before. This `Ask_Profile` transition is there for the client/user to choose a profile as this server has multiple profiles defined. - -To handle this transition and thus choose a profile to continue, we must do multiple steps: -- Add the condition to the transitions class -- Parse the data that we get back -- Reply with a choice for the profile - -If we add the condition and print the data: - -```python -@class_state_transition(State.ASK_PROFILE, StateType.ENTER) -def enter_ask_profile(self, old_state: State, data: str): - print("profiles:", data) -``` - -we get back the following JSON (from the Go docs: `The data for this transition is defined in types/server/server.go RequiredAskTransition with embedded data Profiles in types/server/server.go`): - -```python -{ - "cookie": 4, - "data": { - "map": { - "internet": { - "display_name": { - "en": "Internet" - }, - "supported_protocols": [ - 1, - 2 - ] - }, - "internet-split": { - "display_name": { - "en": "No rfc1918 routes" - }, - "supported_protocols": [ - 1, - 2 - ] - } - }, - "current": "" - } -} -``` - -This thus gives you the list of profiles with a so-called "cookie". This *cookie* is used to confirm the choice to the Go library. To do so we must do the following to handle this: - -```python -import json - -# Do this inside the Transitions class -@class_state_transition(State.ASK_PROFILE, StateType.ENTER) -def enter_ask_profile(self, old_state: State, data: str): - # parse the json - json_dict = json.loads(data) - - self.common.cookie_reply(json_dict["cookie"], "internet") -``` - -If we then re-run the code, we get back the following JSON (from the Go docs: `The return data is the configuration, marshalled as JSON and defined in types/server/server.go Configuration`) - -```python -{ - "config": "the WireGuard config", - "protocol": 2, # 2 specifies WireGuard - "default_gateway": true -} -``` - -## Cleanup -The flow also mentioned: - -> When the client is done, it calls `Deregister` such that the most up to date internal state is saved to disk. Note that eduvpn-common also saves the internal state .e.g. after obtaining a VPN configuration - -Let's be a nice client and do this: - -```python -common.deregister() -``` - -If we then call any function, we get an error, so it is important that you do this on exit: - -```python -print(common.get_servers()) ->>> eduvpn_common.main.WrappedError: No state available, did you register the client? -``` - -But when we register again and then get the list of servers, the servers are retrieved from disk: - -```python -common=edu.EduVPN("org.eduvpn.app.linux", "0.0.1", "/tmp/test") -common.register(debug=True) -print(common.get_servers()) -``` - -gives - -```json -{ - "institute_access_servers": [ - { - "display_name": { - "en": "Demo" - }, - "identifier": "https://demo.eduvpn.nl/", - "profiles": { - "map": { - "internet": { - "display_name": { - "en": "Internet" - }, - "supported_protocols": [ - 1, - 2 - ] - }, - "internet-split": { - "display_name": { - "en": "No rfc1918 routes" - }, - "supported_protocols": [ - 1, - 2 - ] - } - }, - "current": "internet" - }, - "delisted": false - } - ] -} -``` - -Note the difference with the previous JSON, the profiles are now initialized because we have gotten a configuration before. - -If the `/tmp/test` directory is removed (the argument that was passed to register), we get no servers again: - -```python -import shutil -shutil.rmtree("/tmp/test") -common=edu.EduVPN("org.eduvpn.app.linux", "0.0.1", "/tmp/test") -common.register(debug=True) -print(common.get_servers()) -``` - -gives `"{}"`, an empty JSON object string diff --git a/docs/src/api/statemachine.md b/docs/src/api/statemachine.md deleted file mode 100644 index 953bf2b..0000000 --- a/docs/src/api/statemachine.md +++ /dev/null @@ -1,103 +0,0 @@ -# Finite state machine - -The eduvpn-common library uses a finite state machine internally to keep track of which state the client is in and to communicate data callbacks (e.g. to communicate the Authorization URL in the OAuth process to the client). - -## Viewing the FSM -To view the FSM in an image, register to the library with in debug mode. This -outputs the graph with a `.graph` extension in the client-specified -config directory. The format of this -graph is from [Mermaid](https://mermaid-js.github.io/mermaid/#/). You -can convert this to an image using the [Mermaid command-line client](https://github.com/mermaid-js/mermaid-cli) installed or from the Mermaid web site, the [Mermaid Live Editor](https://mermaid.live) - -## FSM example -The following is an example of the FSM when the client has obtained a Wireguard/OpenVPN configuration from an eduVPN server - -<div class="statemachine"> - -```mermaid -graph TD - -style Deregistered fill:cyan -Deregistered(Deregistered) -->|Register| Main - -style Main fill:white -Main(Main) -->|Deregister| Deregistered - -style Main fill:white -Main(Main) -->|Add a server| AddingServer - -style Main fill:white -Main(Main) -->|Get a VPN config| GettingConfig - -style Main fill:white -Main(Main) -->|Already connected| Connected - -style AddingServer fill:white -AddingServer(AddingServer) -->|Authorize| OAuthStarted - -style OAuthStarted fill:white -OAuthStarted(OAuthStarted) -->|Authorized| Main - -style GettingConfig fill:white -GettingConfig(GettingConfig) -->|Invalid location| AskLocation - -style GettingConfig fill:white -GettingConfig(GettingConfig) -->|Invalid or no profile| AskProfile - -style GettingConfig fill:white -GettingConfig(GettingConfig) -->|Successfully got a configuration| GotConfig - -style GettingConfig fill:white -GettingConfig(GettingConfig) -->|Authorize| OAuthStarted - -style AskLocation fill:white -AskLocation(AskLocation) -->|Location chosen| GettingConfig - -style AskProfile fill:white -AskProfile(AskProfile) -->|Profile chosen| GettingConfig - -style GotConfig fill:white -GotConfig(GotConfig) -->|Get a VPN config again| GettingConfig - -style GotConfig fill:white -GotConfig(GotConfig) -->|VPN is connecting| Connecting - -style Connecting fill:white -Connecting(Connecting) -->|VPN is connected| Connected - -style Connecting fill:white -Connecting(Connecting) -->|Cancel connecting| Disconnecting - -style Connected fill:white -Connected(Connected) -->|VPN is disconnecting| Disconnecting - -style Disconnecting fill:white -Disconnecting(Disconnecting) -->|VPN is disconnected| Disconnected - -style Disconnecting fill:white -Disconnecting(Disconnecting) -->|Cancel disconnecting| Connected - -style Disconnected fill:white -Disconnected(Disconnected) -->|Connect again| GettingConfig - -style Disconnected fill:white -Disconnected(Disconnected) -->|Renew| OAuthStarted -``` - -</div> - -The current state is highlighted in the <span style="color:cyan">cyan</span> color. - -## State explanation - -For the explanation of what all the different states mean, see the [client documentation](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/client/fsm.go#L14-L50) - -## States that ask data - -In eduvpn-common, there are certain states that require attention from the client. - -- OAuth Started: A state that must be handled by the client. How a client can 'handle' this state, we will see in the next section. In this state, the client must open the webbrowser with the authorization URL to complete to OAuth process. Note that on mobile platforms, you also need to reply with the authorization URI as these platforms do not support a local callback server using 127.0.0.1 -- Ask Profile: The state that asks for a profile selection to the client. Reply to this state by using a "cookie" and the CookieReply function. What this means will be discussed in the Python client example too -- Ask Location: Same for ask profile but for selecting a secure internet location. Only called if one must be chosen, e.g. due to a selection that is no longer valid - -The rest of the states are miscellaneous states, meaning that the client can handle them however it wants to. However, it can be useful to handle most state transitions to e.g. show loading screens or for logging and debugging purposes. diff --git a/docs/src/api/wheretofinddocs.md b/docs/src/api/wheretofinddocs.md deleted file mode 100644 index cda485f..0000000 --- a/docs/src/api/wheretofinddocs.md +++ /dev/null @@ -1,6 +0,0 @@ -# Where to find API docs -The API documentation, depends on which wrapper you are using. If you're writing a wrapper yourself, or want some background information on how it works internally, you can find function docs in the [exports/exports.go](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/exports/exports.go) file it is available as autogenerated form [here](./functiondocs.md). - -This file is commented using Go comment style. It gives a basic of what the function does, what it returns and what type of arguments you should pass to it. The API documentation for the Python wrapper can be [found here](https://eduvpn.github.io/eduvpn-common/api/python/rtd/index.html). - -There is also a Go API that is defined in the [client package](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/client). However, this is not the primary use case for the library. diff --git a/docs/src/gettingstarted/README.md b/docs/src/gettingstarted/README.md deleted file mode 100644 index 252c8b1..0000000 --- a/docs/src/gettingstarted/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Getting started -This chapter contains the steps to get started with the Go library and the Python wrapper. You will learn how to build the library/wrapper, how to run the test suite and how to debug possible errors. - -Note that the Python wrapper is currently the only wrapper. So for now, we only document the Python wrapper in this documentation and in the future document the other wrappers as well. - -The documentation on how to use this library in your own code will be given in the next chapter: [API](../api/index.html). diff --git a/docs/src/gettingstarted/building/README.md b/docs/src/gettingstarted/building/README.md deleted file mode 100644 index 00295bf..0000000 --- a/docs/src/gettingstarted/building/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Building -This section contains the instruction on how to build the library and associated wrappers. We first explain how to build the Go library and then further explain the wrapper specific building process. As the Python wrapper is the only wrapper at the moment, this only consists of this wrapper language for now. diff --git a/docs/src/gettingstarted/building/example.md b/docs/src/gettingstarted/building/example.md deleted file mode 100644 index 58b1302..0000000 --- a/docs/src/gettingstarted/building/example.md +++ /dev/null @@ -1,29 +0,0 @@ -# Example: commands to build for Python -This section gives an example on how to build and install the library from scratch (assuming you have all the dependencies). It builds the Go library and then builds and installs the Python wrapper. - -1. Clone the library -```bash -git clone https://codeberg.org/eduVPN/eduvpn-common -``` - -2. Go to the library directory -```bash -cd eduvpn-common -``` - -3. Build the go library -```bash -make -``` - -4. Build the python wrapper -```bash -make -C wrappers/python -``` - -5. Install the wheel using pip -```bash -# x.x.x is the version here -pip install wrappers/python/dist/eduvpncommon-x.x.x-py3-none-linux_x86_64.whl -``` -Note that the name of your wheel changes on the platform and version. diff --git a/docs/src/gettingstarted/building/go.md b/docs/src/gettingstarted/building/go.md deleted file mode 100644 index c120c0a..0000000 --- a/docs/src/gettingstarted/building/go.md +++ /dev/null @@ -1,36 +0,0 @@ -# Building the Go library -To build the Go library, you need the dependencies for your system installed. We will go over the needed dependencies for Linux. Afterwards, we explain the basic commands to build the library. - -## Dependencies -### Linux -To build the Go shared library using Linux you need the following dependencies: - -- [Go](https://go.dev/doc/install) 1.18 or later -- [Gcc](https://gcc.gnu.org/) -- [GNU Make](https://www.gnu.org/software/make/) -- Dependencies for the Python wrapper if you want to build that as well - -## Commands -Before we can begin building the wrapper code, we need to build the Go code as a shared library. This section will tell you how to do so. - -To build the shared library for the current platform issue the following command in the root directory: - -```bash -make -``` - -The shared library will be output in `lib/`. - -### Cleaning -To clean build the library and wrapper, issue the following command in the root directory: - -```bash -make clean -``` - -## Note on releases -Releases are build with the go tag "release" (add flag "-tags=release") to bundle the discovery JSON files and embed them in the shared library. See the [make_release](https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/make_release.sh) script on how we bundle the files. A full command without the Makefile to build this library is: - -```bash -go build -o lib/libeduvpn_common-${VERSION}.so -tags=release -buildmode=c-shared ./exports -``` diff --git a/docs/src/gettingstarted/building/packageformats.md b/docs/src/gettingstarted/building/packageformats.md deleted file mode 100644 index 37f5061..0000000 --- a/docs/src/gettingstarted/building/packageformats.md +++ /dev/null @@ -1,9 +0,0 @@ -# Package formats - -We support the following additional package formats: RPM (Linux, Fedora) and Deb (Linux, Debian derivatives) - -# Linux: RPM -The RPM files can be found on a [SourceHut Repo](https://git.sr.ht/~jwijenbergh/python3-eduvpn-common.rpm).These are then build with [builder.rpm](https://codeberg.org/eduVPN/builder.rpm). - -# Linux: Deb -The RPM files can be found on a [SourceHut Repo](https://git.sr.ht/~jwijenbergh/python3-eduvpn-common.deb). These are then build with [nbuilder.deb](https://codeberg.org/eduVPN/nbuilder.deb). diff --git a/docs/src/gettingstarted/building/python.md b/docs/src/gettingstarted/building/python.md deleted file mode 100644 index 03e064a..0000000 --- a/docs/src/gettingstarted/building/python.md +++ /dev/null @@ -1,15 +0,0 @@ -# Python wrapper - -To build the python wrapper issue the following command (in the root directory of the eduvpn-common project): - -```bash -make -C wrappers/python -``` - -This uses the makefile in `wrappers/python/Makefile` to build the python file into a wheel placed in `wrappers/python/dist/eduvpncommon-[version]-py3-none-[platform].whl`. Where version is the version of the library and platform is your current platform. - -The wheel can be installed with `pip`: - -```bash -pip install ./wrappers/python/dist/eduvpncommon-[version]-py3-none-[platform].whl -``` diff --git a/docs/src/gettingstarted/building/release.md b/docs/src/gettingstarted/building/release.md deleted file mode 100644 index 5c5e4e1..0000000 --- a/docs/src/gettingstarted/building/release.md +++ /dev/null @@ -1,15 +0,0 @@ -# Building for release - -To build for release, make sure you obtain the tarball artifacts in the release (ending with `.tar.xz`) at <https://codeberg.org/eduVPN/eduvpn-common/releases>. - -These are signed with minisign and gpg keys, make sure to verify these signatures using the public keys available here: <https://codeberg.org/eduVPN/eduvpn-common/src/branch/main/keys>, they are also available externally: -- <https://app.eduvpn.org/linux/v4/deb/app+linux@eduvpn.org.asc> -- <https://git.sr.ht/~jwijenbergh/python3-eduvpn-common.rpm/tree/main/item/SOURCES/minisign-CA9409316AC93C07.pub> - -To build for release, make sure to extract the tarball, and then add `-tags=release` to the `GOFLAGS` environment variable: - -```bash -GOFLAGS="-tags=release" make -``` - -Proceed the build like normally. |
