mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-26 08:53:05 +00:00
Compare commits
16 commits
b25c2c7f09
...
aa85454185
Author | SHA1 | Date | |
---|---|---|---|
|
aa85454185 | ||
|
59f47913fe | ||
|
f5feff7c22 | ||
|
5fbf3f8327 | ||
|
6275399327 | ||
|
29119bb7f4 | ||
|
93ba21ede5 | ||
|
a7874af3d0 | ||
|
e7245856c5 | ||
|
2345c38e1e | ||
|
8cfaa6bdac | ||
|
4e44d57bf7 | ||
|
0089ceaf1d | ||
|
9a46c5763c | ||
|
a71a933705 | ||
|
0c98d09783 |
43 changed files with 873 additions and 518 deletions
16
.github/CODEOWNERS
vendored
16
.github/CODEOWNERS
vendored
|
@ -1,10 +1,10 @@
|
|||
* @juanfont @kradalby
|
||||
|
||||
*.md @ohdearaugustin
|
||||
*.yml @ohdearaugustin
|
||||
*.yaml @ohdearaugustin
|
||||
Dockerfile* @ohdearaugustin
|
||||
.goreleaser.yaml @ohdearaugustin
|
||||
/docs/ @ohdearaugustin
|
||||
/.github/workflows/ @ohdearaugustin
|
||||
/.github/renovate.json @ohdearaugustin
|
||||
*.md @ohdearaugustin @nblock
|
||||
*.yml @ohdearaugustin @nblock
|
||||
*.yaml @ohdearaugustin @nblock
|
||||
Dockerfile* @ohdearaugustin @nblock
|
||||
.goreleaser.yaml @ohdearaugustin @nblock
|
||||
/docs/ @ohdearaugustin @nblock
|
||||
/.github/workflows/ @ohdearaugustin @nblock
|
||||
/.github/renovate.json @ohdearaugustin @nblock
|
||||
|
|
|
@ -27,6 +27,7 @@ linters:
|
|||
- nolintlint
|
||||
- musttag # causes issues with imported libs
|
||||
- depguard
|
||||
- exportloopref
|
||||
|
||||
# We should strive to enable these:
|
||||
- wrapcheck
|
||||
|
@ -56,9 +57,14 @@ linters-settings:
|
|||
- ok
|
||||
- c
|
||||
- tt
|
||||
- tx
|
||||
- rx
|
||||
|
||||
gocritic:
|
||||
disabled-checks:
|
||||
- appendAssign
|
||||
# TODO(kradalby): Remove this
|
||||
- ifElseChain
|
||||
|
||||
nlreturn:
|
||||
block-size: 4
|
||||
|
|
|
@ -154,7 +154,7 @@ kos:
|
|||
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}.{{ .Patch }}-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}v{{ .Major }}-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}stable{{ else }}unstable-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}stable-debug{{ else }}unstable-debug{{ end }}"
|
||||
- "{{ .Tag }}-debug"
|
||||
- '{{ trimprefix .Tag "v" }}-debug'
|
||||
- "sha-{{ .ShortCommit }}-debug"
|
||||
|
@ -177,7 +177,7 @@ kos:
|
|||
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}.{{ .Patch }}-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}v{{ .Major }}.{{ .Minor }}-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}v{{ .Major }}-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}stable{{ else }}unstable-debug{{ end }}"
|
||||
- "{{ if not .Prerelease }}stable-debug{{ else }}unstable-debug{{ end }}"
|
||||
- "{{ .Tag }}-debug"
|
||||
- '{{ trimprefix .Tag "v" }}-debug'
|
||||
- "sha-{{ .ShortCommit }}-debug"
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
.github/workflows/test-integration-v2*
|
||||
docs/about/features.md
|
||||
docs/ref/remote-cli.md
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
- Fixed processing of fields in post request in MoveNode rpc [#2179](https://github.com/juanfont/headscale/pull/2179)
|
||||
- Added conversion of 'Hostname' to 'givenName' in a node with FQDN rules applied [#2198](https://github.com/juanfont/headscale/pull/2198)
|
||||
- Fixed updating of hostname and givenName when it is updated in HostInfo [#2199](https://github.com/juanfont/headscale/pull/2199)
|
||||
- Fixed missing `stable-debug` container tag [#2232](https://github.com/juanfont/headscale/pr/2232)
|
||||
|
||||
## 0.23.0 (2024-09-18)
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ event.
|
|||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior
|
||||
may be reported to the community leaders responsible for enforcement
|
||||
at our Discord channel. All complaints
|
||||
on our [Discord server](https://discord.gg/c84AZQhmpx). All complaints
|
||||
will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and
|
||||
|
|
|
@ -28,7 +28,9 @@ ARG VERSION_GIT_HASH=""
|
|||
ENV VERSION_GIT_HASH=$VERSION_GIT_HASH
|
||||
ARG TARGETARCH
|
||||
|
||||
RUN GOARCH=$TARGETARCH go install -ldflags="\
|
||||
ARG BUILD_TAGS=""
|
||||
|
||||
RUN GOARCH=$TARGETARCH go install -tags="${BUILD_TAGS}" -ldflags="\
|
||||
-X tailscale.com/version.longStamp=$VERSION_LONG \
|
||||
-X tailscale.com/version.shortStamp=$VERSION_SHORT \
|
||||
-X tailscale.com/version.gitCommitStamp=$VERSION_GIT_HASH" \
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
An open source, self-hosted implementation of the Tailscale control server.
|
||||
|
||||
Join our [Discord](https://discord.gg/c84AZQhmpx) server for a chat.
|
||||
Join our [Discord server](https://discord.gg/c84AZQhmpx) 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.
|
||||
|
|
|
@ -13,3 +13,4 @@ headscale.
|
|||
| Android | Yes (see [docs](../usage/connect/android.md)) |
|
||||
| macOS | Yes (see [docs](../usage/connect/apple.md#macos) and `/apple` on your headscale for more information) |
|
||||
| iOS | Yes (see [docs](../usage/connect/apple.md#ios) and `/apple` on your headscale for more information) |
|
||||
| tvOS | Yes (see [docs](../usage/connect/apple.md#tvos) and `/apple` on your headscale for more information) |
|
||||
|
|
|
@ -41,13 +41,15 @@ In addition to that, you may use packages provided by the community or from dist
|
|||
[installation guide using community packages](../setup/install/community.md).
|
||||
|
||||
For convenience, we also [build Docker images with headscale](../setup/install/container.md). But **please be aware that
|
||||
we don't officially support deploying headscale using Docker**. We have a [Discord
|
||||
channel](https://discord.com/channels/896711691637780480/1070619770942148618) where you can ask for Docker-specific help
|
||||
to the community.
|
||||
we don't officially support deploying headscale using Docker**. On our [Discord server](https://discord.gg/c84AZQhmpx)
|
||||
we have a "docker-issues" channel where you can ask for Docker-specific help to the community.
|
||||
|
||||
## Why is my reverse proxy not working with headscale?
|
||||
|
||||
We don't know. We don't use reverse proxies with headscale ourselves, so we don't have any experience with them. We have [community documentation](../ref/integration/reverse-proxy.md) on how to configure various reverse proxies, and a dedicated [Discord channel](https://discord.com/channels/896711691637780480/1070619818346164324) where you can ask for help to the community.
|
||||
We don't know. We don't use reverse proxies with headscale ourselves, so we don't have any experience with them. We have
|
||||
[community documentation](../ref/integration/reverse-proxy.md) on how to configure various reverse proxies, and a
|
||||
dedicated "reverse-proxy-issues" channel on our [Discord server](https://discord.gg/c84AZQhmpx) where you can ask for
|
||||
help to the community.
|
||||
|
||||
## Can I use headscale and tailscale on the same machine?
|
||||
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
# Getting help
|
||||
|
||||
Join our Discord server for announcements and community support:
|
||||
|
||||
- [announcements](https://discord.com/channels/896711691637780480/896711692120129538)
|
||||
- [general](https://discord.com/channels/896711691637780480/896711692120129540)
|
||||
- [docker-issues](https://discord.com/channels/896711691637780480/1070619770942148618)
|
||||
- [reverse-proxy-issues](https://discord.com/channels/896711691637780480/1070619818346164324)
|
||||
- [web-interfaces](https://discord.com/channels/896711691637780480/1105842846386356294)
|
||||
Join our [Discord server](https://discord.gg/c84AZQhmpx) for announcements and community support.
|
||||
|
||||
Please report bugs via [GitHub issues](https://github.com/juanfont/headscale/issues)
|
||||
|
|
|
@ -6,5 +6,4 @@ code archives. Container images are available on [Docker Hub](https://hub.docker
|
|||
|
||||
An Atom/RSS feed of headscale releases is available [here](https://github.com/juanfont/headscale/releases.atom).
|
||||
|
||||
Join the ["announcements" channel on Discord](https://discord.com/channels/896711691637780480/896711692120129538) for
|
||||
news about headscale.
|
||||
See the "announcements" channel on our [Discord server](https://discord.gg/c84AZQhmpx) for news about headscale.
|
||||
|
|
|
@ -10,7 +10,7 @@ Headscale is an open source, self-hosted implementation of the Tailscale control
|
|||
|
||||
This page contains the documentation for the latest version of headscale. Please also check our [FAQ](./about/faq.md).
|
||||
|
||||
Join our [Discord](https://discord.gg/c84AZQhmpx) server for a chat and community support.
|
||||
Join our [Discord server](https://discord.gg/c84AZQhmpx) for a chat and community support.
|
||||
|
||||
## Design goal
|
||||
|
||||
|
|
|
@ -45,11 +45,11 @@ headscale server.
|
|||
|
||||
ACLs have to be written in [huJSON](https://github.com/tailscale/hujson).
|
||||
|
||||
When registering the servers we will need to add the flag
|
||||
`--advertise-tags=tag:<tag1>,tag:<tag2>`, and the user that is
|
||||
registering the server should be allowed to do it. Since anyone can add tags to
|
||||
a server they can register, the check of the tags is done on headscale server
|
||||
and only valid tags are applied. A tag is valid if the user that is
|
||||
When [registering the servers](../usage/getting-started.md#register-a-node) we
|
||||
will need to add the flag `--advertise-tags=tag:<tag1>,tag:<tag2>`, and the user
|
||||
that is registering the server should be allowed to do it. Since anyone can add
|
||||
tags to a server they can register, the check of the tags is done on headscale
|
||||
server and only valid tags are applied. A tag is valid if the user that is
|
||||
registering it is allowed to do it.
|
||||
|
||||
To use ACLs in headscale, you must edit your `config.yaml` file. In there you will find a `policy.path` parameter. This will need to point to your ACL file. More info on how these policies are written can be found [here](https://tailscale.com/kb/1018/acls/).
|
||||
|
|
12
docs/ref/integration/tools.md
Normal file
12
docs/ref/integration/tools.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Tools related to headscale
|
||||
|
||||
!!! warning "Community contributions"
|
||||
|
||||
This page contains community contributions. The projects listed here are not
|
||||
maintained by the headscale authors and are written by community members.
|
||||
|
||||
This page collects third-party tools and scripts related to headscale.
|
||||
|
||||
| Name | Repository Link | Description |
|
||||
| ----------------- | --------------------------------------------------------------- | ------------------------------------------------- |
|
||||
| tailscale-manager | [Github](https://github.com/singlestore-labs/tailscale-manager) | Dynamically manage Tailscale route advertisements |
|
|
@ -1,17 +1,19 @@
|
|||
# Headscale web interface
|
||||
# Web interfaces for headscale
|
||||
|
||||
!!! warning "Community contributions"
|
||||
|
||||
This page contains community contributions. The projects listed here are not
|
||||
maintained by the headscale authors and are written by community members.
|
||||
|
||||
| Name | Repository Link | Description | Status |
|
||||
| --------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------ |
|
||||
| headscale-webui | [Github](https://github.com/ifargle/headscale-webui) | A simple headscale web UI for small-scale deployments. | Alpha |
|
||||
| headscale-ui | [Github](https://github.com/gurucomputing/headscale-ui) | A web frontend for the headscale Tailscale-compatible coordination server | Alpha |
|
||||
| HeadscaleUi | [GitHub](https://github.com/simcu/headscale-ui) | A static headscale admin ui, no backend enviroment required | Alpha |
|
||||
| Headplane | [GitHub](https://github.com/tale/headplane) | An advanced Tailscale inspired frontend for headscale | Alpha |
|
||||
| headscale-admin | [Github](https://github.com/GoodiesHQ/headscale-admin) | Headscale-Admin is meant to be a simple, modern web interface for headscale | Beta |
|
||||
| ouroboros | [Github](https://github.com/yellowsink/ouroboros) | Ouroboros is designed for users to manage their own devices, rather than for admins | Stable |
|
||||
Headscale doesn't provide a built-in web interface but users may pick one from the available options.
|
||||
|
||||
You can ask for support on our dedicated [Discord channel](https://discord.com/channels/896711691637780480/1105842846386356294).
|
||||
| Name | Repository Link | Description |
|
||||
| --------------- | ------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
||||
| headscale-webui | [Github](https://github.com/ifargle/headscale-webui) | A simple headscale web UI for small-scale deployments. |
|
||||
| headscale-ui | [Github](https://github.com/gurucomputing/headscale-ui) | A web frontend for the headscale Tailscale-compatible coordination server |
|
||||
| HeadscaleUi | [GitHub](https://github.com/simcu/headscale-ui) | A static headscale admin ui, no backend enviroment required |
|
||||
| Headplane | [GitHub](https://github.com/tale/headplane) | An advanced Tailscale inspired frontend for headscale |
|
||||
| headscale-admin | [Github](https://github.com/GoodiesHQ/headscale-admin) | Headscale-Admin is meant to be a simple, modern web interface for headscale |
|
||||
| ouroboros | [Github](https://github.com/yellowsink/ouroboros) | Ouroboros is designed for users to manage their own devices, rather than for admins |
|
||||
|
||||
You can ask for support on our [Discord server](https://discord.gg/c84AZQhmpx) in the "web-interfaces" channel.
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
# Controlling headscale with remote CLI
|
||||
|
||||
This documentation has the goal of showing a user how-to set control a headscale instance
|
||||
This documentation has the goal of showing a user how-to control a headscale instance
|
||||
from a remote machine with the `headscale` command line binary.
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- A workstation to run headscale (could be Linux, macOS, other supported platforms)
|
||||
- A headscale server (version `0.13.0` or newer)
|
||||
- Access to create API keys (local access to the headscale server)
|
||||
- headscale _must_ be served over TLS/HTTPS
|
||||
- Remote access does _not_ support unencrypted traffic.
|
||||
- Port `50443` must be open in the firewall (or port overridden by `grpc_listen_addr` option)
|
||||
- A workstation to run `headscale` (any supported platform, e.g. Linux).
|
||||
- A headscale server with gRPC enabled.
|
||||
- Connections to the gRPC port (default: `50443`) are allowed.
|
||||
- Remote access requires an encrypted connection via TLS.
|
||||
- An API key to authenticate with the headscale server.
|
||||
|
||||
## Create an API key
|
||||
|
||||
We need to create an API key to authenticate our remote headscale when using it from our workstation.
|
||||
We need to create an API key to authenticate with the remote headscale server when using it from our workstation.
|
||||
|
||||
To create a API key, log into your headscale server and generate a key:
|
||||
To create an API key, log into your headscale server and generate a key:
|
||||
|
||||
```shell
|
||||
headscale apikeys create --expiration 90d
|
||||
|
@ -25,7 +24,7 @@ headscale apikeys create --expiration 90d
|
|||
Copy the output of the command and save it for later. Please note that you can not retrieve a key again,
|
||||
if the key is lost, expire the old one, and create a new key.
|
||||
|
||||
To list the keys currently assosicated with the server:
|
||||
To list the keys currently associated with the server:
|
||||
|
||||
```shell
|
||||
headscale apikeys list
|
||||
|
@ -39,7 +38,8 @@ headscale apikeys expire --prefix "<PREFIX>"
|
|||
|
||||
## Download and configure headscale
|
||||
|
||||
1. Download the latest [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases):
|
||||
1. Download the [`headscale` binary from GitHub's release page](https://github.com/juanfont/headscale/releases). Make
|
||||
sure to use the same version as on the server.
|
||||
|
||||
1. Put the binary somewhere in your `PATH`, e.g. `/usr/local/bin/headscale`
|
||||
|
||||
|
@ -49,25 +49,32 @@ headscale apikeys expire --prefix "<PREFIX>"
|
|||
chmod +x /usr/local/bin/headscale
|
||||
```
|
||||
|
||||
1. Configure the CLI through environment variables
|
||||
1. Provide the connection parameters for the remote headscale server either via a minimal YAML configuration file or via
|
||||
environment variables:
|
||||
|
||||
```shell
|
||||
export HEADSCALE_CLI_ADDRESS="<HEADSCALE ADDRESS>:<PORT>"
|
||||
export HEADSCALE_CLI_API_KEY="<API KEY FROM PREVIOUS STAGE>"
|
||||
=== "Minimal YAML configuration file"
|
||||
|
||||
```yaml
|
||||
cli:
|
||||
address: <HEADSCALE_ADDRESS>:<PORT>
|
||||
api_key: <API_KEY_FROM_PREVIOUS_STEP>
|
||||
```
|
||||
|
||||
for example:
|
||||
=== "Environment variables"
|
||||
|
||||
```shell
|
||||
export HEADSCALE_CLI_ADDRESS="headscale.example.com:50443"
|
||||
export HEADSCALE_CLI_API_KEY="abcde12345"
|
||||
export HEADSCALE_CLI_ADDRESS="<HEADSCALE_ADDRESS>:<PORT>"
|
||||
export HEADSCALE_CLI_API_KEY="<API_KEY_FROM_PREVIOUS_STEP>"
|
||||
```
|
||||
|
||||
This will tell the `headscale` binary to connect to a remote instance, instead of looking
|
||||
for a local instance (which is what it does on the server).
|
||||
!!! bug
|
||||
|
||||
The API key is needed to make sure that you are allowed to access the server. The key is _not_
|
||||
needed when running directly on the server, as the connection is local.
|
||||
Headscale 0.23.0 requires at least an empty configuration file when environment variables are used to
|
||||
specify connection details. See [issue 2193](https://github.com/juanfont/headscale/issues/2193) for more
|
||||
information.
|
||||
|
||||
This instructs the `headscale` binary to connect to a remote instance at `<HEADSCALE_ADDRESS>:<PORT>`, instead of
|
||||
connecting to the local instance.
|
||||
|
||||
1. Test the connection
|
||||
|
||||
|
@ -89,10 +96,10 @@ While this is _not a supported_ feature, an example on how this can be set up on
|
|||
|
||||
## Troubleshooting
|
||||
|
||||
Checklist:
|
||||
|
||||
- Make sure you have the _same_ headscale version on your server and workstation
|
||||
- Make sure you use version `0.13.0` or newer.
|
||||
- Verify that your TLS certificate is valid and trusted
|
||||
- If you do not have access to a trusted certificate (e.g. from Let's Encrypt), add your self signed certificate to the trust store of your OS or
|
||||
- Set `HEADSCALE_CLI_INSECURE` to 0 in your environment
|
||||
- Make sure you have the _same_ headscale version on your server and workstation.
|
||||
- Ensure that connections to the gRPC port are allowed.
|
||||
- Verify that your TLS certificate is valid and trusted.
|
||||
- If you don't have access to a trusted certificate (e.g. from Let's Encrypt), either:
|
||||
- Add your self-signed certificate to the trust store of your OS _or_
|
||||
- Disable certificate verification by either setting `cli.insecure: true` in the configuration file or by setting
|
||||
`HEADSCALE_CLI_INSECURE=1` via an environment variable. We do **not** recommend to disable certificate validation.
|
||||
|
|
|
@ -9,6 +9,8 @@ tls_cert_path: ""
|
|||
tls_key_path: ""
|
||||
```
|
||||
|
||||
The certificate should contain the full chain, else some clients, like the Tailscale Android client, will reject it.
|
||||
|
||||
## Let's Encrypt / ACME
|
||||
|
||||
To get a certificate automatically via [Let's Encrypt](https://letsencrypt.org/), set `tls_letsencrypt_hostname` to the desired certificate hostname. This name must resolve to the IP address(es) headscale is reachable on (i.e., it must correspond to the `server_url` configuration parameter). The certificate and Let's Encrypt account credentials will be stored in the directory configured in `tls_letsencrypt_cache_dir`. If the path is relative, it will be interpreted as relative to the directory the configuration file was read from.
|
||||
|
|
|
@ -28,7 +28,7 @@ development version.
|
|||
|
||||
## Fedora, RHEL, CentOS
|
||||
|
||||
A 3rd-party repository for various RPM based distributions is available at:
|
||||
A third-party repository for various RPM based distributions is available at:
|
||||
<https://copr.fedorainfracloud.org/coprs/jonathanspw/headscale/>. The site provides detailed setup and installation
|
||||
instructions.
|
||||
|
||||
|
|
|
@ -47,3 +47,23 @@ tailscale login --login-server <YOUR_HEADSCALE_URL>
|
|||
- Under `Custom Login Server`, select `Add Account...`
|
||||
- Enter the URL of your headscale instance (e.g `https://headscale.example.com`) and press `Add Account`
|
||||
- Follow the login procedure in the browser
|
||||
|
||||
## tvOS
|
||||
|
||||
### Installation
|
||||
|
||||
Install the official Tailscale tvOS client from the [App Store](https://apps.apple.com/app/tailscale/id1470499037).
|
||||
|
||||
!!! danger
|
||||
|
||||
**Don't** open the Tailscale App after installation!
|
||||
|
||||
### Configuring the headscale URL
|
||||
|
||||
- Open Settings (the Apple tvOS settings) > Apps > Tailscale
|
||||
- Under `ALTERNATE COORDINATION SERVER URL`, select `URL`
|
||||
- Enter the URL of your headscale instance (e.g `https://headscale.example.com`) and press `OK`
|
||||
- Return to the tvOS Home screen
|
||||
- Open Tailscale
|
||||
- Click the button `Install VPN configuration` and confirm the appearing popup by clicking the `Allow` button
|
||||
- Scan the QR code and follow the login procedure
|
||||
|
|
|
@ -9,6 +9,8 @@ This page helps you get started with headscale and provides a few usage examples
|
|||
installation instructions.
|
||||
* The configuration file exists and is adjusted to suit your environment, see
|
||||
[Configuration](../ref/configuration.md) for details.
|
||||
* Headscale is reachable from the Internet. Verify this by opening client specific setup instructions in your
|
||||
browser, e.g. https://headscale.example.com/windows
|
||||
* The Tailscale client is installed, see [Client and operating system support](../about/clients.md) for more
|
||||
information.
|
||||
|
||||
|
|
12
flake.lock
12
flake.lock
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1726560853,
|
||||
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1728093190,
|
||||
"narHash": "sha256-CAZF2NRuHmqTtRTNAruWpHA43Gg2UvuCNEIzabP0l6M=",
|
||||
"lastModified": 1731763621,
|
||||
"narHash": "sha256-ddcX4lQL0X05AYkrkV2LMFgGdRvgap7Ho8kgon3iWZk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e2f08f4d8b3ecb5cf5c9fd9cb2d53bb3c71807da",
|
||||
"rev": "c69a9bffbecde46b4b939465422ddc59493d3e4d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
# When updating go.mod or go.sum, a new sha will need to be calculated,
|
||||
# update this if you have a mismatch after doing a change to thos files.
|
||||
vendorHash = "sha256-CMkYTRjmhvTTrB7JbLj0cj9VEyzpG0iUWXkaOagwYTk=";
|
||||
vendorHash = "sha256-Qoqu2k4vvnbRFLmT/v8lI+HCEWqJsHFs8uZRfNmwQpo=";
|
||||
|
||||
subPackages = ["cmd/headscale"];
|
||||
|
||||
|
|
|
@ -460,6 +460,7 @@ func (h *Headscale) createRouter(grpcMux *grpcRuntime.ServeMux) *mux.Router {
|
|||
if h.cfg.DERP.ServerEnabled {
|
||||
router.HandleFunc("/derp", h.DERPServer.DERPHandler)
|
||||
router.HandleFunc("/derp/probe", derpServer.DERPProbeHandler)
|
||||
router.HandleFunc("/derp/latency-check", derpServer.DERPProbeHandler)
|
||||
router.HandleFunc("/bootstrap-dns", derpServer.DERPBootstrapDNSHandler(h.DERPMap))
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
"zgo.at/zcache/v2"
|
||||
)
|
||||
|
@ -44,7 +45,7 @@ func TestMigrations(t *testing.T) {
|
|||
routes, err := Read(h.DB, func(rx *gorm.DB) (types.Routes, error) {
|
||||
return GetRoutes(rx)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, routes, 10)
|
||||
want := types.Routes{
|
||||
|
@ -70,7 +71,7 @@ func TestMigrations(t *testing.T) {
|
|||
routes, err := Read(h.DB, func(rx *gorm.DB) (types.Routes, error) {
|
||||
return GetRoutes(rx)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, routes, 4)
|
||||
want := types.Routes{
|
||||
|
@ -132,7 +133,7 @@ func TestMigrations(t *testing.T) {
|
|||
|
||||
return append(kratest, testkra...), nil
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, keys, 5)
|
||||
want := []types.PreAuthKey{
|
||||
|
@ -177,7 +178,7 @@ func TestMigrations(t *testing.T) {
|
|||
nodes, err := Read(h.DB, func(rx *gorm.DB) (types.Nodes, error) {
|
||||
return ListNodes(rx)
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, node := range nodes {
|
||||
assert.Falsef(t, node.MachineKey.IsZero(), "expected non zero machinekey")
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/juanfont/headscale/hscontrol/types"
|
||||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/ptr"
|
||||
)
|
||||
|
@ -457,7 +458,12 @@ func TestBackfillIPAddresses(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
db := tt.dbFunc()
|
||||
|
||||
alloc, err := NewIPAllocator(db, tt.prefix4, tt.prefix6, types.IPAllocationStrategySequential)
|
||||
alloc, err := NewIPAllocator(
|
||||
db,
|
||||
tt.prefix4,
|
||||
tt.prefix6,
|
||||
types.IPAllocationStrategySequential,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set up ip alloc: %s", err)
|
||||
}
|
||||
|
@ -482,24 +488,29 @@ func TestBackfillIPAddresses(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIPAllocatorNextNoReservedIPs(t *testing.T) {
|
||||
alloc, err := NewIPAllocator(db, ptr.To(tsaddr.CGNATRange()), ptr.To(tsaddr.TailscaleULARange()), types.IPAllocationStrategySequential)
|
||||
alloc, err := NewIPAllocator(
|
||||
db,
|
||||
ptr.To(tsaddr.CGNATRange()),
|
||||
ptr.To(tsaddr.TailscaleULARange()),
|
||||
types.IPAllocationStrategySequential,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to set up ip alloc: %s", err)
|
||||
}
|
||||
|
||||
// Validate that we do not give out 100.100.100.100
|
||||
nextQuad100, err := alloc.next(na("100.100.100.99"), ptr.To(tsaddr.CGNATRange()))
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, na("100.100.100.101"), *nextQuad100)
|
||||
|
||||
// Validate that we do not give out fd7a:115c:a1e0::53
|
||||
nextQuad100v6, err := alloc.next(na("fd7a:115c:a1e0::52"), ptr.To(tsaddr.TailscaleULARange()))
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, na("fd7a:115c:a1e0::54"), *nextQuad100v6)
|
||||
|
||||
// Validate that we do not give out fd7a:115c:a1e0::53
|
||||
nextChrome, err := alloc.next(na("100.115.91.255"), ptr.To(tsaddr.CGNATRange()))
|
||||
t.Logf("chrome: %s", nextChrome.String())
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, na("100.115.94.0"), *nextChrome)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/check.v1"
|
||||
"gorm.io/gorm"
|
||||
"tailscale.com/net/tsaddr"
|
||||
|
@ -558,17 +559,17 @@ func TestAutoApproveRoutes(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
adb, err := newTestDB()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
pol, err := policy.LoadACLPolicyFromBytes([]byte(tt.acl))
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, pol)
|
||||
|
||||
user, err := adb.CreateUser("test")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
pak, err := adb.CreatePreAuthKey(user.Name, false, false, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodeKey := key.NewNode()
|
||||
machineKey := key.NewMachine()
|
||||
|
@ -590,21 +591,21 @@ func TestAutoApproveRoutes(t *testing.T) {
|
|||
}
|
||||
|
||||
trx := adb.DB.Save(&node)
|
||||
assert.NoError(t, trx.Error)
|
||||
require.NoError(t, trx.Error)
|
||||
|
||||
sendUpdate, err := adb.SaveNodeRoutes(&node)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.False(t, sendUpdate)
|
||||
|
||||
node0ByID, err := adb.GetNodeByID(0)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// TODO(kradalby): Check state update
|
||||
err = adb.EnableAutoApprovedRoutes(pol, node0ByID)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
enabledRoutes, err := adb.GetEnabledRoutes(node0ByID)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, enabledRoutes, len(tt.want))
|
||||
|
||||
tsaddr.SortPrefixes(enabledRoutes)
|
||||
|
@ -697,13 +698,13 @@ func TestListEphemeralNodes(t *testing.T) {
|
|||
}
|
||||
|
||||
user, err := db.CreateUser("test")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
pak, err := db.CreatePreAuthKey(user.Name, false, false, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
pakEph, err := db.CreatePreAuthKey(user.Name, false, true, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
node := types.Node{
|
||||
ID: 0,
|
||||
|
@ -726,16 +727,16 @@ func TestListEphemeralNodes(t *testing.T) {
|
|||
}
|
||||
|
||||
err = db.DB.Save(&node).Error
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.DB.Save(&nodeEph).Error
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes, err := db.ListNodes()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
ephemeralNodes, err := db.ListEphemeralNodes()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, nodes, 2)
|
||||
assert.Len(t, ephemeralNodes, 1)
|
||||
|
@ -753,10 +754,10 @@ func TestRenameNode(t *testing.T) {
|
|||
}
|
||||
|
||||
user, err := db.CreateUser("test")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2, err := db.CreateUser("test2")
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
node := types.Node{
|
||||
ID: 0,
|
||||
|
@ -777,10 +778,10 @@ func TestRenameNode(t *testing.T) {
|
|||
}
|
||||
|
||||
err = db.DB.Save(&node).Error
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.DB.Save(&node2).Error
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = db.DB.Transaction(func(tx *gorm.DB) error {
|
||||
_, err := RegisterNode(tx, node, nil, nil)
|
||||
|
@ -790,10 +791,10 @@ func TestRenameNode(t *testing.T) {
|
|||
_, err = RegisterNode(tx, node2, nil, nil)
|
||||
return err
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes, err := db.ListNodes()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, nodes, 2)
|
||||
|
||||
|
@ -815,26 +816,26 @@ func TestRenameNode(t *testing.T) {
|
|||
err = db.Write(func(tx *gorm.DB) error {
|
||||
return RenameNode(tx, nodes[0].ID, "newname")
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes, err = db.ListNodes()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, nodes, 2)
|
||||
assert.Equal(t, nodes[0].Hostname, "test")
|
||||
assert.Equal(t, nodes[0].GivenName, "newname")
|
||||
assert.Equal(t, "test", nodes[0].Hostname)
|
||||
assert.Equal(t, "newname", nodes[0].GivenName)
|
||||
|
||||
// Nodes can reuse name that is no longer used
|
||||
err = db.Write(func(tx *gorm.DB) error {
|
||||
return RenameNode(tx, nodes[1].ID, "test")
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes, err = db.ListNodes()
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, nodes, 2)
|
||||
assert.Equal(t, nodes[0].Hostname, "test")
|
||||
assert.Equal(t, nodes[0].GivenName, "newname")
|
||||
assert.Equal(t, nodes[1].GivenName, "test")
|
||||
assert.Equal(t, "test", nodes[0].Hostname)
|
||||
assert.Equal(t, "newname", nodes[0].GivenName)
|
||||
assert.Equal(t, "test", nodes[1].GivenName)
|
||||
|
||||
// Nodes cannot be renamed to used names
|
||||
err = db.Write(func(tx *gorm.DB) error {
|
||||
|
|
|
@ -78,7 +78,7 @@ func prometheusMiddleware(next http.Handler) http.Handler {
|
|||
|
||||
// Ignore streaming and noise sessions
|
||||
// it has its own router further down.
|
||||
if path == "/ts2021" || path == "/machine/map" || path == "/derp" || path == "/derp/probe" || path == "/bootstrap-dns" {
|
||||
if path == "/ts2021" || path == "/machine/map" || path == "/derp" || path == "/derp/probe" || path == "/derp/latency-check" || path == "/bootstrap-dns" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -488,7 +488,7 @@ func (a *AuthProviderOIDC) registerNode(
|
|||
}
|
||||
|
||||
// TODO(kradalby):
|
||||
// Rewrite in elem-go
|
||||
// Rewrite in elem-go.
|
||||
func renderOIDCCallbackTemplate(
|
||||
user *types.User,
|
||||
) (*bytes.Buffer, error) {
|
||||
|
|
|
@ -599,7 +599,7 @@ func (pol *ACLPolicy) ExpandAlias(
|
|||
// TODO(kradalby): It is quite hard to understand what this function is doing,
|
||||
// it seems like it trying to ensure that we dont include nodes that are tagged
|
||||
// when we look up the nodes owned by a user.
|
||||
// This should be refactored to be more clear as part of the Tags work in #1369
|
||||
// This should be refactored to be more clear as part of the Tags work in #1369.
|
||||
func excludeCorrectlyTaggedNodes(
|
||||
aclPolicy *ACLPolicy,
|
||||
nodes types.Nodes,
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/juanfont/headscale/hscontrol/util"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go4.org/netipx"
|
||||
"gopkg.in/check.v1"
|
||||
"tailscale.com/net/tsaddr"
|
||||
|
@ -1824,12 +1824,20 @@ func TestTheInternet(t *testing.T) {
|
|||
|
||||
for i := range internetPrefs {
|
||||
if internetPrefs[i].String() != hsExitNodeDest[i].IP {
|
||||
t.Errorf("prefix from internet set %q != hsExit list %q", internetPrefs[i].String(), hsExitNodeDest[i].IP)
|
||||
t.Errorf(
|
||||
"prefix from internet set %q != hsExit list %q",
|
||||
internetPrefs[i].String(),
|
||||
hsExitNodeDest[i].IP,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if len(internetPrefs) != len(hsExitNodeDest) {
|
||||
t.Fatalf("expected same length of prefixes, internet: %d, hsExit: %d", len(internetPrefs), len(hsExitNodeDest))
|
||||
t.Fatalf(
|
||||
"expected same length of prefixes, internet: %d, hsExit: %d",
|
||||
len(internetPrefs),
|
||||
len(hsExitNodeDest),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2036,7 +2044,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "100.64.0.100/32",
|
||||
|
@ -2049,7 +2062,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: hsExitNodeDest,
|
||||
},
|
||||
},
|
||||
|
@ -2132,7 +2150,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "100.64.0.100/32",
|
||||
|
@ -2145,7 +2168,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{IP: "0.0.0.0/5", Ports: tailcfg.PortRangeAny},
|
||||
{IP: "8.0.0.0/7", Ports: tailcfg.PortRangeAny},
|
||||
|
@ -2217,7 +2245,10 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||
User: types.User{Name: "user100"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/16"), netip.MustParsePrefix("16.0.0.0/16")},
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("8.0.0.0/16"),
|
||||
netip.MustParsePrefix("16.0.0.0/16"),
|
||||
},
|
||||
},
|
||||
},
|
||||
peers: types.Nodes{
|
||||
|
@ -2234,7 +2265,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "100.64.0.100/32",
|
||||
|
@ -2247,7 +2283,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "8.0.0.0/8",
|
||||
|
@ -2294,7 +2335,10 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
IPv6: iap("fd7a:115c:a1e0::100"),
|
||||
User: types.User{Name: "user100"},
|
||||
Hostinfo: &tailcfg.Hostinfo{
|
||||
RoutableIPs: []netip.Prefix{netip.MustParsePrefix("8.0.0.0/8"), netip.MustParsePrefix("16.0.0.0/8")},
|
||||
RoutableIPs: []netip.Prefix{
|
||||
netip.MustParsePrefix("8.0.0.0/8"),
|
||||
netip.MustParsePrefix("16.0.0.0/8"),
|
||||
},
|
||||
},
|
||||
},
|
||||
peers: types.Nodes{
|
||||
|
@ -2311,7 +2355,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
want: []tailcfg.FilterRule{
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "100.64.0.100/32",
|
||||
|
@ -2324,7 +2373,12 @@ func TestReduceFilterRules(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
SrcIPs: []string{"100.64.0.1/32", "100.64.0.2/32", "fd7a:115c:a1e0::1/128", "fd7a:115c:a1e0::2/128"},
|
||||
SrcIPs: []string{
|
||||
"100.64.0.1/32",
|
||||
"100.64.0.2/32",
|
||||
"fd7a:115c:a1e0::1/128",
|
||||
"fd7a:115c:a1e0::2/128",
|
||||
},
|
||||
DstPorts: []tailcfg.NetPortRange{
|
||||
{
|
||||
IP: "8.0.0.0/16",
|
||||
|
@ -3299,7 +3353,11 @@ func TestSSHRules(t *testing.T) {
|
|||
SSHUsers: map[string]string{
|
||||
"autogroup:nonroot": "=",
|
||||
},
|
||||
Action: &tailcfg.SSHAction{Accept: true, AllowAgentForwarding: true, AllowLocalPortForwarding: true},
|
||||
Action: &tailcfg.SSHAction{
|
||||
Accept: true,
|
||||
AllowAgentForwarding: true,
|
||||
AllowLocalPortForwarding: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
SSHUsers: map[string]string{
|
||||
|
@ -3310,7 +3368,11 @@ func TestSSHRules(t *testing.T) {
|
|||
Any: true,
|
||||
},
|
||||
},
|
||||
Action: &tailcfg.SSHAction{Accept: true, AllowAgentForwarding: true, AllowLocalPortForwarding: true},
|
||||
Action: &tailcfg.SSHAction{
|
||||
Accept: true,
|
||||
AllowAgentForwarding: true,
|
||||
AllowLocalPortForwarding: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Principals: []*tailcfg.SSHPrincipal{
|
||||
|
@ -3321,7 +3383,11 @@ func TestSSHRules(t *testing.T) {
|
|||
SSHUsers: map[string]string{
|
||||
"autogroup:nonroot": "=",
|
||||
},
|
||||
Action: &tailcfg.SSHAction{Accept: true, AllowAgentForwarding: true, AllowLocalPortForwarding: true},
|
||||
Action: &tailcfg.SSHAction{
|
||||
Accept: true,
|
||||
AllowAgentForwarding: true,
|
||||
AllowLocalPortForwarding: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
SSHUsers: map[string]string{
|
||||
|
@ -3332,7 +3398,11 @@ func TestSSHRules(t *testing.T) {
|
|||
Any: true,
|
||||
},
|
||||
},
|
||||
Action: &tailcfg.SSHAction{Accept: true, AllowAgentForwarding: true, AllowLocalPortForwarding: true},
|
||||
Action: &tailcfg.SSHAction{
|
||||
Accept: true,
|
||||
AllowAgentForwarding: true,
|
||||
AllowLocalPortForwarding: true,
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
|
@ -3392,7 +3462,7 @@ func TestSSHRules(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := tt.pol.CompileSSHPolicy(&tt.node, tt.peers)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||
t.Errorf("TestSSHRules() unexpected result (-want +got):\n%s", diff)
|
||||
|
@ -3499,7 +3569,7 @@ func TestValidExpandTagOwnersInSources(t *testing.T) {
|
|||
}
|
||||
|
||||
got, _, err := GenerateFilterAndSSHRulesForTests(pol, node, types.Nodes{})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
want := []tailcfg.FilterRule{
|
||||
{
|
||||
|
@ -3550,7 +3620,7 @@ func TestInvalidTagValidUser(t *testing.T) {
|
|||
}
|
||||
|
||||
got, _, err := GenerateFilterAndSSHRulesForTests(pol, node, types.Nodes{})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
want := []tailcfg.FilterRule{
|
||||
{
|
||||
|
@ -3609,7 +3679,7 @@ func TestValidExpandTagOwnersInDestinations(t *testing.T) {
|
|||
// c.Assert(rules[0].DstPorts[0].IP, check.Equals, "100.64.0.1/32")
|
||||
|
||||
got, _, err := GenerateFilterAndSSHRulesForTests(pol, node, types.Nodes{})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
want := []tailcfg.FilterRule{
|
||||
{
|
||||
|
@ -3679,7 +3749,7 @@ func TestValidTagInvalidUser(t *testing.T) {
|
|||
}
|
||||
|
||||
got, _, err := GenerateFilterAndSSHRulesForTests(pol, node, types.Nodes{nodes2})
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
want := []tailcfg.FilterRule{
|
||||
{
|
||||
|
|
|
@ -17,9 +17,13 @@ func Apple(url string) *elem.Element {
|
|||
headerOne("headscale: iOS configuration"),
|
||||
headerTwo("GUI"),
|
||||
elem.Ol(nil,
|
||||
elem.Li(nil,
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text("Install the official Tailscale iOS client from the "),
|
||||
elem.A(attrs.Props{attrs.Href: "https://apps.apple.com/app/tailscale/id1470499037"},
|
||||
elem.A(
|
||||
attrs.Props{
|
||||
attrs.Href: "https://apps.apple.com/app/tailscale/id1470499037",
|
||||
},
|
||||
elem.Text("App store"),
|
||||
),
|
||||
),
|
||||
|
@ -31,27 +35,47 @@ func Apple(url string) *elem.Element {
|
|||
elem.Li(nil,
|
||||
elem.Text("Open Settings on the iOS device"),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`Scroll down to the "third party apps" section, under "Game Center" or "TV Provider"`),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
`Scroll down to the "third party apps" section, under "Game Center" or "TV Provider"`,
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Find Tailscale and select it"),
|
||||
elem.Ul(nil,
|
||||
elem.Li(nil,
|
||||
elem.Text(`If the iOS device was previously logged into Tailscale, switch the "Reset Keychain" toggle to "on"`),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
`If the iOS device was previously logged into Tailscale, switch the "Reset Keychain" toggle to "on"`,
|
||||
),
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(fmt.Sprintf(`Enter "%s" under "Alternate Coordination Server URL"`,url)),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Restart the app by closing it from the iOS app switcher, open the app and select the regular sign in option "),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
fmt.Sprintf(
|
||||
`Enter "%s" under "Alternate Coordination Server URL"`,
|
||||
url,
|
||||
),
|
||||
),
|
||||
),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
"Restart the app by closing it from the iOS app switcher, open the app and select the regular sign in option ",
|
||||
),
|
||||
elem.I(nil, elem.Text("(non-SSO)")),
|
||||
elem.Text(". It should open up to the headscale authentication page."),
|
||||
elem.Text(
|
||||
". It should open up to the headscale authentication page.",
|
||||
),
|
||||
),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
"Enter your credentials and log in. Headscale should now be working on your iOS device",
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Enter your credentials and log in. Headscale should now be working on your iOS device"),
|
||||
),
|
||||
),
|
||||
headerOne("headscale: macOS configuration"),
|
||||
|
@ -61,39 +85,63 @@ func Apple(url string) *elem.Element {
|
|||
),
|
||||
elem.Pre(nil,
|
||||
elem.Code(nil,
|
||||
elem.Text(fmt.Sprintf("tailscale login --login-server %s",url)),
|
||||
elem.Text(fmt.Sprintf("tailscale login --login-server %s", url)),
|
||||
),
|
||||
),
|
||||
headerTwo("GUI"),
|
||||
elem.Ol(nil,
|
||||
elem.Li(nil,
|
||||
elem.Text("ALT + Click the Tailscale icon in the menu and hover over the Debug menu"),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
"ALT + Click the Tailscale icon in the menu and hover over the Debug menu",
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`Under "Custom Login Server", select "Add Account..."`),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(fmt.Sprintf(`Enter "%s" of the headscale instance and press "Add Account"`,url)),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
fmt.Sprintf(
|
||||
`Enter "%s" of the headscale instance and press "Add Account"`,
|
||||
url,
|
||||
),
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`Follow the login procedure in the browser`),
|
||||
),
|
||||
),
|
||||
headerTwo("Profiles"),
|
||||
elem.P(nil,
|
||||
elem.Text("Headscale can be set to the default server by installing a Headscale configuration profile:"),
|
||||
elem.P(
|
||||
nil,
|
||||
elem.Text(
|
||||
"Headscale can be set to the default server by installing a Headscale configuration profile:",
|
||||
),
|
||||
elem.P(nil,
|
||||
elem.A(attrs.Props{attrs.Href: "/apple/macos-app-store", attrs.Download: "headscale_macos.mobileconfig"},
|
||||
),
|
||||
elem.P(
|
||||
nil,
|
||||
elem.A(
|
||||
attrs.Props{
|
||||
attrs.Href: "/apple/macos-app-store",
|
||||
attrs.Download: "headscale_macos.mobileconfig",
|
||||
},
|
||||
elem.Text("macOS AppStore profile "),
|
||||
),
|
||||
elem.A(attrs.Props{attrs.Href: "/apple/macos-standalone", attrs.Download: "headscale_macos.mobileconfig"},
|
||||
elem.A(
|
||||
attrs.Props{
|
||||
attrs.Href: "/apple/macos-standalone",
|
||||
attrs.Download: "headscale_macos.mobileconfig",
|
||||
},
|
||||
elem.Text("macOS Standalone profile"),
|
||||
),
|
||||
),
|
||||
elem.Ol(nil,
|
||||
elem.Li(nil,
|
||||
elem.Text("Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed"),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
"Download the profile, then open it. When it has been opened, there should be a notification that a profile can be installed",
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`Open System Preferences and go to "Profiles"`),
|
||||
|
@ -106,20 +154,35 @@ func Apple(url string) *elem.Element {
|
|||
),
|
||||
),
|
||||
elem.P(nil, elem.Text("Or")),
|
||||
elem.P(nil,
|
||||
elem.Text("Use your terminal to configure the default setting for Tailscale by issuing:"),
|
||||
elem.P(
|
||||
nil,
|
||||
elem.Text(
|
||||
"Use your terminal to configure the default setting for Tailscale by issuing:",
|
||||
),
|
||||
),
|
||||
elem.Ul(nil,
|
||||
elem.Li(nil,
|
||||
elem.Text(`for app store client:`),
|
||||
elem.Code(nil,
|
||||
elem.Text(fmt.Sprintf(`defaults write io.tailscale.ipn.macos ControlURL %s`,url)),
|
||||
elem.Code(
|
||||
nil,
|
||||
elem.Text(
|
||||
fmt.Sprintf(
|
||||
`defaults write io.tailscale.ipn.macos ControlURL %s`,
|
||||
url,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`for standalone client:`),
|
||||
elem.Code(nil,
|
||||
elem.Text(fmt.Sprintf(`defaults write io.tailscale.ipn.macsys ControlURL %s`,url)),
|
||||
elem.Code(
|
||||
nil,
|
||||
elem.Text(
|
||||
fmt.Sprintf(
|
||||
`defaults write io.tailscale.ipn.macsys ControlURL %s`,
|
||||
url,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -127,23 +190,73 @@ func Apple(url string) *elem.Element {
|
|||
elem.Text("Restart Tailscale.app and log in."),
|
||||
),
|
||||
headerThree("Caution"),
|
||||
elem.P(nil,
|
||||
elem.Text("You should always download and inspect the profile before installing it:"),
|
||||
elem.P(
|
||||
nil,
|
||||
elem.Text(
|
||||
"You should always download and inspect the profile before installing it:",
|
||||
),
|
||||
),
|
||||
elem.Ul(nil,
|
||||
elem.Li(nil,
|
||||
elem.Text(`for app store client: `),
|
||||
elem.Code(nil,
|
||||
elem.Text(fmt.Sprintf(`curl %s/apple/macos-app-store`,url)),
|
||||
elem.Text(fmt.Sprintf(`curl %s/apple/macos-app-store`, url)),
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`for standalone client: `),
|
||||
elem.Code(nil,
|
||||
elem.Text(fmt.Sprintf(`curl %s/apple/macos-standalone`,url)),
|
||||
elem.Text(fmt.Sprintf(`curl %s/apple/macos-standalone`, url)),
|
||||
),
|
||||
),
|
||||
),
|
||||
headerOne("headscale: tvOS configuration"),
|
||||
headerTwo("GUI"),
|
||||
elem.Ol(nil,
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text("Install the official Tailscale tvOS client from the "),
|
||||
elem.A(
|
||||
attrs.Props{
|
||||
attrs.Href: "https://apps.apple.com/app/tailscale/id1470499037",
|
||||
},
|
||||
elem.Text("App store"),
|
||||
),
|
||||
),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
"Open Settings (the Apple tvOS settings) > Apps > Tailscale",
|
||||
),
|
||||
),
|
||||
elem.Li(
|
||||
nil,
|
||||
elem.Text(
|
||||
fmt.Sprintf(
|
||||
`Enter "%s" under "ALTERNATE COORDINATION SERVER URL"`,
|
||||
url,
|
||||
),
|
||||
),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Return to the tvOS Home screen"),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Open Tailscale"),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`Select "Install VPN configuration"`),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text(`Select "Allow"`),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Scan the QR code and follow the login procedure"),
|
||||
),
|
||||
elem.Li(nil,
|
||||
elem.Text("Headscale should now be working on your tvOS device"),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ func Windows(url string) *elem.Element {
|
|||
elem.Text("headscale - Windows"),
|
||||
),
|
||||
elem.Body(attrs.Props{
|
||||
attrs.Style : bodyStyle.ToInline(),
|
||||
attrs.Style: bodyStyle.ToInline(),
|
||||
},
|
||||
headerOne("headscale: Windows configuration"),
|
||||
elem.P(nil,
|
||||
|
@ -21,7 +21,8 @@ func Windows(url string) *elem.Element {
|
|||
elem.A(attrs.Props{
|
||||
attrs.Href: "https://tailscale.com/download/windows",
|
||||
attrs.Rel: "noreferrer noopener",
|
||||
attrs.Target: "_blank"},
|
||||
attrs.Target: "_blank",
|
||||
},
|
||||
elem.Text("Tailscale for Windows ")),
|
||||
elem.Text("and install it."),
|
||||
),
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/types/dnstype"
|
||||
)
|
||||
|
@ -35,8 +36,17 @@ func TestReadConfig(t *testing.T) {
|
|||
MagicDNS: true,
|
||||
BaseDomain: "example.com",
|
||||
Nameservers: Nameservers{
|
||||
Global: []string{"1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001", "https://dns.nextdns.io/abc123"},
|
||||
Split: map[string][]string{"darp.headscale.net": {"1.1.1.1", "8.8.8.8"}, "foo.bar.com": {"1.1.1.1"}},
|
||||
Global: []string{
|
||||
"1.1.1.1",
|
||||
"1.0.0.1",
|
||||
"2606:4700:4700::1111",
|
||||
"2606:4700:4700::1001",
|
||||
"https://dns.nextdns.io/abc123",
|
||||
},
|
||||
Split: map[string][]string{
|
||||
"darp.headscale.net": {"1.1.1.1", "8.8.8.8"},
|
||||
"foo.bar.com": {"1.1.1.1"},
|
||||
},
|
||||
},
|
||||
ExtraRecords: []tailcfg.DNSRecord{
|
||||
{Name: "grafana.myvpn.example.com", Type: "A", Value: "100.64.0.3"},
|
||||
|
@ -91,8 +101,17 @@ func TestReadConfig(t *testing.T) {
|
|||
MagicDNS: false,
|
||||
BaseDomain: "example.com",
|
||||
Nameservers: Nameservers{
|
||||
Global: []string{"1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001", "https://dns.nextdns.io/abc123"},
|
||||
Split: map[string][]string{"darp.headscale.net": {"1.1.1.1", "8.8.8.8"}, "foo.bar.com": {"1.1.1.1"}},
|
||||
Global: []string{
|
||||
"1.1.1.1",
|
||||
"1.0.0.1",
|
||||
"2606:4700:4700::1111",
|
||||
"2606:4700:4700::1001",
|
||||
"https://dns.nextdns.io/abc123",
|
||||
},
|
||||
Split: map[string][]string{
|
||||
"darp.headscale.net": {"1.1.1.1", "8.8.8.8"},
|
||||
"foo.bar.com": {"1.1.1.1"},
|
||||
},
|
||||
},
|
||||
ExtraRecords: []tailcfg.DNSRecord{
|
||||
{Name: "grafana.myvpn.example.com", Type: "A", Value: "100.64.0.3"},
|
||||
|
@ -186,7 +205,7 @@ func TestReadConfig(t *testing.T) {
|
|||
t.Run(tt.name, func(t *testing.T) {
|
||||
viper.Reset()
|
||||
err := LoadConfig(tt.configPath, true)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
conf, err := tt.setup(t)
|
||||
|
||||
|
@ -196,7 +215,7 @@ func TestReadConfig(t *testing.T) {
|
|||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
if diff := cmp.Diff(tt.want, conf); diff != "" {
|
||||
t.Errorf("ReadConfig() mismatch (-want +got):\n%s", diff)
|
||||
|
@ -276,10 +295,10 @@ func TestReadConfigFromEnv(t *testing.T) {
|
|||
|
||||
viper.Reset()
|
||||
err := LoadConfig("testdata/minimal.yaml", true)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
conf, err := tt.setup(t)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
if diff := cmp.Diff(tt.want, conf); diff != "" {
|
||||
t.Errorf("ReadConfig() mismatch (-want +got):\n%s", diff)
|
||||
|
@ -310,13 +329,25 @@ noise:
|
|||
|
||||
// Check configuration validation errors (1)
|
||||
err = LoadConfig(tmpDir, false)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = validateServerConfig()
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both")
|
||||
assert.Contains(t, err.Error(), "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are")
|
||||
assert.Contains(t, err.Error(), "Fatal config error: server_url must start with https:// or http://")
|
||||
require.Error(t, err)
|
||||
assert.Contains(
|
||||
t,
|
||||
err.Error(),
|
||||
"Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both",
|
||||
)
|
||||
assert.Contains(
|
||||
t,
|
||||
err.Error(),
|
||||
"Fatal config error: the only supported values for tls_letsencrypt_challenge_type are",
|
||||
)
|
||||
assert.Contains(
|
||||
t,
|
||||
err.Error(),
|
||||
"Fatal config error: server_url must start with https:// or http://",
|
||||
)
|
||||
|
||||
// Check configuration validation errors (2)
|
||||
configYaml = []byte(`---
|
||||
|
@ -331,5 +362,5 @@ tls_letsencrypt_challenge_type: TLS-ALPN-01
|
|||
t.Fatalf("Couldn't write file %s", configFilePath)
|
||||
}
|
||||
err = LoadConfig(tmpDir, false)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGenerateRandomStringDNSSafe(t *testing.T) {
|
||||
for i := 0; i < 100000; i++ {
|
||||
str, err := GenerateRandomStringDNSSafe(8)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, str, 8)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/juanfont/headscale/integration/hsic"
|
||||
"github.com/juanfont/headscale/integration/tsic"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var veryLargeDestination = []string{
|
||||
|
@ -54,7 +55,7 @@ func aclScenario(
|
|||
) *Scenario {
|
||||
t.Helper()
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
spec := map[string]int{
|
||||
"user1": clientsPerUser,
|
||||
|
@ -77,10 +78,10 @@ func aclScenario(
|
|||
hsic.WithACLPolicy(policy),
|
||||
hsic.WithTestName("acl"),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = scenario.ListTailscaleClientsFQDNs()
|
||||
assertNoErrListFQDN(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
return scenario
|
||||
}
|
||||
|
@ -267,7 +268,7 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||
for name, testCase := range tests {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
spec := testCase.users
|
||||
|
||||
|
@ -275,22 +276,22 @@ func TestACLHostsInNetMapTable(t *testing.T) {
|
|||
[]tsic.Option{},
|
||||
hsic.WithACLPolicy(&testCase.policy),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
allClients, err := scenario.ListTailscaleClients()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scenario.WaitForTailscaleSyncWithPeerCount(testCase.want["user1"])
|
||||
assertNoErrSync(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, client := range allClients {
|
||||
status, err := client.Status()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user := status.User[status.Self.UserID].LoginName
|
||||
|
||||
assert.Equal(t, (testCase.want[user]), len(status.Peer))
|
||||
assert.Len(t, status.Peer, (testCase.want[user]))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -319,23 +320,23 @@ func TestACLAllowUser80Dst(t *testing.T) {
|
|||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
user1Clients, err := scenario.ListTailscaleClients("user1")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2Clients, err := scenario.ListTailscaleClients("user2")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test that user1 can visit all user2
|
||||
for _, client := range user1Clients {
|
||||
for _, peer := range user2Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -343,14 +344,14 @@ func TestACLAllowUser80Dst(t *testing.T) {
|
|||
for _, client := range user2Clients {
|
||||
for _, peer := range user1Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,10 +377,10 @@ func TestACLDenyAllPort80(t *testing.T) {
|
|||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
allClients, err := scenario.ListTailscaleClients()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
allHostnames, err := scenario.ListTailscaleClientsFQDNs()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, client := range allClients {
|
||||
for _, hostname := range allHostnames {
|
||||
|
@ -394,7 +395,7 @@ func TestACLDenyAllPort80(t *testing.T) {
|
|||
|
||||
result, err := client.Curl(url)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,23 +421,23 @@ func TestACLAllowUserDst(t *testing.T) {
|
|||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
user1Clients, err := scenario.ListTailscaleClients("user1")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2Clients, err := scenario.ListTailscaleClients("user2")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test that user1 can visit all user2
|
||||
for _, client := range user1Clients {
|
||||
for _, peer := range user2Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,14 +445,14 @@ func TestACLAllowUserDst(t *testing.T) {
|
|||
for _, client := range user2Clients {
|
||||
for _, peer := range user1Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -476,23 +477,23 @@ func TestACLAllowStarDst(t *testing.T) {
|
|||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
user1Clients, err := scenario.ListTailscaleClients("user1")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2Clients, err := scenario.ListTailscaleClients("user2")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test that user1 can visit all user2
|
||||
for _, client := range user1Clients {
|
||||
for _, peer := range user2Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,14 +501,14 @@ func TestACLAllowStarDst(t *testing.T) {
|
|||
for _, client := range user2Clients {
|
||||
for _, peer := range user1Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -537,23 +538,23 @@ func TestACLNamedHostsCanReachBySubnet(t *testing.T) {
|
|||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
user1Clients, err := scenario.ListTailscaleClients("user1")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2Clients, err := scenario.ListTailscaleClients("user2")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test that user1 can visit all user2
|
||||
for _, client := range user1Clients {
|
||||
for _, peer := range user2Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -561,14 +562,14 @@ func TestACLNamedHostsCanReachBySubnet(t *testing.T) {
|
|||
for _, client := range user2Clients {
|
||||
for _, peer := range user1Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -679,10 +680,10 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test1ip4 := netip.MustParseAddr("100.64.0.1")
|
||||
test1ip6 := netip.MustParseAddr("fd7a:115c:a1e0::1")
|
||||
test1, err := scenario.FindTailscaleClientByIP(test1ip6)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
test1fqdn, err := test1.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
test1ip4URL := fmt.Sprintf("http://%s/etc/hostname", test1ip4.String())
|
||||
test1ip6URL := fmt.Sprintf("http://[%s]/etc/hostname", test1ip6.String())
|
||||
test1fqdnURL := fmt.Sprintf("http://%s/etc/hostname", test1fqdn)
|
||||
|
@ -690,10 +691,10 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test2ip4 := netip.MustParseAddr("100.64.0.2")
|
||||
test2ip6 := netip.MustParseAddr("fd7a:115c:a1e0::2")
|
||||
test2, err := scenario.FindTailscaleClientByIP(test2ip6)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
test2fqdn, err := test2.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
test2ip4URL := fmt.Sprintf("http://%s/etc/hostname", test2ip4.String())
|
||||
test2ip6URL := fmt.Sprintf("http://[%s]/etc/hostname", test2ip6.String())
|
||||
test2fqdnURL := fmt.Sprintf("http://%s/etc/hostname", test2fqdn)
|
||||
|
@ -701,10 +702,10 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3ip4 := netip.MustParseAddr("100.64.0.3")
|
||||
test3ip6 := netip.MustParseAddr("fd7a:115c:a1e0::3")
|
||||
test3, err := scenario.FindTailscaleClientByIP(test3ip6)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
test3fqdn, err := test3.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
test3ip4URL := fmt.Sprintf("http://%s/etc/hostname", test3ip4.String())
|
||||
test3ip6URL := fmt.Sprintf("http://[%s]/etc/hostname", test3ip6.String())
|
||||
test3fqdnURL := fmt.Sprintf("http://%s/etc/hostname", test3fqdn)
|
||||
|
@ -719,7 +720,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3ip4URL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test1.Curl(test3ip6URL)
|
||||
assert.Lenf(
|
||||
|
@ -730,7 +731,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3ip6URL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test1.Curl(test3fqdnURL)
|
||||
assert.Lenf(
|
||||
|
@ -741,7 +742,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3fqdnURL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test2 can query test3
|
||||
result, err = test2.Curl(test3ip4URL)
|
||||
|
@ -753,7 +754,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3ip4URL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test2.Curl(test3ip6URL)
|
||||
assert.Lenf(
|
||||
|
@ -764,7 +765,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3ip6URL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test2.Curl(test3fqdnURL)
|
||||
assert.Lenf(
|
||||
|
@ -775,33 +776,33 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test3fqdnURL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test3 cannot query test1
|
||||
result, err = test3.Curl(test1ip4URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test3.Curl(test1ip6URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test3.Curl(test1fqdnURL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// test3 cannot query test2
|
||||
result, err = test3.Curl(test2ip4URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test3.Curl(test2ip6URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test3.Curl(test2fqdnURL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
// test1 can query test2
|
||||
result, err = test1.Curl(test2ip4URL)
|
||||
|
@ -814,7 +815,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
result,
|
||||
)
|
||||
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
result, err = test1.Curl(test2ip6URL)
|
||||
assert.Lenf(
|
||||
t,
|
||||
|
@ -824,7 +825,7 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test2ip6URL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test1.Curl(test2fqdnURL)
|
||||
assert.Lenf(
|
||||
|
@ -835,20 +836,20 @@ func TestACLNamedHostsCanReach(t *testing.T) {
|
|||
test2fqdnURL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// test2 cannot query test1
|
||||
result, err = test2.Curl(test1ip4URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test2.Curl(test1ip6URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test2.Curl(test1fqdnURL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -946,10 +947,10 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
|
|||
test1ip6 := netip.MustParseAddr("fd7a:115c:a1e0::1")
|
||||
test1, err := scenario.FindTailscaleClientByIP(test1ip)
|
||||
assert.NotNil(t, test1)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
test1fqdn, err := test1.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
test1ipURL := fmt.Sprintf("http://%s/etc/hostname", test1ip.String())
|
||||
test1ip6URL := fmt.Sprintf("http://[%s]/etc/hostname", test1ip6.String())
|
||||
test1fqdnURL := fmt.Sprintf("http://%s/etc/hostname", test1fqdn)
|
||||
|
@ -958,10 +959,10 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
|
|||
test2ip6 := netip.MustParseAddr("fd7a:115c:a1e0::2")
|
||||
test2, err := scenario.FindTailscaleClientByIP(test2ip)
|
||||
assert.NotNil(t, test2)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
test2fqdn, err := test2.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
test2ipURL := fmt.Sprintf("http://%s/etc/hostname", test2ip.String())
|
||||
test2ip6URL := fmt.Sprintf("http://[%s]/etc/hostname", test2ip6.String())
|
||||
test2fqdnURL := fmt.Sprintf("http://%s/etc/hostname", test2fqdn)
|
||||
|
@ -976,7 +977,7 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
|
|||
test2ipURL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test1.Curl(test2ip6URL)
|
||||
assert.Lenf(
|
||||
|
@ -987,7 +988,7 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
|
|||
test2ip6URL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test1.Curl(test2fqdnURL)
|
||||
assert.Lenf(
|
||||
|
@ -998,19 +999,19 @@ func TestACLDevice1CanAccessDevice2(t *testing.T) {
|
|||
test2fqdnURL,
|
||||
result,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result, err = test2.Curl(test1ipURL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test2.Curl(test1ip6URL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
|
||||
result, err = test2.Curl(test1fqdnURL)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1020,7 +1021,7 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -1046,19 +1047,19 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
"HEADSCALE_POLICY_MODE": "database",
|
||||
}),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = scenario.ListTailscaleClientsFQDNs()
|
||||
assertNoErrListFQDN(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scenario.WaitForTailscaleSync()
|
||||
assertNoErrSync(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user1Clients, err := scenario.ListTailscaleClients("user1")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
user2Clients, err := scenario.ListTailscaleClients("user2")
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
all := append(user1Clients, user2Clients...)
|
||||
|
||||
|
@ -1070,19 +1071,19 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
}
|
||||
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
p := policy.ACLPolicy{
|
||||
ACLs: []policy.ACL{
|
||||
|
@ -1100,7 +1101,7 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
policyFilePath := "/etc/headscale/policy.json"
|
||||
|
||||
err = headscale.WriteFile(policyFilePath, pBytes)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// No policy is present at this time.
|
||||
// Add a new policy from a file.
|
||||
|
@ -1113,7 +1114,7 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
policyFilePath,
|
||||
},
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get the current policy and check
|
||||
// if it is the same as the one we set.
|
||||
|
@ -1129,7 +1130,7 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
},
|
||||
&output,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, output.ACLs, 1)
|
||||
|
||||
|
@ -1141,14 +1142,14 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
for _, client := range user1Clients {
|
||||
for _, peer := range user2Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Len(t, result, 13)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1156,14 +1157,14 @@ func TestPolicyUpdateWhileRunningWithCLIInDatabase(t *testing.T) {
|
|||
for _, client := range user2Clients {
|
||||
for _, peer := range user1Clients {
|
||||
fqdn, err := peer.FQDN()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
url := fmt.Sprintf("http://%s/etc/hostname", fqdn)
|
||||
t.Logf("url from %s to %s", client.Hostname(), url)
|
||||
|
||||
result, err := client.Curl(url)
|
||||
assert.Empty(t, result)
|
||||
assert.Error(t, err)
|
||||
require.Error(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/juanfont/headscale/integration/hsic"
|
||||
"github.com/juanfont/headscale/integration/tsic"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func executeAndUnmarshal[T any](headscale ControlServer, command []string, result T) error {
|
||||
|
@ -34,7 +35,7 @@ func TestUserCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -43,10 +44,10 @@ func TestUserCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var listUsers []v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
|
@ -59,7 +60,7 @@ func TestUserCommand(t *testing.T) {
|
|||
},
|
||||
&listUsers,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result := []string{listUsers[0].GetName(), listUsers[1].GetName()}
|
||||
sort.Strings(result)
|
||||
|
@ -81,7 +82,7 @@ func TestUserCommand(t *testing.T) {
|
|||
"newname",
|
||||
},
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var listAfterRenameUsers []v1.User
|
||||
err = executeAndUnmarshal(headscale,
|
||||
|
@ -94,7 +95,7 @@ func TestUserCommand(t *testing.T) {
|
|||
},
|
||||
&listAfterRenameUsers,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
result = []string{listAfterRenameUsers[0].GetName(), listAfterRenameUsers[1].GetName()}
|
||||
sort.Strings(result)
|
||||
|
@ -114,7 +115,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
count := 3
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -122,13 +123,13 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipak"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
keys := make([]*v1.PreAuthKey, count)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for index := 0; index < count; index++ {
|
||||
var preAuthKey v1.PreAuthKey
|
||||
|
@ -150,7 +151,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
},
|
||||
&preAuthKey,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
keys[index] = &preAuthKey
|
||||
}
|
||||
|
@ -171,7 +172,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
},
|
||||
&listedPreAuthKeys,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// There is one key created by "scenario.CreateHeadscaleEnv"
|
||||
assert.Len(t, listedPreAuthKeys, 4)
|
||||
|
@ -212,7 +213,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
assert.Equal(t, listedPreAuthKeys[index].GetAclTags(), []string{"tag:test1", "tag:test2"})
|
||||
assert.Equal(t, []string{"tag:test1", "tag:test2"}, listedPreAuthKeys[index].GetAclTags())
|
||||
}
|
||||
|
||||
// Test key expiry
|
||||
|
@ -226,7 +227,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
listedPreAuthKeys[1].GetKey(),
|
||||
},
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var listedPreAuthKeysAfterExpire []v1.PreAuthKey
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -242,7 +243,7 @@ func TestPreAuthKeyCommand(t *testing.T) {
|
|||
},
|
||||
&listedPreAuthKeysAfterExpire,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, listedPreAuthKeysAfterExpire[1].GetExpiration().AsTime().Before(time.Now()))
|
||||
assert.True(t, listedPreAuthKeysAfterExpire[2].GetExpiration().AsTime().After(time.Now()))
|
||||
|
@ -256,7 +257,7 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
|
|||
user := "pre-auth-key-without-exp-user"
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -264,10 +265,10 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipaknaexp"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var preAuthKey v1.PreAuthKey
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -284,7 +285,7 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
|
|||
},
|
||||
&preAuthKey,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var listedPreAuthKeys []v1.PreAuthKey
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -300,7 +301,7 @@ func TestPreAuthKeyCommandWithoutExpiry(t *testing.T) {
|
|||
},
|
||||
&listedPreAuthKeys,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// There is one key created by "scenario.CreateHeadscaleEnv"
|
||||
assert.Len(t, listedPreAuthKeys, 2)
|
||||
|
@ -319,7 +320,7 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||
user := "pre-auth-key-reus-ephm-user"
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -327,10 +328,10 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clipakresueeph"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var preAuthReusableKey v1.PreAuthKey
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -347,7 +348,7 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||
},
|
||||
&preAuthReusableKey,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var preAuthEphemeralKey v1.PreAuthKey
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -364,7 +365,7 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||
},
|
||||
&preAuthEphemeralKey,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, preAuthEphemeralKey.GetEphemeral())
|
||||
assert.False(t, preAuthEphemeralKey.GetReusable())
|
||||
|
@ -383,7 +384,7 @@ func TestPreAuthKeyCommandReusableEphemeral(t *testing.T) {
|
|||
},
|
||||
&listedPreAuthKeys,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// There is one key created by "scenario.CreateHeadscaleEnv"
|
||||
assert.Len(t, listedPreAuthKeys, 3)
|
||||
|
@ -397,7 +398,7 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
|
|||
user2 := "user2"
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -413,10 +414,10 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
|
|||
hsic.WithTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var user2Key v1.PreAuthKey
|
||||
|
||||
|
@ -438,10 +439,10 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
|
|||
},
|
||||
&user2Key,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
allClients, err := scenario.ListTailscaleClients()
|
||||
assertNoErrListClients(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, allClients, 1)
|
||||
|
||||
|
@ -449,22 +450,22 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
|
|||
|
||||
// Log out from user1
|
||||
err = client.Logout()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = scenario.WaitForTailscaleLogout()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
status, err := client.Status()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
if status.BackendState == "Starting" || status.BackendState == "Running" {
|
||||
t.Fatalf("expected node to be logged out, backend state: %s", status.BackendState)
|
||||
}
|
||||
|
||||
err = client.Login(headscale.GetEndpoint(), user2Key.GetKey())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
status, err = client.Status()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
if status.BackendState != "Running" {
|
||||
t.Fatalf("expected node to be logged in, backend state: %s", status.BackendState)
|
||||
}
|
||||
|
@ -485,7 +486,7 @@ func TestPreAuthKeyCorrectUserLoggedInCommand(t *testing.T) {
|
|||
},
|
||||
&listNodes,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, listNodes, 1)
|
||||
|
||||
assert.Equal(t, "user2", listNodes[0].GetUser().GetName())
|
||||
|
@ -498,7 +499,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
count := 5
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -507,10 +508,10 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
keys := make([]string, count)
|
||||
|
||||
|
@ -526,7 +527,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotEmpty(t, apiResult)
|
||||
|
||||
keys[idx] = apiResult
|
||||
|
@ -545,7 +546,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
},
|
||||
&listedAPIKeys,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listedAPIKeys, 5)
|
||||
|
||||
|
@ -601,7 +602,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
listedAPIKeys[idx].GetPrefix(),
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
expiredPrefixes[listedAPIKeys[idx].GetPrefix()] = true
|
||||
}
|
||||
|
@ -617,7 +618,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
},
|
||||
&listedAfterExpireAPIKeys,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for index := range listedAfterExpireAPIKeys {
|
||||
if _, ok := expiredPrefixes[listedAfterExpireAPIKeys[index].GetPrefix()]; ok {
|
||||
|
@ -643,7 +644,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
"--prefix",
|
||||
listedAPIKeys[0].GetPrefix(),
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var listedAPIKeysAfterDelete []v1.ApiKey
|
||||
err = executeAndUnmarshal(headscale,
|
||||
|
@ -656,7 +657,7 @@ func TestApiKeyCommand(t *testing.T) {
|
|||
},
|
||||
&listedAPIKeysAfterDelete,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listedAPIKeysAfterDelete, 4)
|
||||
}
|
||||
|
@ -666,7 +667,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -674,17 +675,17 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
machineKeys := []string{
|
||||
"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
||||
"mkey:6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
_, err := headscale.Execute(
|
||||
|
@ -702,7 +703,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -720,7 +721,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes[index] = &node
|
||||
}
|
||||
|
@ -739,7 +740,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, []string{"tag:test"}, node.GetForcedTags())
|
||||
|
||||
|
@ -753,7 +754,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
"--output", "json",
|
||||
},
|
||||
)
|
||||
assert.ErrorContains(t, err, "tag must start with the string 'tag:'")
|
||||
require.ErrorContains(t, err, "tag must start with the string 'tag:'")
|
||||
|
||||
// Test list all nodes after added seconds
|
||||
resultMachines := make([]*v1.Node, len(machineKeys))
|
||||
|
@ -767,7 +768,7 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
},
|
||||
&resultMachines,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
found := false
|
||||
for _, node := range resultMachines {
|
||||
if node.GetForcedTags() != nil {
|
||||
|
@ -778,9 +779,8 @@ func TestNodeTagCommand(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
assert.Equal(
|
||||
assert.True(
|
||||
t,
|
||||
true,
|
||||
found,
|
||||
"should find a node with the tag 'tag:test' in the list of nodes",
|
||||
)
|
||||
|
@ -791,18 +791,22 @@ func TestNodeAdvertiseTagNoACLCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
"user1": 1,
|
||||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{tsic.WithTags([]string{"tag:test"})}, hsic.WithTestName("cliadvtags"))
|
||||
assertNoErr(t, err)
|
||||
err = scenario.CreateHeadscaleEnv(
|
||||
spec,
|
||||
[]tsic.Option{tsic.WithTags([]string{"tag:test"})},
|
||||
hsic.WithTestName("cliadvtags"),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test list all nodes after added seconds
|
||||
resultMachines := make([]*v1.Node, spec["user1"])
|
||||
|
@ -817,7 +821,7 @@ func TestNodeAdvertiseTagNoACLCommand(t *testing.T) {
|
|||
},
|
||||
&resultMachines,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
found := false
|
||||
for _, node := range resultMachines {
|
||||
if node.GetInvalidTags() != nil {
|
||||
|
@ -828,9 +832,8 @@ func TestNodeAdvertiseTagNoACLCommand(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
assert.Equal(
|
||||
assert.True(
|
||||
t,
|
||||
true,
|
||||
found,
|
||||
"should not find a node with the tag 'tag:test' in the list of nodes",
|
||||
)
|
||||
|
@ -841,14 +844,18 @@ func TestNodeAdvertiseTagWithACLCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
"user1": 1,
|
||||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{tsic.WithTags([]string{"tag:exists"})}, hsic.WithTestName("cliadvtags"), hsic.WithACLPolicy(
|
||||
err = scenario.CreateHeadscaleEnv(
|
||||
spec,
|
||||
[]tsic.Option{tsic.WithTags([]string{"tag:exists"})},
|
||||
hsic.WithTestName("cliadvtags"),
|
||||
hsic.WithACLPolicy(
|
||||
&policy.ACLPolicy{
|
||||
ACLs: []policy.ACL{
|
||||
{
|
||||
|
@ -861,11 +868,12 @@ func TestNodeAdvertiseTagWithACLCommand(t *testing.T) {
|
|||
"tag:exists": {"user1"},
|
||||
},
|
||||
},
|
||||
))
|
||||
assertNoErr(t, err)
|
||||
),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test list all nodes after added seconds
|
||||
resultMachines := make([]*v1.Node, spec["user1"])
|
||||
|
@ -880,7 +888,7 @@ func TestNodeAdvertiseTagWithACLCommand(t *testing.T) {
|
|||
},
|
||||
&resultMachines,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
found := false
|
||||
for _, node := range resultMachines {
|
||||
if node.GetValidTags() != nil {
|
||||
|
@ -891,9 +899,8 @@ func TestNodeAdvertiseTagWithACLCommand(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
assert.Equal(
|
||||
assert.True(
|
||||
t,
|
||||
true,
|
||||
found,
|
||||
"should not find a node with the tag 'tag:exists' in the list of nodes",
|
||||
)
|
||||
|
@ -904,7 +911,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -913,10 +920,10 @@ func TestNodeCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Pregenerated machine keys
|
||||
machineKeys := []string{
|
||||
|
@ -927,7 +934,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"mkey:cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
_, err := headscale.Execute(
|
||||
|
@ -945,7 +952,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -963,7 +970,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes[index] = &node
|
||||
}
|
||||
|
@ -983,7 +990,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
},
|
||||
&listAll,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listAll, 5)
|
||||
|
||||
|
@ -1004,7 +1011,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"mkey:dc721977ac7415aafa87f7d4574cbe07c6b171834a6d37375782bdc1fb6b3584",
|
||||
}
|
||||
otherUserMachines := make([]*v1.Node, len(otherUserMachineKeys))
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for index, machineKey := range otherUserMachineKeys {
|
||||
_, err := headscale.Execute(
|
||||
|
@ -1022,7 +1029,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1040,7 +1047,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
otherUserMachines[index] = &node
|
||||
}
|
||||
|
@ -1060,7 +1067,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
},
|
||||
&listAllWithotherUser,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// All nodes, nodes + otherUser
|
||||
assert.Len(t, listAllWithotherUser, 7)
|
||||
|
@ -1086,7 +1093,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
},
|
||||
&listOnlyotherUserMachineUser,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listOnlyotherUserMachineUser, 2)
|
||||
|
||||
|
@ -1118,7 +1125,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
"--force",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test: list main user after node is deleted
|
||||
var listOnlyMachineUserAfterDelete []v1.Node
|
||||
|
@ -1135,7 +1142,7 @@ func TestNodeCommand(t *testing.T) {
|
|||
},
|
||||
&listOnlyMachineUserAfterDelete,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listOnlyMachineUserAfterDelete, 4)
|
||||
}
|
||||
|
@ -1145,7 +1152,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -1153,10 +1160,10 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Pregenerated machine keys
|
||||
machineKeys := []string{
|
||||
|
@ -1184,7 +1191,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1202,7 +1209,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes[index] = &node
|
||||
}
|
||||
|
@ -1221,7 +1228,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
},
|
||||
&listAll,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listAll, 5)
|
||||
|
||||
|
@ -1241,7 +1248,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
fmt.Sprintf("%d", listAll[idx].GetId()),
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
var listAllAfterExpiry []v1.Node
|
||||
|
@ -1256,7 +1263,7 @@ func TestNodeExpireCommand(t *testing.T) {
|
|||
},
|
||||
&listAllAfterExpiry,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listAllAfterExpiry, 5)
|
||||
|
||||
|
@ -1272,7 +1279,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -1280,10 +1287,10 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Pregenerated machine keys
|
||||
machineKeys := []string{
|
||||
|
@ -1294,7 +1301,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
"mkey:9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
|
||||
}
|
||||
nodes := make([]*v1.Node, len(machineKeys))
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
for index, machineKey := range machineKeys {
|
||||
_, err := headscale.Execute(
|
||||
|
@ -1312,7 +1319,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1330,7 +1337,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
nodes[index] = &node
|
||||
}
|
||||
|
@ -1349,7 +1356,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
},
|
||||
&listAll,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listAll, 5)
|
||||
|
||||
|
@ -1370,7 +1377,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
fmt.Sprintf("newnode-%d", idx+1),
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Contains(t, res, "Node renamed")
|
||||
}
|
||||
|
@ -1387,7 +1394,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
},
|
||||
&listAllAfterRename,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listAllAfterRename, 5)
|
||||
|
||||
|
@ -1408,7 +1415,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
strings.Repeat("t", 64),
|
||||
},
|
||||
)
|
||||
assert.ErrorContains(t, err, "not be over 63 chars")
|
||||
require.ErrorContains(t, err, "not be over 63 chars")
|
||||
|
||||
var listAllAfterRenameAttempt []v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1422,7 +1429,7 @@ func TestNodeRenameCommand(t *testing.T) {
|
|||
},
|
||||
&listAllAfterRenameAttempt,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, listAllAfterRenameAttempt, 5)
|
||||
|
||||
|
@ -1438,7 +1445,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -1447,10 +1454,10 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
}
|
||||
|
||||
err = scenario.CreateHeadscaleEnv(spec, []tsic.Option{}, hsic.WithTestName("clins"))
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Randomly generated node key
|
||||
machineKey := "mkey:688411b767663479632d44140f08a9fde87383adc7cdeb518f62ce28a17ef0aa"
|
||||
|
@ -1470,7 +1477,7 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
var node v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1488,11 +1495,11 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, uint64(1), node.GetId())
|
||||
assert.Equal(t, "nomad-node", node.GetName())
|
||||
assert.Equal(t, node.GetUser().GetName(), "old-user")
|
||||
assert.Equal(t, "old-user", node.GetUser().GetName())
|
||||
|
||||
nodeID := fmt.Sprintf("%d", node.GetId())
|
||||
|
||||
|
@ -1511,9 +1518,9 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, node.GetUser().GetName(), "new-user")
|
||||
assert.Equal(t, "new-user", node.GetUser().GetName())
|
||||
|
||||
var allNodes []v1.Node
|
||||
err = executeAndUnmarshal(
|
||||
|
@ -1527,13 +1534,13 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
},
|
||||
&allNodes,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, allNodes, 1)
|
||||
|
||||
assert.Equal(t, allNodes[0].GetId(), node.GetId())
|
||||
assert.Equal(t, allNodes[0].GetUser(), node.GetUser())
|
||||
assert.Equal(t, allNodes[0].GetUser().GetName(), "new-user")
|
||||
assert.Equal(t, "new-user", allNodes[0].GetUser().GetName())
|
||||
|
||||
_, err = headscale.Execute(
|
||||
[]string{
|
||||
|
@ -1548,12 +1555,12 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.ErrorContains(
|
||||
require.ErrorContains(
|
||||
t,
|
||||
err,
|
||||
"user not found",
|
||||
)
|
||||
assert.Equal(t, node.GetUser().GetName(), "new-user")
|
||||
assert.Equal(t, "new-user", node.GetUser().GetName())
|
||||
|
||||
err = executeAndUnmarshal(
|
||||
headscale,
|
||||
|
@ -1570,9 +1577,9 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, node.GetUser().GetName(), "old-user")
|
||||
assert.Equal(t, "old-user", node.GetUser().GetName())
|
||||
|
||||
err = executeAndUnmarshal(
|
||||
headscale,
|
||||
|
@ -1589,9 +1596,9 @@ func TestNodeMoveCommand(t *testing.T) {
|
|||
},
|
||||
&node,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, node.GetUser().GetName(), "old-user")
|
||||
assert.Equal(t, "old-user", node.GetUser().GetName())
|
||||
}
|
||||
|
||||
func TestPolicyCommand(t *testing.T) {
|
||||
|
@ -1599,7 +1606,7 @@ func TestPolicyCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -1614,10 +1621,10 @@ func TestPolicyCommand(t *testing.T) {
|
|||
"HEADSCALE_POLICY_MODE": "database",
|
||||
}),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
p := policy.ACLPolicy{
|
||||
ACLs: []policy.ACL{
|
||||
|
@ -1637,7 +1644,7 @@ func TestPolicyCommand(t *testing.T) {
|
|||
policyFilePath := "/etc/headscale/policy.json"
|
||||
|
||||
err = headscale.WriteFile(policyFilePath, pBytes)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// No policy is present at this time.
|
||||
// Add a new policy from a file.
|
||||
|
@ -1651,7 +1658,7 @@ func TestPolicyCommand(t *testing.T) {
|
|||
},
|
||||
)
|
||||
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get the current policy and check
|
||||
// if it is the same as the one we set.
|
||||
|
@ -1667,11 +1674,11 @@ func TestPolicyCommand(t *testing.T) {
|
|||
},
|
||||
&output,
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Len(t, output.TagOwners, 1)
|
||||
assert.Len(t, output.ACLs, 1)
|
||||
assert.Equal(t, output.TagOwners["tag:exists"], []string{"policy-user"})
|
||||
assert.Equal(t, []string{"policy-user"}, output.TagOwners["tag:exists"])
|
||||
}
|
||||
|
||||
func TestPolicyBrokenConfigCommand(t *testing.T) {
|
||||
|
@ -1679,7 +1686,7 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
scenario, err := NewScenario(dockertestMaxWait())
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
defer scenario.ShutdownAssertNoPanics(t)
|
||||
|
||||
spec := map[string]int{
|
||||
|
@ -1694,10 +1701,10 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
|
|||
"HEADSCALE_POLICY_MODE": "database",
|
||||
}),
|
||||
)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
headscale, err := scenario.Headscale()
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
p := policy.ACLPolicy{
|
||||
ACLs: []policy.ACL{
|
||||
|
@ -1719,7 +1726,7 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
|
|||
policyFilePath := "/etc/headscale/policy.json"
|
||||
|
||||
err = headscale.WriteFile(policyFilePath, pBytes)
|
||||
assertNoErr(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
// No policy is present at this time.
|
||||
// Add a new policy from a file.
|
||||
|
@ -1732,7 +1739,7 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
|
|||
policyFilePath,
|
||||
},
|
||||
)
|
||||
assert.ErrorContains(t, err, "verifying policy rules: invalid action")
|
||||
require.ErrorContains(t, err, "verifying policy rules: invalid action")
|
||||
|
||||
// The new policy was invalid, the old one should still be in place, which
|
||||
// is none.
|
||||
|
@ -1745,5 +1752,5 @@ func TestPolicyBrokenConfigCommand(t *testing.T) {
|
|||
"json",
|
||||
},
|
||||
)
|
||||
assert.ErrorContains(t, err, "acl policy not found")
|
||||
require.ErrorContains(t, err, "acl policy not found")
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func TestDERPServerWebsocketScenario(t *testing.T) {
|
|||
spec := map[string]ClientsSpec{
|
||||
"user1": {
|
||||
Plain: 0,
|
||||
WebsocketDERP: len(MustTestVersions),
|
||||
WebsocketDERP: 2,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -239,10 +239,13 @@ func (s *EmbeddedDERPServerScenario) CreateHeadscaleEnv(
|
|||
|
||||
if clientCount.WebsocketDERP > 0 {
|
||||
// Containers that use DERP-over-WebSocket
|
||||
// Note that these clients *must* be built
|
||||
// from source, which is currently
|
||||
// only done for HEAD.
|
||||
err = s.CreateTailscaleIsolatedNodesInUser(
|
||||
hash,
|
||||
userName,
|
||||
"all",
|
||||
tsic.VersionHead,
|
||||
clientCount.WebsocketDERP,
|
||||
tsic.WithWebsocketDERP(true),
|
||||
)
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"tailscale.com/client/tailscale/apitype"
|
||||
"tailscale.com/types/key"
|
||||
|
@ -244,7 +245,11 @@ func TestEphemeral(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEphemeralInAlternateTimezone(t *testing.T) {
|
||||
testEphemeralWithOptions(t, hsic.WithTestName("ephemeral-tz"), hsic.WithTimezone("America/Los_Angeles"))
|
||||
testEphemeralWithOptions(
|
||||
t,
|
||||
hsic.WithTestName("ephemeral-tz"),
|
||||
hsic.WithTimezone("America/Los_Angeles"),
|
||||
)
|
||||
}
|
||||
|
||||
func testEphemeralWithOptions(t *testing.T, opts ...hsic.Option) {
|
||||
|
@ -1164,10 +1169,10 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) {
|
|||
},
|
||||
&nodeList,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, nodeList, 2)
|
||||
assert.True(t, nodeList[0].Online)
|
||||
assert.True(t, nodeList[1].Online)
|
||||
assert.True(t, nodeList[0].GetOnline())
|
||||
assert.True(t, nodeList[1].GetOnline())
|
||||
|
||||
// Delete the first node, which is online
|
||||
_, err = headscale.Execute(
|
||||
|
@ -1177,13 +1182,13 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) {
|
|||
"delete",
|
||||
"--identifier",
|
||||
// Delete the last added machine
|
||||
fmt.Sprintf("%d", nodeList[0].Id),
|
||||
fmt.Sprintf("%d", nodeList[0].GetId()),
|
||||
"--output",
|
||||
"json",
|
||||
"--force",
|
||||
},
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
|
@ -1200,9 +1205,8 @@ func Test2118DeletingOnlineNodePanics(t *testing.T) {
|
|||
},
|
||||
&nodeListAfter,
|
||||
)
|
||||
assert.Nil(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, nodeListAfter, 1)
|
||||
assert.True(t, nodeListAfter[0].Online)
|
||||
assert.Equal(t, nodeList[1].Id, nodeListAfter[0].Id)
|
||||
|
||||
assert.True(t, nodeListAfter[0].GetOnline())
|
||||
assert.Equal(t, nodeList[1].GetId(), nodeListAfter[0].GetId())
|
||||
}
|
||||
|
|
|
@ -92,9 +92,9 @@ func TestEnablingRoutes(t *testing.T) {
|
|||
assert.Len(t, routes, 3)
|
||||
|
||||
for _, route := range routes {
|
||||
assert.Equal(t, true, route.GetAdvertised())
|
||||
assert.Equal(t, false, route.GetEnabled())
|
||||
assert.Equal(t, false, route.GetIsPrimary())
|
||||
assert.True(t, route.GetAdvertised())
|
||||
assert.False(t, route.GetEnabled())
|
||||
assert.False(t, route.GetIsPrimary())
|
||||
}
|
||||
|
||||
// Verify that no routes has been sent to the client,
|
||||
|
@ -139,9 +139,9 @@ func TestEnablingRoutes(t *testing.T) {
|
|||
assert.Len(t, enablingRoutes, 3)
|
||||
|
||||
for _, route := range enablingRoutes {
|
||||
assert.Equal(t, true, route.GetAdvertised())
|
||||
assert.Equal(t, true, route.GetEnabled())
|
||||
assert.Equal(t, true, route.GetIsPrimary())
|
||||
assert.True(t, route.GetAdvertised())
|
||||
assert.True(t, route.GetEnabled())
|
||||
assert.True(t, route.GetIsPrimary())
|
||||
}
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
@ -212,18 +212,18 @@ func TestEnablingRoutes(t *testing.T) {
|
|||
assertNoErr(t, err)
|
||||
|
||||
for _, route := range disablingRoutes {
|
||||
assert.Equal(t, true, route.GetAdvertised())
|
||||
assert.True(t, route.GetAdvertised())
|
||||
|
||||
if route.GetId() == routeToBeDisabled.GetId() {
|
||||
assert.Equal(t, false, route.GetEnabled())
|
||||
assert.False(t, route.GetEnabled())
|
||||
|
||||
// since this is the only route of this cidr,
|
||||
// it will not failover, and remain Primary
|
||||
// until something can replace it.
|
||||
assert.Equal(t, true, route.GetIsPrimary())
|
||||
assert.True(t, route.GetIsPrimary())
|
||||
} else {
|
||||
assert.Equal(t, true, route.GetEnabled())
|
||||
assert.Equal(t, true, route.GetIsPrimary())
|
||||
assert.True(t, route.GetEnabled())
|
||||
assert.True(t, route.GetIsPrimary())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -342,9 +342,9 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
t.Logf("initial routes %#v", routes)
|
||||
|
||||
for _, route := range routes {
|
||||
assert.Equal(t, true, route.GetAdvertised())
|
||||
assert.Equal(t, false, route.GetEnabled())
|
||||
assert.Equal(t, false, route.GetIsPrimary())
|
||||
assert.True(t, route.GetAdvertised())
|
||||
assert.False(t, route.GetEnabled())
|
||||
assert.False(t, route.GetIsPrimary())
|
||||
}
|
||||
|
||||
// Verify that no routes has been sent to the client,
|
||||
|
@ -391,14 +391,14 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
assert.Len(t, enablingRoutes, 2)
|
||||
|
||||
// Node 1 is primary
|
||||
assert.Equal(t, true, enablingRoutes[0].GetAdvertised())
|
||||
assert.Equal(t, true, enablingRoutes[0].GetEnabled())
|
||||
assert.Equal(t, true, enablingRoutes[0].GetIsPrimary(), "both subnet routers are up, expected r1 to be primary")
|
||||
assert.True(t, enablingRoutes[0].GetAdvertised())
|
||||
assert.True(t, enablingRoutes[0].GetEnabled())
|
||||
assert.True(t, enablingRoutes[0].GetIsPrimary(), "both subnet routers are up, expected r1 to be primary")
|
||||
|
||||
// Node 2 is not primary
|
||||
assert.Equal(t, true, enablingRoutes[1].GetAdvertised())
|
||||
assert.Equal(t, true, enablingRoutes[1].GetEnabled())
|
||||
assert.Equal(t, false, enablingRoutes[1].GetIsPrimary(), "both subnet routers are up, expected r2 to be non-primary")
|
||||
assert.True(t, enablingRoutes[1].GetAdvertised())
|
||||
assert.True(t, enablingRoutes[1].GetEnabled())
|
||||
assert.False(t, enablingRoutes[1].GetIsPrimary(), "both subnet routers are up, expected r2 to be non-primary")
|
||||
|
||||
// Verify that the client has routes from the primary machine
|
||||
srs1, err := subRouter1.Status()
|
||||
|
@ -446,14 +446,14 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
assert.Len(t, routesAfterMove, 2)
|
||||
|
||||
// Node 1 is not primary
|
||||
assert.Equal(t, true, routesAfterMove[0].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterMove[0].GetEnabled())
|
||||
assert.Equal(t, false, routesAfterMove[0].GetIsPrimary(), "r1 is down, expected r2 to be primary")
|
||||
assert.True(t, routesAfterMove[0].GetAdvertised())
|
||||
assert.True(t, routesAfterMove[0].GetEnabled())
|
||||
assert.False(t, routesAfterMove[0].GetIsPrimary(), "r1 is down, expected r2 to be primary")
|
||||
|
||||
// Node 2 is primary
|
||||
assert.Equal(t, true, routesAfterMove[1].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterMove[1].GetEnabled())
|
||||
assert.Equal(t, true, routesAfterMove[1].GetIsPrimary(), "r1 is down, expected r2 to be primary")
|
||||
assert.True(t, routesAfterMove[1].GetAdvertised())
|
||||
assert.True(t, routesAfterMove[1].GetEnabled())
|
||||
assert.True(t, routesAfterMove[1].GetIsPrimary(), "r1 is down, expected r2 to be primary")
|
||||
|
||||
srs2, err = subRouter2.Status()
|
||||
|
||||
|
@ -501,16 +501,16 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
assert.Len(t, routesAfterBothDown, 2)
|
||||
|
||||
// Node 1 is not primary
|
||||
assert.Equal(t, true, routesAfterBothDown[0].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterBothDown[0].GetEnabled())
|
||||
assert.Equal(t, false, routesAfterBothDown[0].GetIsPrimary(), "r1 and r2 is down, expected r2 to _still_ be primary")
|
||||
assert.True(t, routesAfterBothDown[0].GetAdvertised())
|
||||
assert.True(t, routesAfterBothDown[0].GetEnabled())
|
||||
assert.False(t, routesAfterBothDown[0].GetIsPrimary(), "r1 and r2 is down, expected r2 to _still_ be primary")
|
||||
|
||||
// Node 2 is primary
|
||||
// if the node goes down, but no other suitable route is
|
||||
// available, keep the last known good route.
|
||||
assert.Equal(t, true, routesAfterBothDown[1].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterBothDown[1].GetEnabled())
|
||||
assert.Equal(t, true, routesAfterBothDown[1].GetIsPrimary(), "r1 and r2 is down, expected r2 to _still_ be primary")
|
||||
assert.True(t, routesAfterBothDown[1].GetAdvertised())
|
||||
assert.True(t, routesAfterBothDown[1].GetEnabled())
|
||||
assert.True(t, routesAfterBothDown[1].GetIsPrimary(), "r1 and r2 is down, expected r2 to _still_ be primary")
|
||||
|
||||
// TODO(kradalby): Check client status
|
||||
// Both are expected to be down
|
||||
|
@ -560,14 +560,14 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
assert.Len(t, routesAfter1Up, 2)
|
||||
|
||||
// Node 1 is primary
|
||||
assert.Equal(t, true, routesAfter1Up[0].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfter1Up[0].GetEnabled())
|
||||
assert.Equal(t, true, routesAfter1Up[0].GetIsPrimary(), "r1 is back up, expected r1 to become be primary")
|
||||
assert.True(t, routesAfter1Up[0].GetAdvertised())
|
||||
assert.True(t, routesAfter1Up[0].GetEnabled())
|
||||
assert.True(t, routesAfter1Up[0].GetIsPrimary(), "r1 is back up, expected r1 to become be primary")
|
||||
|
||||
// Node 2 is not primary
|
||||
assert.Equal(t, true, routesAfter1Up[1].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfter1Up[1].GetEnabled())
|
||||
assert.Equal(t, false, routesAfter1Up[1].GetIsPrimary(), "r1 is back up, expected r1 to become be primary")
|
||||
assert.True(t, routesAfter1Up[1].GetAdvertised())
|
||||
assert.True(t, routesAfter1Up[1].GetEnabled())
|
||||
assert.False(t, routesAfter1Up[1].GetIsPrimary(), "r1 is back up, expected r1 to become be primary")
|
||||
|
||||
// Verify that the route is announced from subnet router 1
|
||||
clientStatus, err = client.Status()
|
||||
|
@ -614,14 +614,14 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
assert.Len(t, routesAfter2Up, 2)
|
||||
|
||||
// Node 1 is not primary
|
||||
assert.Equal(t, true, routesAfter2Up[0].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfter2Up[0].GetEnabled())
|
||||
assert.Equal(t, true, routesAfter2Up[0].GetIsPrimary(), "r1 and r2 is back up, expected r1 to _still_ be primary")
|
||||
assert.True(t, routesAfter2Up[0].GetAdvertised())
|
||||
assert.True(t, routesAfter2Up[0].GetEnabled())
|
||||
assert.True(t, routesAfter2Up[0].GetIsPrimary(), "r1 and r2 is back up, expected r1 to _still_ be primary")
|
||||
|
||||
// Node 2 is primary
|
||||
assert.Equal(t, true, routesAfter2Up[1].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfter2Up[1].GetEnabled())
|
||||
assert.Equal(t, false, routesAfter2Up[1].GetIsPrimary(), "r1 and r2 is back up, expected r1 to _still_ be primary")
|
||||
assert.True(t, routesAfter2Up[1].GetAdvertised())
|
||||
assert.True(t, routesAfter2Up[1].GetEnabled())
|
||||
assert.False(t, routesAfter2Up[1].GetIsPrimary(), "r1 and r2 is back up, expected r1 to _still_ be primary")
|
||||
|
||||
// Verify that the route is announced from subnet router 1
|
||||
clientStatus, err = client.Status()
|
||||
|
@ -677,14 +677,14 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
t.Logf("routes after disabling r1 %#v", routesAfterDisabling1)
|
||||
|
||||
// Node 1 is not primary
|
||||
assert.Equal(t, true, routesAfterDisabling1[0].GetAdvertised())
|
||||
assert.Equal(t, false, routesAfterDisabling1[0].GetEnabled())
|
||||
assert.Equal(t, false, routesAfterDisabling1[0].GetIsPrimary())
|
||||
assert.True(t, routesAfterDisabling1[0].GetAdvertised())
|
||||
assert.False(t, routesAfterDisabling1[0].GetEnabled())
|
||||
assert.False(t, routesAfterDisabling1[0].GetIsPrimary())
|
||||
|
||||
// Node 2 is primary
|
||||
assert.Equal(t, true, routesAfterDisabling1[1].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterDisabling1[1].GetEnabled())
|
||||
assert.Equal(t, true, routesAfterDisabling1[1].GetIsPrimary())
|
||||
assert.True(t, routesAfterDisabling1[1].GetAdvertised())
|
||||
assert.True(t, routesAfterDisabling1[1].GetEnabled())
|
||||
assert.True(t, routesAfterDisabling1[1].GetIsPrimary())
|
||||
|
||||
// Verify that the route is announced from subnet router 1
|
||||
clientStatus, err = client.Status()
|
||||
|
@ -735,14 +735,14 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
assert.Len(t, routesAfterEnabling1, 2)
|
||||
|
||||
// Node 1 is not primary
|
||||
assert.Equal(t, true, routesAfterEnabling1[0].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterEnabling1[0].GetEnabled())
|
||||
assert.Equal(t, false, routesAfterEnabling1[0].GetIsPrimary())
|
||||
assert.True(t, routesAfterEnabling1[0].GetAdvertised())
|
||||
assert.True(t, routesAfterEnabling1[0].GetEnabled())
|
||||
assert.False(t, routesAfterEnabling1[0].GetIsPrimary())
|
||||
|
||||
// Node 2 is primary
|
||||
assert.Equal(t, true, routesAfterEnabling1[1].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterEnabling1[1].GetEnabled())
|
||||
assert.Equal(t, true, routesAfterEnabling1[1].GetIsPrimary())
|
||||
assert.True(t, routesAfterEnabling1[1].GetAdvertised())
|
||||
assert.True(t, routesAfterEnabling1[1].GetEnabled())
|
||||
assert.True(t, routesAfterEnabling1[1].GetIsPrimary())
|
||||
|
||||
// Verify that the route is announced from subnet router 1
|
||||
clientStatus, err = client.Status()
|
||||
|
@ -795,9 +795,9 @@ func TestHASubnetRouterFailover(t *testing.T) {
|
|||
t.Logf("routes after deleting r2 %#v", routesAfterDeleting2)
|
||||
|
||||
// Node 1 is primary
|
||||
assert.Equal(t, true, routesAfterDeleting2[0].GetAdvertised())
|
||||
assert.Equal(t, true, routesAfterDeleting2[0].GetEnabled())
|
||||
assert.Equal(t, true, routesAfterDeleting2[0].GetIsPrimary())
|
||||
assert.True(t, routesAfterDeleting2[0].GetAdvertised())
|
||||
assert.True(t, routesAfterDeleting2[0].GetEnabled())
|
||||
assert.True(t, routesAfterDeleting2[0].GetIsPrimary())
|
||||
|
||||
// Verify that the route is announced from subnet router 1
|
||||
clientStatus, err = client.Status()
|
||||
|
@ -893,9 +893,9 @@ func TestEnableDisableAutoApprovedRoute(t *testing.T) {
|
|||
assert.Len(t, routes, 1)
|
||||
|
||||
// All routes should be auto approved and enabled
|
||||
assert.Equal(t, true, routes[0].GetAdvertised())
|
||||
assert.Equal(t, true, routes[0].GetEnabled())
|
||||
assert.Equal(t, true, routes[0].GetIsPrimary())
|
||||
assert.True(t, routes[0].GetAdvertised())
|
||||
assert.True(t, routes[0].GetEnabled())
|
||||
assert.True(t, routes[0].GetIsPrimary())
|
||||
|
||||
// Stop advertising route
|
||||
command = []string{
|
||||
|
@ -924,9 +924,9 @@ func TestEnableDisableAutoApprovedRoute(t *testing.T) {
|
|||
assert.Len(t, notAdvertisedRoutes, 1)
|
||||
|
||||
// Route is no longer advertised
|
||||
assert.Equal(t, false, notAdvertisedRoutes[0].GetAdvertised())
|
||||
assert.Equal(t, false, notAdvertisedRoutes[0].GetEnabled())
|
||||
assert.Equal(t, true, notAdvertisedRoutes[0].GetIsPrimary())
|
||||
assert.False(t, notAdvertisedRoutes[0].GetAdvertised())
|
||||
assert.False(t, notAdvertisedRoutes[0].GetEnabled())
|
||||
assert.True(t, notAdvertisedRoutes[0].GetIsPrimary())
|
||||
|
||||
// Advertise route again
|
||||
command = []string{
|
||||
|
@ -955,9 +955,9 @@ func TestEnableDisableAutoApprovedRoute(t *testing.T) {
|
|||
assert.Len(t, reAdvertisedRoutes, 1)
|
||||
|
||||
// All routes should be auto approved and enabled
|
||||
assert.Equal(t, true, reAdvertisedRoutes[0].GetAdvertised())
|
||||
assert.Equal(t, true, reAdvertisedRoutes[0].GetEnabled())
|
||||
assert.Equal(t, true, reAdvertisedRoutes[0].GetIsPrimary())
|
||||
assert.True(t, reAdvertisedRoutes[0].GetAdvertised())
|
||||
assert.True(t, reAdvertisedRoutes[0].GetEnabled())
|
||||
assert.True(t, reAdvertisedRoutes[0].GetIsPrimary())
|
||||
}
|
||||
|
||||
func TestAutoApprovedSubRoute2068(t *testing.T) {
|
||||
|
@ -1163,9 +1163,9 @@ func TestSubnetRouteACL(t *testing.T) {
|
|||
assert.Len(t, routes, 1)
|
||||
|
||||
for _, route := range routes {
|
||||
assert.Equal(t, true, route.GetAdvertised())
|
||||
assert.Equal(t, false, route.GetEnabled())
|
||||
assert.Equal(t, false, route.GetIsPrimary())
|
||||
assert.True(t, route.GetAdvertised())
|
||||
assert.False(t, route.GetEnabled())
|
||||
assert.False(t, route.GetIsPrimary())
|
||||
}
|
||||
|
||||
// Verify that no routes has been sent to the client,
|
||||
|
@ -1212,9 +1212,9 @@ func TestSubnetRouteACL(t *testing.T) {
|
|||
assert.Len(t, enablingRoutes, 1)
|
||||
|
||||
// Node 1 has active route
|
||||
assert.Equal(t, true, enablingRoutes[0].GetAdvertised())
|
||||
assert.Equal(t, true, enablingRoutes[0].GetEnabled())
|
||||
assert.Equal(t, true, enablingRoutes[0].GetIsPrimary())
|
||||
assert.True(t, enablingRoutes[0].GetAdvertised())
|
||||
assert.True(t, enablingRoutes[0].GetEnabled())
|
||||
assert.True(t, enablingRoutes[0].GetIsPrimary())
|
||||
|
||||
// Verify that the client has routes from the primary machine
|
||||
srs1, _ := subRouter1.Status()
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/puzpuzpuz/xsync/v3"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"tailscale.com/envknob"
|
||||
)
|
||||
|
@ -203,11 +204,11 @@ func (s *Scenario) ShutdownAssertNoPanics(t *testing.T) {
|
|||
|
||||
if t != nil {
|
||||
stdout, err := os.ReadFile(stdoutPath)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, string(stdout), "panic")
|
||||
|
||||
stderr, err := os.ReadFile(stderrPath)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
assert.NotContains(t, string(stderr), "panic")
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -44,6 +45,11 @@ var (
|
|||
errTailscaleCannotUpWithoutAuthkey = errors.New("cannot up without authkey")
|
||||
errTailscaleNotConnected = errors.New("tailscale not connected")
|
||||
errTailscaledNotReadyForLogin = errors.New("tailscaled not ready for login")
|
||||
errInvalidClientConfig = errors.New("verifiably invalid client config requested")
|
||||
)
|
||||
|
||||
const (
|
||||
VersionHead = "head"
|
||||
)
|
||||
|
||||
func errTailscaleStatus(hostname string, err error) error {
|
||||
|
@ -74,6 +80,13 @@ type TailscaleInContainer struct {
|
|||
withExtraHosts []string
|
||||
workdir string
|
||||
netfilter string
|
||||
|
||||
// build options, solely for HEAD
|
||||
buildConfig TailscaleInContainerBuildConfig
|
||||
}
|
||||
|
||||
type TailscaleInContainerBuildConfig struct {
|
||||
tags []string
|
||||
}
|
||||
|
||||
// Option represent optional settings that can be given to a
|
||||
|
@ -175,6 +188,22 @@ func WithNetfilter(state string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithBuildTag adds an additional value to the `-tags=` parameter
|
||||
// of the Go compiler, allowing callers to customize the Tailscale client build.
|
||||
// This option is only meaningful when invoked on **HEAD** versions of the client.
|
||||
// Attempts to use it with any other version is a bug in the calling code.
|
||||
func WithBuildTag(tag string) Option {
|
||||
return func(tsic *TailscaleInContainer) {
|
||||
if tsic.version != VersionHead {
|
||||
panic(errInvalidClientConfig)
|
||||
}
|
||||
|
||||
tsic.buildConfig.tags = append(
|
||||
tsic.buildConfig.tags, tag,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// New returns a new TailscaleInContainer instance.
|
||||
func New(
|
||||
pool *dockertest.Pool,
|
||||
|
@ -219,6 +248,12 @@ func New(
|
|||
}
|
||||
|
||||
if tsic.withWebsocketDERP {
|
||||
if version != VersionHead {
|
||||
return tsic, errInvalidClientConfig
|
||||
}
|
||||
|
||||
WithBuildTag("ts_debug_websockets")(tsic)
|
||||
|
||||
tailscaleOptions.Env = append(
|
||||
tailscaleOptions.Env,
|
||||
fmt.Sprintf("TS_DEBUG_DERP_WS_CLIENT=%t", tsic.withWebsocketDERP),
|
||||
|
@ -245,14 +280,36 @@ func New(
|
|||
}
|
||||
|
||||
var container *dockertest.Resource
|
||||
|
||||
if version != VersionHead {
|
||||
// build options are not meaningful with pre-existing images,
|
||||
// let's not lead anyone astray by pretending otherwise.
|
||||
defaultBuildConfig := TailscaleInContainerBuildConfig{}
|
||||
hasBuildConfig := !reflect.DeepEqual(defaultBuildConfig, tsic.buildConfig)
|
||||
if hasBuildConfig {
|
||||
return tsic, errInvalidClientConfig
|
||||
}
|
||||
}
|
||||
|
||||
switch version {
|
||||
case "head":
|
||||
case VersionHead:
|
||||
buildOptions := &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.tailscale-HEAD",
|
||||
ContextDir: dockerContextPath,
|
||||
BuildArgs: []docker.BuildArg{},
|
||||
}
|
||||
|
||||
buildTags := strings.Join(tsic.buildConfig.tags, ",")
|
||||
if len(buildTags) > 0 {
|
||||
buildOptions.BuildArgs = append(
|
||||
buildOptions.BuildArgs,
|
||||
docker.BuildArg{
|
||||
Name: "BUILD_TAGS",
|
||||
Value: buildTags,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
container, err = pool.BuildAndRunWithBuildOptions(
|
||||
buildOptions,
|
||||
tailscaleOptions,
|
||||
|
|
|
@ -183,3 +183,4 @@ nav:
|
|||
- Integration:
|
||||
- Reverse proxy: ref/integration/reverse-proxy.md
|
||||
- Web UI: ref/integration/web-ui.md
|
||||
- Tools: ref/integration/tools.md
|
||||
|
|
Loading…
Reference in a new issue