Skip to content

Commit

Permalink
Merge branch 'main' into metrics-listen
Browse files Browse the repository at this point in the history
  • Loading branch information
kradalby authored Feb 28, 2022
2 parents 06e6c29 + b1bd17f commit 6126d6d
Show file tree
Hide file tree
Showing 39 changed files with 773 additions and 2,367 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ jobs:

- name: Run Integration tests
if: steps.changed-files.outputs.any_changed == 'true'
run: go test -tags integration -timeout 30m
run: make test_integration
17 changes: 15 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# CHANGELOG

**TBD (TBD):**
**0.15.0 (2022-xx-xx):**

**0.14.0 (2022-xx-xx):**
**BREAKING**:

- Boundaries between Namespaces has been removed and all nodes can communicate by default [#357](https://github.com/juanfont/headscale/pull/357)
- To limit access between nodes, use [ACLs](./docs/acls.md).

**Changes**:

- Fix a bug were the same IP could be assigned to multiple hosts if joined in quick succession [#346](https://github.com/juanfont/headscale/pull/346)

**0.14.0 (2022-02-24):**

**UPCOMING BREAKING**:
From the **next** version (`0.15.0`), all machines will be able to communicate regardless of
Expand Down Expand Up @@ -36,6 +45,10 @@ This is a part of aligning `headscale`'s behaviour with Tailscale's upstream beh
- Add API Key support
- Enable remote control of `headscale` via CLI [docs](docs/remote-cli.md)
- Enable HTTP API (beta, subject to change)
- OpenID Connect users will be mapped per namespaces
- Each user will get its own namespace, created if it does not exist
- `oidc.domain_map` option has been removed
- `strip_email_domain` option has been added (see [config-example.yaml](./config_example.yaml))

**Changes**:

Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ test:
@go test -coverprofile=coverage.out ./...

test_integration:
go test -tags integration -timeout 30m -count=1 ./...
go test -failfast -tags integration -timeout 30m -count=1 ./...

test_integration_cli:
go test -tags integration -v integration_cli_test.go integration_common_test.go
Expand Down
86 changes: 55 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,67 @@

![ci](https://github.com/juanfont/headscale/actions/workflows/test.yml/badge.svg)

An open source, self-hosted implementation of the Tailscale coordination server.
An open source, self-hosted implementation of the Tailscale control server.

Join our [Discord](https://discord.gg/XcQxk2VHjx) server for a chat.

**Note:** Always select the same GitHub tag as the released version you use to ensure you have the correct example configuration and documentation. The `main` branch might contain unreleased changes.
**Note:** Always select the same GitHub tag as the released version you use
to ensure you have the correct example configuration and documentation.
The `main` branch might contain unreleased changes.

## Overview
## What is Tailscale

Tailscale is [a modern VPN](https://tailscale.com/) built on top of [Wireguard](https://www.wireguard.com/). It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/) between the computers of your networks - using [NAT traversal](https://tailscale.com/blog/how-nat-traversal-works/).
Tailscale is [a modern VPN](https://tailscale.com/) built on top of
[Wireguard](https://www.wireguard.com/).
It [works like an overlay network](https://tailscale.com/blog/how-tailscale-works/)
between the computers of your networks - using
[NAT traversal](https://tailscale.com/blog/how-nat-traversal-works/).

Everything in Tailscale is Open Source, except the GUI clients for proprietary OS (Windows and macOS/iOS), and the 'coordination/control server'.
Everything in Tailscale is Open Source, except the GUI clients for proprietary OS
(Windows and macOS/iOS), and the control server.

The control server works as an exchange point of Wireguard public keys for the nodes in the Tailscale network. It also assigns the IP addresses of the clients, creates the boundaries between each user, enables sharing machines between users, and exposes the advertised routes of your nodes.
The control server works as an exchange point of Wireguard public keys for the
nodes in the Tailscale network. It assigns the IP addresses of the clients,
creates the boundaries between each user, enables sharing machines between users,
and exposes the advertised routes of your nodes.

headscale implements this coordination server.
A [Tailscale network (tailnet)](https://tailscale.com/kb/1136/tailnet/) is private
network which Tailscale assigns to a user in terms of private users or an
organisations.

## Design goal

`headscale` aims to implement a self-hosted, open source alternative to the Tailscale
control server. `headscale` has a narrower scope and an instance of `headscale`
implements a _single_ Tailnet, which is typically what a single organisation, or
home/personal setup would use.

`headscale` uses terms that maps to Tailscale's control server, consult the
[glossary](./docs/glossary.md) for explainations.

## Support

If you like `headscale` and find it useful, there is sponsorship and donation buttons available in the repo.
If you like `headscale` and find it useful, there is a sponsorship and donation
buttons available in the repo.

If you would like to sponsor features, bugs or prioritisation, reach out to one of the maintainers.
If you would like to sponsor features, bugs or prioritisation, reach out to
one of the maintainers.

## Status
## Features

- [x] Base functionality (nodes can communicate with each other)
- [x] Node registration through the web flow
- [x] Network changes are relayed to the nodes
- [x] Namespaces support (~tailnets in Tailscale.com naming)
- [x] Routing (advertise & accept, including exit nodes)
- [x] Node registration via pre-auth keys (including reusable keys, and ephemeral node support)
- [x] JSON-formatted output
- [x] ACLs
- [x] Taildrop (File Sharing)
- [x] Support for alternative IP ranges in the tailnets (default Tailscale's 100.64.0.0/10)
- [x] DNS (passing DNS servers to nodes)
- [x] Single-Sign-On (via Open ID Connect)
- [x] Share nodes between namespaces
- [x] MagicDNS (see `docs/`)
- Full "base" support of Tailscale's features
- Configurable DNS
- [Split DNS](https://tailscale.com/kb/1054/dns/#using-dns-settings-in-the-admin-console)
- Node registration
- Single-Sign-On (via Open ID Connect)
- Pre authenticated key
- Taildrop (File Sharing)
- [Access control lists](https://tailscale.com/kb/1018/acls/)
- [MagicDNS](https://tailscale.com/kb/1081/magicdns)
- Support for multiple IP ranges in the tailnet
- Dual stack (IPv4 and IPv6)
- Routing advertising (including exit nodes)
- Ephemeral nodes

## Client OS support

Expand All @@ -53,10 +76,6 @@ If you would like to sponsor features, bugs or prioritisation, reach out to one
| Android | [You need to compile the client yourself](https://github.com/juanfont/headscale/issues/58#issuecomment-885255270) |
| iOS | Not yet |

## Roadmap

Suggestions/PRs welcomed!

## Running headscale

Please have a look at the documentation under [`docs/`](docs/).
Expand All @@ -68,11 +87,15 @@ Please have a look at the documentation under [`docs/`](docs/).

## Contributing

To contribute to Headscale you would need the lastest version of [Go](https://golang.org) and [Buf](https://buf.build)(Protobuf generator).
To contribute to headscale you would need the lastest version of [Go](https://golang.org)
and [Buf](https://buf.build)(Protobuf generator).

PRs and suggestions are welcome.

### Code style

To ensure we have some consistency with a growing number of contributions, this project has adopted linting and style/formatting rules:
To ensure we have some consistency with a growing number of contributions,
this project has adopted linting and style/formatting rules:

The **Go** code is linted with [`golangci-lint`](https://golangci-lint.run) and
formatted with [`golines`](https://github.com/segmentio/golines) (width 88) and
Expand All @@ -99,7 +122,8 @@ make install-protobuf-plugins

### Testing and building

Some parts of the project require the generation of Go code from Protobuf (if changes are made in `proto/`) and it must be (re-)generated with:
Some parts of the project require the generation of Go code from Protobuf
(if changes are made in `proto/`) and it must be (re-)generated with:

```shell
make generate
Expand Down
2 changes: 1 addition & 1 deletion acls.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func (h *Headscale) generateACLRules() ([]tailcfg.FilterRule, error) {
return nil, errEmptyPolicy
}

machines, err := h.ListAllMachines()
machines, err := h.ListMachines()
if err != nil {
return nil, err
}
Expand Down
5 changes: 5 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,9 @@ func (h *Headscale) handleAuthKey(
Str("func", "handleAuthKey").
Str("machine", machine.Name).
Msg("Authentication key was valid, proceeding to acquire IP addresses")

h.ipAllocationMutex.Lock()

ips, err := h.getAvailableIPs()
if err != nil {
log.Error().
Expand Down Expand Up @@ -602,6 +605,8 @@ func (h *Headscale) handleAuthKey(
machine.Registered = true
machine.RegisterMethod = RegisterMethodAuthKey
h.db.Save(&machine)

h.ipAllocationMutex.Unlock()
}

pak.Used = true
Expand Down
10 changes: 6 additions & 4 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,10 @@ type Config struct {
}

type OIDCConfig struct {
Issuer string
ClientID string
ClientSecret string
MatchMap map[string]string
Issuer string
ClientID string
ClientSecret string
StripEmaildomain bool
}

type DERPConfig struct {
Expand Down Expand Up @@ -155,6 +155,8 @@ type Headscale struct {
oidcStateCache *cache.Cache

requestedExpiryCache *cache.Cache

ipAllocationMutex sync.Mutex
}

// Look up the TLS constant relative to user-supplied TLS client
Expand Down
157 changes: 0 additions & 157 deletions cmd/headscale/cli/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,6 @@ func init() {
log.Fatalf(err.Error())
}
nodeCmd.AddCommand(deleteNodeCmd)

shareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace")
err = shareMachineCmd.MarkFlagRequired("namespace")
if err != nil {
log.Fatalf(err.Error())
}
shareMachineCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
err = shareMachineCmd.MarkFlagRequired("identifier")
if err != nil {
log.Fatalf(err.Error())
}
nodeCmd.AddCommand(shareMachineCmd)

unshareMachineCmd.Flags().StringP("namespace", "n", "", "Namespace")
err = unshareMachineCmd.MarkFlagRequired("namespace")
if err != nil {
log.Fatalf(err.Error())
}
unshareMachineCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
err = unshareMachineCmd.MarkFlagRequired("identifier")
if err != nil {
log.Fatalf(err.Error())
}
nodeCmd.AddCommand(unshareMachineCmd)
}

var nodeCmd = &cobra.Command{
Expand Down Expand Up @@ -317,139 +293,6 @@ var deleteNodeCmd = &cobra.Command{
},
}

func sharingWorker(
cmd *cobra.Command,
) (string, *v1.Machine, *v1.Namespace, error) {
output, _ := cmd.Flags().GetString("output")
namespaceStr, err := cmd.Flags().GetString("namespace")
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error getting namespace: %s", err), output)

return "", nil, nil, err
}

ctx, client, conn, cancel := getHeadscaleCLIClient()
defer cancel()
defer conn.Close()

identifier, err := cmd.Flags().GetUint64("identifier")
if err != nil {
ErrorOutput(err, fmt.Sprintf("Error converting ID to integer: %s", err), output)

return "", nil, nil, err
}

machineRequest := &v1.GetMachineRequest{
MachineId: identifier,
}

machineResponse, err := client.GetMachine(ctx, machineRequest)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting node node: %s", status.Convert(err).Message()),
output,
)

return "", nil, nil, err
}

namespaceRequest := &v1.GetNamespaceRequest{
Name: namespaceStr,
}

namespaceResponse, err := client.GetNamespace(ctx, namespaceRequest)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error getting node node: %s", status.Convert(err).Message()),
output,
)

return "", nil, nil, err
}

return output, machineResponse.GetMachine(), namespaceResponse.GetNamespace(), nil
}

var shareMachineCmd = &cobra.Command{
Use: "share",
Short: "Shares a node from the current namespace to the specified one",
Run: func(cmd *cobra.Command, args []string) {
output, machine, namespace, err := sharingWorker(cmd)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Failed to fetch namespace or machine: %s", err),
output,
)

return
}

ctx, client, conn, cancel := getHeadscaleCLIClient()
defer cancel()
defer conn.Close()

request := &v1.ShareMachineRequest{
MachineId: machine.Id,
Namespace: namespace.Name,
}

response, err := client.ShareMachine(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error sharing node: %s", status.Convert(err).Message()),
output,
)

return
}

SuccessOutput(response.Machine, "Node shared", output)
},
}

var unshareMachineCmd = &cobra.Command{
Use: "unshare",
Short: "Unshares a node from the specified namespace",
Run: func(cmd *cobra.Command, args []string) {
output, machine, namespace, err := sharingWorker(cmd)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Failed to fetch namespace or machine: %s", err),
output,
)

return
}

ctx, client, conn, cancel := getHeadscaleCLIClient()
defer cancel()
defer conn.Close()

request := &v1.UnshareMachineRequest{
MachineId: machine.Id,
Namespace: namespace.Name,
}

response, err := client.UnshareMachine(ctx, request)
if err != nil {
ErrorOutput(
err,
fmt.Sprintf("Error unsharing node: %s", status.Convert(err).Message()),
output,
)

return
}

SuccessOutput(response.Machine, "Node unshared", output)
},
}

func nodesToPtables(
currentNamespace string,
machines []*v1.Machine,
Expand Down
Loading

0 comments on commit 6126d6d

Please sign in to comment.