mirror of
https://github.com/juanfont/headscale.git
synced 2024-12-20 19:09:07 +01:00
Merge branch 'main' into flake-build-env
This commit is contained in:
commit
5c285afda5
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -7,5 +7,5 @@ contact_links:
|
||||
url: "https://github.com/juanfont/headscale/blob/main/docs"
|
||||
about: "Find documentation about how to configure and run headscale."
|
||||
- name: "headscale Discord community"
|
||||
url: "https://discord.com/invite/XcQxk2VHjx"
|
||||
url: "https://discord.gg/xGj2TuqyxY"
|
||||
about: "Please ask and answer questions about usage of headscale here."
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.17.7
|
||||
go-version: 1.18.0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
|
14
CHANGELOG.md
14
CHANGELOG.md
@ -1,6 +1,14 @@
|
||||
# CHANGELOG
|
||||
|
||||
## 0.15.0 (2022-xx-xx)
|
||||
## 0.16.0 (2022-xx-xx)
|
||||
|
||||
### Changes
|
||||
|
||||
- Headscale fails to serve if the ACL policy file cannot be parsed [#537](https://github.com/juanfont/headscale/pull/537)
|
||||
- Fix labels cardinality error when registering unknown pre-auth key [#519](https://github.com/juanfont/headscale/pull/519)
|
||||
- Fix send on closed channel crash in polling [#542](https://github.com/juanfont/headscale/pull/542)
|
||||
|
||||
## 0.15.0 (2022-03-20)
|
||||
|
||||
**Note:** Take a backup of your database before upgrading.
|
||||
|
||||
@ -19,7 +27,7 @@
|
||||
- Users can now use emails in ACL's groups [#372](https://github.com/juanfont/headscale/issues/372)
|
||||
- Add shorthand aliases for commands and subcommands [#376](https://github.com/juanfont/headscale/pull/376)
|
||||
- Add `/windows` endpoint for Windows configuration instructions + registry file download [#392](https://github.com/juanfont/headscale/pull/392)
|
||||
- Added embedded DERP server into Headscale [#388](https://github.com/juanfont/headscale/pull/388)
|
||||
- Added embedded DERP (and STUN) server into Headscale [#388](https://github.com/juanfont/headscale/pull/388)
|
||||
|
||||
### Changes
|
||||
|
||||
@ -29,6 +37,8 @@
|
||||
- Fix a limitation in the ACLs that prevented users to write rules with `*` as source [#374](https://github.com/juanfont/headscale/issues/374)
|
||||
- Reduce the overhead of marshal/unmarshal for Hostinfo, routes and endpoints by using specific types in Machine [#371](https://github.com/juanfont/headscale/pull/371)
|
||||
- Apply normalization function to FQDN on hostnames when hosts registers and retrieve informations [#363](https://github.com/juanfont/headscale/issues/363)
|
||||
- Fix a bug that prevented the use of `tailscale logout` with OIDC [#508](https://github.com/juanfont/headscale/issues/508)
|
||||
- Added Tailscale repo HEAD and unstable releases channel to the integration tests targets [#513](https://github.com/juanfont/headscale/pull/513)
|
||||
|
||||
## 0.14.0 (2022-02-24)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Builder image
|
||||
FROM docker.io/golang:1.17.8-bullseye AS build
|
||||
FROM docker.io/golang:1.18.0-bullseye AS build
|
||||
ENV GOPATH /go
|
||||
WORKDIR /go/src/headscale
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Builder image
|
||||
FROM docker.io/golang:1.17.8-alpine AS build
|
||||
FROM docker.io/golang:1.18.0-alpine AS build
|
||||
ENV GOPATH /go
|
||||
WORKDIR /go/src/headscale
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Builder image
|
||||
FROM docker.io/golang:1.17.8-bullseye AS build
|
||||
FROM docker.io/golang:1.18.0-bullseye AS build
|
||||
ENV GOPATH /go
|
||||
WORKDIR /go/src/headscale
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
FROM ubuntu:latest
|
||||
|
||||
ARG TAILSCALE_VERSION
|
||||
ARG TAILSCALE_VERSION=*
|
||||
ARG TAILSCALE_CHANNEL=stable
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y gnupg curl \
|
||||
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.gpg | apt-key add - \
|
||||
&& curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
|
||||
&& curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.gpg | apt-key add - \
|
||||
&& curl -fsSL https://pkgs.tailscale.com/${TAILSCALE_CHANNEL}/ubuntu/focal.list | tee /etc/apt/sources.list.d/tailscale.list \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y ca-certificates tailscale=${TAILSCALE_VERSION} dnsutils \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
21
Dockerfile.tailscale-HEAD
Normal file
21
Dockerfile.tailscale-HEAD
Normal file
@ -0,0 +1,21 @@
|
||||
FROM golang:latest
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y ca-certificates dnsutils git iptables \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
RUN git clone https://github.com/tailscale/tailscale.git
|
||||
|
||||
WORKDIR tailscale
|
||||
|
||||
RUN sh build_dist.sh tailscale.com/cmd/tailscale
|
||||
RUN sh build_dist.sh tailscale.com/cmd/tailscaled
|
||||
|
||||
RUN cp tailscale /usr/local/bin/
|
||||
RUN cp tailscaled /usr/local/bin/
|
||||
|
||||
ADD integration_test/etc_embedded_derp/tls/server.crt /usr/local/share/ca-certificates/
|
||||
RUN chmod 644 /usr/local/share/ca-certificates/server.crt
|
||||
|
||||
RUN update-ca-certificates
|
4
Makefile
4
Makefile
@ -1,5 +1,5 @@
|
||||
# Calculate version
|
||||
version = $(shell ./scripts/version-at-commit.sh)
|
||||
version = $(git describe --always --tags --dirty)
|
||||
|
||||
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
|
||||
|
||||
@ -10,7 +10,7 @@ PROTO_SOURCES = $(call rwildcard,,*.proto)
|
||||
|
||||
|
||||
build:
|
||||
GGO_ENABLED=0 go build -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go
|
||||
CGO_ENABLED=0 go build -trimpath -buildmode=pie -mod=readonly -ldflags "-s -w -X github.com/juanfont/headscale/cmd/headscale/cli.Version=$(version)" cmd/headscale/headscale.go
|
||||
|
||||
dev: lint test build
|
||||
|
||||
|
66
README.md
66
README.md
@ -4,7 +4,7 @@
|
||||
|
||||
An open source, self-hosted implementation of the Tailscale control server.
|
||||
|
||||
Join our [Discord](https://discord.gg/XcQxk2VHjx) server for a chat.
|
||||
Join our [Discord](https://discord.gg/c84AZQhmpx) server for a chat.
|
||||
|
||||
**Note:** Always select the same GitHub tag as the released version you use
|
||||
to ensure you have the correct example configuration and documentation.
|
||||
@ -232,6 +232,13 @@ make build
|
||||
<sub style="font-size:14px"><b>Nico</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/Niek>
|
||||
<img src=https://avatars.githubusercontent.com/u/213140?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Niek van der Maas/>
|
||||
<br />
|
||||
<sub style="font-size:14px"><b>Niek van der Maas</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/negbie>
|
||||
<img src=https://avatars.githubusercontent.com/u/20154956?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Eugen Biegler/>
|
||||
@ -239,6 +246,8 @@ make build
|
||||
<sub style="font-size:14px"><b>Eugen Biegler</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/qbit>
|
||||
<img src=https://avatars.githubusercontent.com/u/68368?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Aaron Bieber/>
|
||||
@ -246,8 +255,6 @@ make build
|
||||
<sub style="font-size:14px"><b>Aaron Bieber</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/fdelucchijr>
|
||||
<img src=https://avatars.githubusercontent.com/u/69133647?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Fernando De Lucchi/>
|
||||
@ -283,6 +290,15 @@ make build
|
||||
<sub style="font-size:14px"><b>Paul Tötterman</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/artemklevtsov>
|
||||
<img src=https://avatars.githubusercontent.com/u/603798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Artem Klevtsov/>
|
||||
<br />
|
||||
<sub style="font-size:14px"><b>Artem Klevtsov</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/cmars>
|
||||
<img src=https://avatars.githubusercontent.com/u/23741?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Casey Marshall/>
|
||||
@ -290,8 +306,6 @@ make build
|
||||
<sub style="font-size:14px"><b>Casey Marshall</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/SilverBut>
|
||||
<img src=https://avatars.githubusercontent.com/u/6560655?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Silver Bullet/>
|
||||
@ -320,6 +334,8 @@ make build
|
||||
<sub style="font-size:14px"><b>thomas</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/aberoham>
|
||||
<img src=https://avatars.githubusercontent.com/u/586805?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Abraham Ingersoll/>
|
||||
@ -328,14 +344,12 @@ make build
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/artemklevtsov>
|
||||
<img src=https://avatars.githubusercontent.com/u/603798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Artem Klevtsov/>
|
||||
<a href=https://github.com/aofei>
|
||||
<img src=https://avatars.githubusercontent.com/u/5037285?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Aofei Sheng/>
|
||||
<br />
|
||||
<sub style="font-size:14px"><b>Artem Klevtsov</b></sub>
|
||||
<sub style="font-size:14px"><b>Aofei Sheng</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/awoimbee>
|
||||
<img src=https://avatars.githubusercontent.com/u/22431493?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Arthur Woimbée/>
|
||||
@ -350,6 +364,13 @@ make build
|
||||
<sub style="font-size:14px"><b>Bryan Stenson</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/yangchuansheng>
|
||||
<img src=https://avatars.githubusercontent.com/u/15308462?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt= Carson Yang/>
|
||||
<br />
|
||||
<sub style="font-size:14px"><b> Carson Yang</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/fkr>
|
||||
<img src=https://avatars.githubusercontent.com/u/51063?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Felix Kronlage-Dammers/>
|
||||
@ -357,6 +378,8 @@ make build
|
||||
<sub style="font-size:14px"><b>Felix Kronlage-Dammers</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/felixonmars>
|
||||
<img src=https://avatars.githubusercontent.com/u/1006477?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Felix Yan/>
|
||||
@ -378,8 +401,6 @@ make build
|
||||
<sub style="font-size:14px"><b>Jamie Greeff</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/jimt>
|
||||
<img src=https://avatars.githubusercontent.com/u/180326?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Jim Tittsler/>
|
||||
@ -401,6 +422,8 @@ make build
|
||||
<sub style="font-size:14px"><b>rcursaru</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/renovate-bot>
|
||||
<img src=https://avatars.githubusercontent.com/u/25180681?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=WhiteSource Renovate/>
|
||||
@ -422,8 +445,6 @@ make build
|
||||
<sub style="font-size:14px"><b>Shaanan Cohney</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/m-tanner-dev0>
|
||||
<img src=https://avatars.githubusercontent.com/u/97977342?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Tanner/>
|
||||
@ -445,6 +466,8 @@ make build
|
||||
<sub style="font-size:14px"><b>The Gitter Badger</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/tianon>
|
||||
<img src=https://avatars.githubusercontent.com/u/161631?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Tianon Gravi/>
|
||||
@ -466,8 +489,6 @@ make build
|
||||
<sub style="font-size:14px"><b>Yang Bin</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/zekker6>
|
||||
<img src=https://avatars.githubusercontent.com/u/1367798?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Zakhar Bessarab/>
|
||||
@ -489,6 +510,8 @@ make build
|
||||
<sub style="font-size:14px"><b>bravechamp</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/derelm>
|
||||
<img src=https://avatars.githubusercontent.com/u/465155?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=derelm/>
|
||||
@ -496,6 +519,13 @@ make build
|
||||
<sub style="font-size:14px"><b>derelm</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/nning>
|
||||
<img src=https://avatars.githubusercontent.com/u/557430?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=henning mueller/>
|
||||
<br />
|
||||
<sub style="font-size:14px"><b>henning mueller</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/ignoramous>
|
||||
<img src=https://avatars.githubusercontent.com/u/852289?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=ignoramous/>
|
||||
@ -510,8 +540,6 @@ make build
|
||||
<sub style="font-size:14px"><b>lion24</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/pernila>
|
||||
<img src=https://avatars.githubusercontent.com/u/12460060?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=pernila/>
|
||||
@ -526,6 +554,8 @@ make build
|
||||
<sub style="font-size:14px"><b>Wakeful-Cloud</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
|
||||
<a href=https://github.com/xpzouying>
|
||||
<img src=https://avatars.githubusercontent.com/u/3946563?v=4 width="100;" style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=zy/>
|
||||
|
2
api.go
2
api.go
@ -573,7 +573,7 @@ func (h *Headscale) handleAuthKey(
|
||||
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", pak.Namespace.Name).
|
||||
Inc()
|
||||
} else {
|
||||
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error").Inc()
|
||||
machineRegistrations.WithLabelValues("new", RegisterMethodAuthKey, "error", "unknown").Inc()
|
||||
}
|
||||
|
||||
return
|
||||
|
23
app.go
23
app.go
@ -47,6 +47,14 @@ import (
|
||||
"tailscale.com/types/key"
|
||||
)
|
||||
|
||||
const (
|
||||
errSTUNAddressNotSet = Error("STUN address not set")
|
||||
errUnsupportedDatabase = Error("unsupported DB")
|
||||
errUnsupportedLetsEncryptChallengeType = Error(
|
||||
"unknown value for Lets Encrypt challenge type",
|
||||
)
|
||||
)
|
||||
|
||||
const (
|
||||
AuthPrefix = "Bearer "
|
||||
Postgres = "postgres"
|
||||
@ -58,11 +66,6 @@ const (
|
||||
registerCacheExpiration = time.Minute * 15
|
||||
registerCacheCleanup = time.Minute * 20
|
||||
|
||||
errUnsupportedDatabase = Error("unsupported DB")
|
||||
errUnsupportedLetsEncryptChallengeType = Error(
|
||||
"unknown value for Lets Encrypt challenge type",
|
||||
)
|
||||
|
||||
DisabledClientAuth = "disabled"
|
||||
RelaxedClientAuth = "relaxed"
|
||||
EnforcedClientAuth = "enforced"
|
||||
@ -124,7 +127,6 @@ type DERPConfig struct {
|
||||
ServerRegionID int
|
||||
ServerRegionCode string
|
||||
ServerRegionName string
|
||||
STUNEnabled bool
|
||||
STUNAddr string
|
||||
URLs []url.URL
|
||||
Paths []string
|
||||
@ -500,10 +502,13 @@ func (h *Headscale) Serve() error {
|
||||
h.DERPMap = GetDERPMap(h.cfg.DERP)
|
||||
|
||||
if h.cfg.DERP.ServerEnabled {
|
||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
||||
if h.cfg.DERP.STUNEnabled {
|
||||
go h.ServeSTUN()
|
||||
// When embedded DERP is enabled we always need a STUN server
|
||||
if h.cfg.DERP.STUNAddr == "" {
|
||||
return errSTUNAddressNotSet
|
||||
}
|
||||
|
||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
||||
go h.ServeSTUN()
|
||||
}
|
||||
|
||||
if h.cfg.DERP.AutoUpdate {
|
||||
|
@ -23,7 +23,7 @@ func init() {
|
||||
apiKeysCmd.AddCommand(listAPIKeys)
|
||||
|
||||
createAPIKeyCmd.Flags().
|
||||
DurationP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (30m, 24h, 365d...)")
|
||||
DurationP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
|
||||
|
||||
apiKeysCmd.AddCommand(createAPIKeyCmd)
|
||||
|
||||
|
@ -31,7 +31,7 @@ func init() {
|
||||
createPreAuthKeyCmd.PersistentFlags().
|
||||
Bool("ephemeral", false, "Preauthkey for ephemeral nodes")
|
||||
createPreAuthKeyCmd.Flags().
|
||||
DurationP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (30m, 24h, 365d...)")
|
||||
DurationP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
|
||||
}
|
||||
|
||||
var preauthkeysCmd = &cobra.Command{
|
||||
|
@ -55,6 +55,9 @@ func LoadConfig(path string) error {
|
||||
|
||||
viper.SetDefault("dns_config", nil)
|
||||
|
||||
viper.SetDefault("derp.server.enabled", false)
|
||||
viper.SetDefault("derp.server.stun.enabled", true)
|
||||
|
||||
viper.SetDefault("unix_socket", "/var/run/headscale.sock")
|
||||
viper.SetDefault("unix_socket_permission", "0o770")
|
||||
|
||||
@ -121,8 +124,11 @@ func GetDERPConfig() headscale.DERPConfig {
|
||||
serverRegionID := viper.GetInt("derp.server.region_id")
|
||||
serverRegionCode := viper.GetString("derp.server.region_code")
|
||||
serverRegionName := viper.GetString("derp.server.region_name")
|
||||
stunEnabled := viper.GetBool("derp.server.stun.enabled")
|
||||
stunAddr := viper.GetString("derp.server.stun.listen_addr")
|
||||
stunAddr := viper.GetString("derp.server.stun_listen_addr")
|
||||
|
||||
if serverEnabled && stunAddr == "" {
|
||||
log.Fatal().Msg("derp.server.stun_listen_addr must be set if derp.server.enabled is true")
|
||||
}
|
||||
|
||||
urlStrs := viper.GetStringSlice("derp.urls")
|
||||
|
||||
@ -149,7 +155,6 @@ func GetDERPConfig() headscale.DERPConfig {
|
||||
ServerRegionID: serverRegionID,
|
||||
ServerRegionCode: serverRegionCode,
|
||||
ServerRegionName: serverRegionName,
|
||||
STUNEnabled: stunEnabled,
|
||||
STUNAddr: stunAddr,
|
||||
URLs: urls,
|
||||
Paths: paths,
|
||||
@ -403,7 +408,7 @@ func getHeadscaleApp() (*headscale.Headscale, error) {
|
||||
aclPath := absPath(viper.GetString("acl_policy_path"))
|
||||
err = app.LoadACLPolicy(aclPath)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
log.Fatal().
|
||||
Str("path", aclPath).
|
||||
Err(err).
|
||||
Msg("Could not load the ACL policy")
|
||||
|
@ -69,11 +69,11 @@ derp:
|
||||
region_code: "headscale"
|
||||
region_name: "Headscale Embedded DERP"
|
||||
|
||||
# If enabled, also listens in UDP at the configured address for STUN connections to help on NAT traversal
|
||||
# Listens in UDP at the configured address for STUN connections to help on NAT traversal.
|
||||
# When the embedded DERP server is enabled stun_listen_addr MUST be defined.
|
||||
#
|
||||
# For more details on how this works, check this great article: https://tailscale.com/blog/how-tailscale-works/
|
||||
stun:
|
||||
enabled: false
|
||||
listen_addr: "0.0.0.0:3478"
|
||||
stun_listen_addr: "0.0.0.0:3478"
|
||||
|
||||
# List of externally available DERP maps encoded in JSON
|
||||
urls:
|
||||
|
@ -12,4 +12,4 @@ regions:
|
||||
ipv6: "2604:a880:400:d1::828:b001"
|
||||
stunport: 0
|
||||
stunonly: false
|
||||
derptestport: 0
|
||||
derpport: 0
|
||||
|
4
derp.go
4
derp.go
@ -148,7 +148,9 @@ func (h *Headscale) scheduledDERPMapUpdateWorker(cancelChan <-chan struct{}) {
|
||||
case <-ticker.C:
|
||||
log.Info().Msg("Fetching DERPMap updates")
|
||||
h.DERPMap = GetDERPMap(h.cfg.DERP)
|
||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
||||
if h.cfg.DERP.ServerEnabled {
|
||||
h.DERPMap.Regions[h.DERPServer.region.RegionID] = &h.DERPServer.region
|
||||
}
|
||||
|
||||
namespaces, err := h.ListNamespaces()
|
||||
if err != nil {
|
||||
|
@ -77,17 +77,15 @@ func (h *Headscale) generateRegionLocalDERP() (tailcfg.DERPRegion, error) {
|
||||
},
|
||||
}
|
||||
|
||||
if h.cfg.DERP.STUNEnabled {
|
||||
_, portStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
|
||||
if err != nil {
|
||||
return tailcfg.DERPRegion{}, err
|
||||
}
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return tailcfg.DERPRegion{}, err
|
||||
}
|
||||
localDERPregion.Nodes[0].STUNPort = port
|
||||
_, portSTUNStr, err := net.SplitHostPort(h.cfg.DERP.STUNAddr)
|
||||
if err != nil {
|
||||
return tailcfg.DERPRegion{}, err
|
||||
}
|
||||
portSTUN, err := strconv.Atoi(portSTUNStr)
|
||||
if err != nil {
|
||||
return tailcfg.DERPRegion{}, err
|
||||
}
|
||||
localDERPregion.Nodes[0].STUNPort = portSTUN
|
||||
|
||||
return localDERPregion, nil
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
This page contains the official and community contributed documentation for `headscale`.
|
||||
|
||||
If you are having trouble with following the documentation or get unexpected results,
|
||||
please ask on [Discord](https://discord.gg/XcQxk2VHjx) instead of opening an Issue.
|
||||
please ask on [Discord](https://discord.gg/c84AZQhmpx) instead of opening an Issue.
|
||||
|
||||
## Official documentation
|
||||
|
||||
|
@ -14,8 +14,8 @@ not work with alternatives like [Podman](https://podman.io). The Docker image ca
|
||||
1. Prepare a directory on the host Docker node in your directory of choice, used to hold `headscale` configuration and the [SQLite](https://www.sqlite.org/) database:
|
||||
|
||||
```shell
|
||||
mkdir ./headscale && cd ./headscale
|
||||
mkdir ./config
|
||||
mkdir -p ./headscale/config
|
||||
cd ./headscale
|
||||
```
|
||||
|
||||
2. Create an empty SQlite datebase in the headscale directory:
|
||||
@ -45,6 +45,17 @@ touch ./config/config.yaml
|
||||
```
|
||||
|
||||
Modify the config file to your preferences before launching Docker container.
|
||||
Here are some settings that you likely want:
|
||||
|
||||
```yaml
|
||||
server_url: http://your-host-name:8080 # Change to your hostname or host IP
|
||||
# Listen to 0.0.0.0 so it's accessible outside the container
|
||||
metrics_listen_addr: 0.0.0.0:9090
|
||||
# The default /var/lib/headscale path is not writable in the container
|
||||
private_key_path: /etc/headscale/private.key
|
||||
# The default /var/lib/headscale path is not writable in the container
|
||||
db_path: /etc/headscale/db.sqlite
|
||||
```
|
||||
|
||||
4. Start the headscale server while working in the host headscale directory:
|
||||
|
||||
@ -61,6 +72,8 @@ docker run \
|
||||
|
||||
```
|
||||
|
||||
Note: use `0.0.0.0:8080:8080` instead of `127.0.0.1:8080:8080` if you want to expose the container externally.
|
||||
|
||||
This command will mount `config/` under `/etc/headscale`, forward port 8080 out of the container so the
|
||||
`headscale` instance becomes available and then detach so headscale runs in the background.
|
||||
|
||||
@ -87,7 +100,8 @@ curl http://127.0.0.1:9090/metrics
|
||||
6. Create a namespace ([tailnet](https://tailscale.com/kb/1136/tailnet/)):
|
||||
|
||||
```shell
|
||||
docker exec headscale -- headscale namespaces create myfirstnamespace
|
||||
docker exec headscale \
|
||||
headscale namespaces create myfirstnamespace
|
||||
```
|
||||
|
||||
### Register a machine (normal login)
|
||||
@ -101,7 +115,7 @@ tailscale up --login-server YOUR_HEADSCALE_URL
|
||||
To register a machine when running `headscale` in a container, take the headscale command and pass it to the container:
|
||||
|
||||
```shell
|
||||
docker exec headscale -- \
|
||||
docker exec headscale \
|
||||
headscale --namespace myfirstnamespace nodes register --key <YOU_+MACHINE_KEY>
|
||||
```
|
||||
|
||||
@ -110,7 +124,7 @@ docker exec headscale -- \
|
||||
Generate a key using the command line:
|
||||
|
||||
```shell
|
||||
docker exec headscale -- \
|
||||
docker exec headscale \
|
||||
headscale --namespace myfirstnamespace preauthkeys create --reusable --expiration 24h
|
||||
```
|
||||
|
||||
|
@ -178,7 +178,7 @@ systemctl status headscale
|
||||
Verify `headscale` is available:
|
||||
|
||||
```shell
|
||||
curl http://127.0.0.1:8080/metrics
|
||||
curl http://127.0.0.1:9090/metrics
|
||||
```
|
||||
|
||||
`headscale` will now run in the background and start at boot.
|
||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/juanfont/headscale
|
||||
|
||||
go 1.17
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/AlecAivazis/survey/v2 v2.3.2
|
||||
|
@ -72,7 +72,7 @@ func (s *IntegrationCLITestSuite) SetupTest() {
|
||||
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
|
||||
s.headscale = *pheadscale
|
||||
} else {
|
||||
log.Fatalf("Could not start resource: %s", err)
|
||||
log.Fatalf("Could not start headscale container: %s", err)
|
||||
}
|
||||
fmt.Println("Created headscale container")
|
||||
|
||||
|
@ -20,7 +20,7 @@ var (
|
||||
IpPrefix4 = netaddr.MustParseIPPrefix("100.64.0.0/10")
|
||||
IpPrefix6 = netaddr.MustParseIPPrefix("fd7a:115c:a1e0::/48")
|
||||
|
||||
tailscaleVersions = []string{"1.22.0", "1.20.4", "1.18.2", "1.16.2", "1.14.3", "1.12.3"}
|
||||
tailscaleVersions = []string{"head", "unstable", "1.22.2", "1.20.4", "1.18.2", "1.16.2", "1.14.3", "1.12.3"}
|
||||
)
|
||||
|
||||
type TestNamespace struct {
|
||||
@ -128,6 +128,49 @@ func DockerAllowNetworkAdministration(config *docker.HostConfig) {
|
||||
})
|
||||
}
|
||||
|
||||
func getDockerBuildOptions(version string) *dockertest.BuildOptions {
|
||||
var tailscaleBuildOptions *dockertest.BuildOptions
|
||||
switch version {
|
||||
case "head":
|
||||
tailscaleBuildOptions = &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.tailscale-HEAD",
|
||||
ContextDir: ".",
|
||||
BuildArgs: []docker.BuildArg{},
|
||||
}
|
||||
case "unstable":
|
||||
tailscaleBuildOptions = &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.tailscale",
|
||||
ContextDir: ".",
|
||||
BuildArgs: []docker.BuildArg{
|
||||
{
|
||||
Name: "TAILSCALE_VERSION",
|
||||
Value: "*", // Installs the latest version https://askubuntu.com/a/824926
|
||||
},
|
||||
{
|
||||
Name: "TAILSCALE_CHANNEL",
|
||||
Value: "unstable",
|
||||
},
|
||||
},
|
||||
}
|
||||
default:
|
||||
tailscaleBuildOptions = &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.tailscale",
|
||||
ContextDir: ".",
|
||||
BuildArgs: []docker.BuildArg{
|
||||
{
|
||||
Name: "TAILSCALE_VERSION",
|
||||
Value: version,
|
||||
},
|
||||
{
|
||||
Name: "TAILSCALE_CHANNEL",
|
||||
Value: "stable",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return tailscaleBuildOptions
|
||||
}
|
||||
|
||||
func getIPs(
|
||||
tailscales map[string]dockertest.Resource,
|
||||
) (map[string][]netaddr.IP, error) {
|
||||
|
@ -121,7 +121,7 @@ func (s *IntegrationDERPTestSuite) SetupSuite() {
|
||||
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
|
||||
s.headscale = *pheadscale
|
||||
} else {
|
||||
log.Fatalf("Could not start resource: %s", err)
|
||||
log.Fatalf("Could not start headscale container: %s", err)
|
||||
}
|
||||
log.Println("Created headscale container to test DERP")
|
||||
|
||||
@ -245,16 +245,8 @@ func (s *IntegrationDERPTestSuite) Join(
|
||||
|
||||
func (s *IntegrationDERPTestSuite) tailscaleContainer(identifier, version string, network dockertest.Network,
|
||||
) (string, *dockertest.Resource) {
|
||||
tailscaleBuildOptions := &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.tailscale",
|
||||
ContextDir: ".",
|
||||
BuildArgs: []docker.BuildArg{
|
||||
{
|
||||
Name: "TAILSCALE_VERSION",
|
||||
Value: version,
|
||||
},
|
||||
},
|
||||
}
|
||||
tailscaleBuildOptions := getDockerBuildOptions(version)
|
||||
|
||||
hostname := fmt.Sprintf(
|
||||
"tailscale-%s-%s",
|
||||
strings.Replace(version, ".", "-", -1),
|
||||
@ -279,7 +271,7 @@ func (s *IntegrationDERPTestSuite) tailscaleContainer(identifier, version string
|
||||
DockerAllowNetworkAdministration,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not start resource: %s", err)
|
||||
log.Fatalf("Could not start tailscale container version %s: %s", version, err)
|
||||
}
|
||||
log.Printf("Created %s container\n", hostname)
|
||||
|
||||
|
@ -168,16 +168,8 @@ func (s *IntegrationTestSuite) Join(
|
||||
func (s *IntegrationTestSuite) tailscaleContainer(
|
||||
namespace, identifier, version string,
|
||||
) (string, *dockertest.Resource) {
|
||||
tailscaleBuildOptions := &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.tailscale",
|
||||
ContextDir: ".",
|
||||
BuildArgs: []docker.BuildArg{
|
||||
{
|
||||
Name: "TAILSCALE_VERSION",
|
||||
Value: version,
|
||||
},
|
||||
},
|
||||
}
|
||||
tailscaleBuildOptions := getDockerBuildOptions(version)
|
||||
|
||||
hostname := fmt.Sprintf(
|
||||
"%s-tailscale-%s-%s",
|
||||
namespace,
|
||||
@ -200,7 +192,7 @@ func (s *IntegrationTestSuite) tailscaleContainer(
|
||||
DockerAllowNetworkAdministration,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not start resource: %s", err)
|
||||
log.Fatalf("Could not start tailscale container version %s: %s", version, err)
|
||||
}
|
||||
log.Printf("Created %s container\n", hostname)
|
||||
|
||||
@ -249,7 +241,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
|
||||
if pheadscale, err := s.pool.BuildAndRunWithBuildOptions(headscaleBuildOptions, headscaleOptions, DockerRestartPolicy); err == nil {
|
||||
s.headscale = *pheadscale
|
||||
} else {
|
||||
log.Fatalf("Could not start resource: %s", err)
|
||||
log.Fatalf("Could not start headscale container: %s", err)
|
||||
}
|
||||
log.Println("Created headscale container")
|
||||
|
||||
|
@ -24,6 +24,5 @@ derp:
|
||||
region_id: 999
|
||||
region_code: "headscale"
|
||||
region_name: "Headscale Embedded DERP"
|
||||
stun:
|
||||
enabled: true
|
||||
listen_addr: "0.0.0.0:3478"
|
||||
|
||||
stun_listen_addr: "0.0.0.0:3478"
|
||||
|
7
oidc.go
7
oidc.go
@ -10,6 +10,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/gin-gonic/gin"
|
||||
@ -129,6 +130,10 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
|
||||
|
||||
oauth2Token, err := h.oauth2Config.Exchange(context.Background(), code)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Caller().
|
||||
Msg("Could not exchange code for token")
|
||||
ctx.String(http.StatusBadRequest, "Could not exchange code for token")
|
||||
|
||||
return
|
||||
@ -229,7 +234,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
|
||||
Str("machine", machine.Name).
|
||||
Msg("machine already registered, reauthenticating")
|
||||
|
||||
h.RefreshMachine(machine, *machine.Expiry)
|
||||
h.RefreshMachine(machine, time.Time{})
|
||||
|
||||
var content bytes.Buffer
|
||||
if err := oidcCallbackTemplate.Execute(&content, oidcCallbackTemplateConfig{
|
||||
|
68
poll.go
68
poll.go
@ -175,32 +175,13 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
|
||||
Str("machine", machine.Name).
|
||||
Msg("Loading or creating update channel")
|
||||
|
||||
// TODO: could probably remove all that duplication once generics land.
|
||||
closeChanWithLog := func(channel interface{}, name string) {
|
||||
log.Trace().
|
||||
Str("handler", "PollNetMap").
|
||||
Str("machine", machine.Name).
|
||||
Str("channel", "Done").
|
||||
Msg(fmt.Sprintf("Closing %s channel", name))
|
||||
|
||||
switch c := channel.(type) {
|
||||
case (chan struct{}):
|
||||
close(c)
|
||||
|
||||
case (chan []byte):
|
||||
close(c)
|
||||
}
|
||||
}
|
||||
|
||||
const chanSize = 8
|
||||
updateChan := make(chan struct{}, chanSize)
|
||||
defer closeChanWithLog(updateChan, "updateChan")
|
||||
|
||||
pollDataChan := make(chan []byte, chanSize)
|
||||
defer closeChanWithLog(pollDataChan, "pollDataChan")
|
||||
defer closeChanWithLog(pollDataChan, machine.Name, "pollDataChan")
|
||||
|
||||
keepAliveChan := make(chan []byte)
|
||||
defer closeChanWithLog(keepAliveChan, "keepAliveChan")
|
||||
|
||||
if req.OmitPeers && !req.Stream {
|
||||
log.Info().
|
||||
@ -273,7 +254,27 @@ func (h *Headscale) PollNetMapStream(
|
||||
updateChan chan struct{},
|
||||
) {
|
||||
{
|
||||
ctx, cancel := context.WithCancel(ctx.Request.Context())
|
||||
machine, err := h.GetMachineByMachineKey(machineKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
log.Warn().
|
||||
Str("handler", "PollNetMap").
|
||||
Msgf("Ignoring request, cannot find machine with key %s", machineKey.String())
|
||||
ctx.String(http.StatusUnauthorized, "")
|
||||
|
||||
return
|
||||
}
|
||||
log.Error().
|
||||
Str("handler", "PollNetMap").
|
||||
Msgf("Failed to fetch machine from the database with Machine key: %s", machineKey.String())
|
||||
ctx.String(http.StatusInternalServerError, "")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(ctx.Request.Context(), "machineName", machine.Name)
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
go h.scheduledPollWorker(
|
||||
@ -564,8 +565,8 @@ func (h *Headscale) PollNetMapStream(
|
||||
|
||||
func (h *Headscale) scheduledPollWorker(
|
||||
ctx context.Context,
|
||||
updateChan chan<- struct{},
|
||||
keepAliveChan chan<- []byte,
|
||||
updateChan chan struct{},
|
||||
keepAliveChan chan []byte,
|
||||
machineKey key.MachinePublic,
|
||||
mapRequest tailcfg.MapRequest,
|
||||
machine *Machine,
|
||||
@ -573,6 +574,17 @@ func (h *Headscale) scheduledPollWorker(
|
||||
keepAliveTicker := time.NewTicker(keepAliveInterval)
|
||||
updateCheckerTicker := time.NewTicker(updateCheckInterval)
|
||||
|
||||
defer closeChanWithLog(
|
||||
updateChan,
|
||||
fmt.Sprint(ctx.Value("machineName")),
|
||||
"updateChan",
|
||||
)
|
||||
defer closeChanWithLog(
|
||||
keepAliveChan,
|
||||
fmt.Sprint(ctx.Value("machineName")),
|
||||
"updateChan",
|
||||
)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@ -606,3 +618,13 @@ func (h *Headscale) scheduledPollWorker(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func closeChanWithLog[C chan []byte | chan struct{}](channel C, machine, name string) {
|
||||
log.Trace().
|
||||
Str("handler", "PollNetMap").
|
||||
Str("machine", machine).
|
||||
Str("channel", "Done").
|
||||
Msg(fmt.Sprintf("Closing %s channel", name))
|
||||
|
||||
close(channel)
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e -o pipefail
|
||||
commit="$1"
|
||||
versionglob="v[0-9].[0-9]*.[0-9]*"
|
||||
devsuffix=".dev"
|
||||
if [ -z "$commit" ]; then
|
||||
commit=`git log -n1 --first-parent "--format=format:%h"`
|
||||
fi
|
||||
|
||||
# automatically assign version
|
||||
#
|
||||
# handles the following cases:
|
||||
#
|
||||
# 0. no tags on the repository. Print "dev".
|
||||
#
|
||||
# 1. no local modifications and commit is directly tagged. Print tag.
|
||||
#
|
||||
# 2. no local modifications and commit is not tagged. Take greatest version tag in repo X.Y.Z and assign X.Y.(Z+1). Print that + $devsuffix + $timestamp.
|
||||
#
|
||||
# 3. local modifications. Print "dev".
|
||||
|
||||
tags=$(git tag)
|
||||
if [[ -z "$tags" ]]; then
|
||||
echo "dev"
|
||||
elif `git diff --quiet 2>/dev/null`; then
|
||||
tagged=$(git tag --points-at "$commit")
|
||||
if [[ -n "$tagged" ]] ; then
|
||||
echo $tagged
|
||||
else
|
||||
nearest_tag=$(git describe --tags --abbrev=0 --match "$versionglob" "$commit")
|
||||
v=$(echo $nearest_tag | perl -pe 's/(\d+)$/$1+1/e')
|
||||
isodate=$(TZ=UTC git log -n1 --format=%cd --date=iso "$commit")
|
||||
ts=$(TZ=UTC date --date="$isodate" "+%Y%m%d%H%M%S")
|
||||
echo "${v}${devsuffix}${ts}"
|
||||
fi
|
||||
else
|
||||
echo "dev"
|
||||
fi
|
Loading…
Reference in New Issue
Block a user