Compare commits

...
5 Commits
Author SHA1 Message Date
Jakob Borg 2ca95cf149 build: give macos security set-key-partition-list the correct password
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-09-08 08:57:55 +02:00
Jakob Borg 62f6060ceb Merge branch 'infrastructure'
* infrastructure:
  chore(discosrv): add debug headers to ping response
  chore(stupgrades): allow templating the asset URL
2026-09-08 08:07:35 +02:00
Jakob Borg c10408263e chore(discosrv): add debug headers to ping response
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-09-08 08:06:31 +02:00
Jakob Borg 6c25cdebf6 chore(stupgrades): allow templating the asset URL
Signed-off-by: Jakob Borg <jakob@kastelo.net>
2026-09-08 08:06:31 +02:00
Syncthing Release Automation ed8d87f251 chore(gui, man, authors): update docs, translations, and contributors 2026-09-07 03:52:21 +00:00
20 changed files with 73 additions and 47 deletions

No files matched your search

+1 -1
View File
@@ -445,7 +445,7 @@ jobs:
CERTIFICATE_PATH=$RUNNER_TEMP/codesign.p12
echo "$DEVELOPER_ID_CERTIFICATE_BASE64" | base64 -d -o "$CERTIFICATE_PATH"
security import "$CERTIFICATE_PATH" -k "$KEYCHAIN_PATH" -P "$DEVELOPER_ID_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign
security set-key-partition-list -S apple-tool:,apple: -s -k actions "$KEYCHAIN_PATH"
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
# Set the codesign identity for following steps
echo "CODESIGN_IDENTITY=$CODESIGN_IDENTITY" >> $GITHUB_ENV
+26 -9
View File
@@ -7,6 +7,7 @@
package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -20,6 +21,7 @@ import (
"strconv"
"strings"
"sync"
"text/template"
"time"
"github.com/alecthomas/kong"
@@ -31,11 +33,12 @@ import (
)
type cli struct {
Listen string `default:":8080" help:"Listen address"`
MetricsListen string `default:":8082" help:"Listen address for metrics"`
URL string `short:"u" default:"https://api.github.com/repos/syncthing/syncthing/releases?per_page=25" help:"GitHub releases url"`
Forward []string `short:"f" help:"Forwarded pages, format: /path->https://example/com/url"`
CacheTime time.Duration `default:"15m" help:"Cache time"`
Listen string `default:":8080" help:"Listen address"`
MetricsListen string `default:":8082" help:"Listen address for metrics"`
URL string `short:"u" default:"https://api.github.com/repos/syncthing/syncthing/releases?per_page=25" help:"GitHub releases url"`
Forward []string `short:"f" help:"Forwarded pages, format: /path->https://example/com/url"`
CacheTime time.Duration `default:"15m" help:"Cache time"`
AssetURLTemplate string `help:"Template for asset URLs, blank to use GitHub default" env:"ASSET_URL_TEMPLATE"`
}
func main() {
@@ -69,7 +72,7 @@ func server(params *cli) error {
}()
}
cache := &cachedReleases{url: params.URL}
cache := &cachedReleases{url: params.URL, assetURLTemplate: params.AssetURLTemplate}
if err := cache.Update(context.Background()); err != nil {
return fmt.Errorf("initial cache update: %w", err)
} else {
@@ -266,6 +269,7 @@ func filterForCompatibility(rels []upgrade.Release, ua, osv string) []upgrade.Re
type cachedReleases struct {
url string
assetURLTemplate string
mut sync.RWMutex
current []upgrade.Release
latestRel, latestPre string
@@ -278,7 +282,7 @@ func (c *cachedReleases) Releases() []upgrade.Release {
}
func (c *cachedReleases) Update(ctx context.Context) error {
rels, err := fetchGithubReleases(ctx, c.url)
rels, err := fetchGithubReleases(ctx, c.url, c.assetURLTemplate)
if err != nil {
return err
}
@@ -306,7 +310,7 @@ func (c *cachedReleases) Update(ctx context.Context) error {
return nil
}
func fetchGithubReleases(ctx context.Context, url string) ([]upgrade.Release, error) {
func fetchGithubReleases(ctx context.Context, url, assetURLTemplate string) ([]upgrade.Release, error) {
req, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, url, nil)
if err != nil {
metricHTTPRequests.WithLabelValues("github-releases", "error").Inc()
@@ -325,12 +329,25 @@ func fetchGithubReleases(ctx context.Context, url string) ([]upgrade.Release, er
}
metricHTTPRequests.WithLabelValues("github-releases", "success").Inc()
tpl, err := template.New("asset").Parse(assetURLTemplate)
if err != nil {
return nil, err
}
// Move the URL used for browser downloads to the URL field, and remove
// the browser URL field. This avoids going via the GitHub API for
// downloads, since Syncthing uses the URL field.
for _, rel := range rels {
for j, asset := range rel.Assets {
rel.Assets[j].URL = asset.BrowserURL
if assetURLTemplate != "" {
buf := new(bytes.Buffer)
if err := tpl.Execute(buf, map[string]any{"Release": rel, "Asset": asset}); err != nil {
return nil, err
}
rel.Assets[j].URL = buf.String()
} else {
rel.Assets[j].URL = asset.BrowserURL
}
rel.Assets[j].BrowserURL = ""
}
}
+23 -14
View File
@@ -23,6 +23,7 @@ import (
"net"
"net/http"
"net/url"
"os"
"slices"
"strconv"
"strings"
@@ -154,21 +155,10 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
slog.Debug("Handling request", "id", reqID, "method", req.Method, "url", req.URL, "proto", req.Proto)
remoteAddr := &net.TCPAddr{
IP: nil,
Port: -1,
}
var remoteAddr *net.TCPAddr
if s.useHTTP {
// X-Forwarded-For can have multiple client IPs; split using the comma separator
forwardIP, _, _ := strings.Cut(req.Header.Get("X-Forwarded-For"), ",")
// net.ParseIP will return nil if leading/trailing whitespace exists; use strings.TrimSpace()
remoteAddr.IP = net.ParseIP(strings.TrimSpace(forwardIP))
if parsedPort, err := strconv.ParseInt(req.Header.Get("X-Client-Port"), 10, 0); err == nil {
remoteAddr.Port = int(parsedPort)
}
remoteAddr = forwardedRemoteAddr(req)
} else {
var err error
remoteAddr, err = net.ResolveTCPAddr("tcp", req.RemoteAddr)
@@ -191,6 +181,21 @@ func (s *apiSrv) handler(w http.ResponseWriter, req *http.Request) {
}
}
func forwardedRemoteAddr(req *http.Request) *net.TCPAddr {
forwardIP, _, _ := strings.Cut(req.Header.Get("X-Forwarded-For"), ",")
remoteAddr := &net.TCPAddr{
IP: net.ParseIP(strings.TrimSpace(forwardIP)),
Port: -1,
}
if parsedPort, err := strconv.ParseInt(req.Header.Get("X-Client-Port"), 10, 0); err == nil {
remoteAddr.Port = int(parsedPort)
}
return remoteAddr
}
func (s *apiSrv) handleGET(w http.ResponseWriter, req *http.Request) {
reqID := req.Context().Value(idKey).(requestID)
@@ -327,7 +332,11 @@ func (s *apiSrv) handleAnnounce(deviceID protocol.DeviceID, addresses []string)
return s.db.merge(&deviceID, dbAddrs, seen)
}
func handlePing(w http.ResponseWriter, _ *http.Request) {
func handlePing(w http.ResponseWriter, req *http.Request) {
hostname, _ := os.Hostname()
w.Header().Set("Discovery-Server-Instance", hostname)
w.Header().Set("Discovery-Client-Address", req.RemoteAddr)
w.Header().Set("Discovery-Client-Remote", forwardedRemoteAddr(req).String())
w.WriteHeader(http.StatusNoContent)
}
+1 -1
View File
@@ -47,7 +47,7 @@
"Automatic upgrade now offers the choice between stable releases and release candidates.": "Le système de mise à jour automatique propose le choix entre versions stables et versions préliminaires.",
"Automatic upgrades": "Mises à jour automatiques",
"Automatic upgrades are always enabled for candidate releases.": "Les mises à jour automatiques sont toujours activées pour les versions mineures.",
"Automatically create or share folders that this device advertises at the default path.": "Automatiquement créer dans le chemin par défaut les partages auxquels cet appareil vous propose de participer, ou accepter leur partage s'ils pré-existent.",
"Automatically create or share folders that this device advertises at the default path.": "Accepter automatiquement ses propositions de partage. Si le partage n'existe pas déjà ici, le répertoire correspondant sera créé dans le chemin par défaut.",
"Available debug logging facilities:": "Outils de débogage disponibles :",
"Be careful!": "Faites attention !",
"Block Indexing": "Indexation des blocs",
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STDISCOSRV" "1" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "STDISCOSRV" "1" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
stdiscosrv \- Syncthing Discovery Server
.SH SYNOPSIS
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "STRELAYSRV" "1" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "STRELAYSRV" "1" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
strelaysrv \- Syncthing Relay Server
.SH SYNOPSIS
+1 -1
View File
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-BEP" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-BEP" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-bep \- Block Exchange Protocol v1
.SH INTRODUCTION AND DEFINITIONS
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-CONFIG" "5" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-CONFIG" "5" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-config \- Syncthing Configuration
.SH OVERVIEW
+7 -7
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-DEVICE-IDS" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-DEVICE-IDS" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-device-ids \- Understanding Device IDs
.sp
@@ -40,12 +40,12 @@ the public key in use.
To understand device IDs we need to look at the underlying mechanisms. At first
startup, Syncthing will create a public/private keypair.
.sp
Currently this is a 384 bit ECDSA key (3072 bit RSA prior to v0.12.5,
which is what is used as an example in this article). The keys are saved in
the form of the private key (\fBkey.pem\fP) and a self signed certificate
(\fBcert.pem\fP). The self signing part doesnt actually add any security or
functionality as far as Syncthing is concerned but it enables the use of the
keys in a standard TLS exchange.
Currently this is a 256 bit Ed25519 key (384 bit ECDSA prior to v2.0.0 and
3072 bit RSA prior to v0.12.5, which is what is used as an example in this
article). The keys are saved in the form of the private key (\fBkey.pem\fP) and a
self signed certificate (\fBcert.pem\fP). The self signing part doesnt actually
add any security or functionality as far as Syncthing is concerned but it enables
the use of the keys in a standard TLS exchange.
.sp
The typical certificate will look something like this, inspected with
\fBopenssl x509\fP:
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-EVENT-API" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-EVENT-API" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-event-api \- Event API
.SH DESCRIPTION
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-FAQ" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-FAQ" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-faq \- Frequently Asked Questions
.INDENT 0.0
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-GLOBALDISCO" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-GLOBALDISCO" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-globaldisco \- Global Discovery Protocol v3
.SH ANNOUNCEMENTS
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-LOCALDISCO" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-LOCALDISCO" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-localdisco \- Local Discovery Protocol v4
.SH MODE OF OPERATION
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-NETWORKING" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-NETWORKING" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-networking \- Firewall Setup
.SH ROUTER SETUP
+1 -1
View File
@@ -28,7 +28,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-RELAY" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-RELAY" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-relay \- Relay Protocol v1
.SH WHAT IS A RELAY?
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-REST-API" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-REST-API" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-rest-api \- REST API
.sp
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-SECURITY" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-SECURITY" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-security \- Security Principles
.sp
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-STIGNORE" "5" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-STIGNORE" "5" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-stignore \- Prevent files from being synchronized to other nodes
.SH SYNOPSIS
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING-VERSIONING" "7" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING-VERSIONING" "7" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing-versioning \- Keep automatic backups of deleted files by other nodes
.sp
+1 -1
View File
@@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]
.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]
.in \\n[rst2man-indent\\n[rst2man-indent-level]]u
..
.TH "SYNCTHING" "1" "Aug 12, 2026" "v2.1.0" "Syncthing"
.TH "SYNCTHING" "1" "Sep 06, 2026" "v2.1.0" "Syncthing"
.SH NAME
syncthing \- Syncthing
.SH SYNOPSIS