Add provisioning blueprint file

This commit is contained in:
Owen
2026-04-02 21:39:59 -04:00
parent 8d82460a76
commit f4d071fe27
4 changed files with 27 additions and 6 deletions

View File

@@ -540,12 +540,12 @@ func interpolateBlueprint(data []byte) []byte {
}) })
} }
func sendBlueprint(client *websocket.Client) error { func sendBlueprint(client *websocket.Client, file string) error {
if blueprintFile == "" { if file == "" {
return nil return nil
} }
// try to read the blueprint file // try to read the blueprint file
blueprintData, err := os.ReadFile(blueprintFile) blueprintData, err := os.ReadFile(file)
if err != nil { if err != nil {
logger.Error("Failed to read blueprint file: %v", err) logger.Error("Failed to read blueprint file: %v", err)
} else { } else {

15
main.go
View File

@@ -155,8 +155,9 @@ var (
region string region string
metricsAsyncBytes bool metricsAsyncBytes bool
pprofEnabled bool pprofEnabled bool
blueprintFile string blueprintFile string
noCloud bool provisioningBlueprintFile string
noCloud bool
// New mTLS configuration variables // New mTLS configuration variables
tlsClientCert string tlsClientCert string
@@ -284,6 +285,7 @@ func runNewtMain(ctx context.Context) {
tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT") tlsPrivateKey = os.Getenv("TLS_CLIENT_CERT")
} }
blueprintFile = os.Getenv("BLUEPRINT_FILE") blueprintFile = os.Getenv("BLUEPRINT_FILE")
provisioningBlueprintFile = os.Getenv("PROVISIONING_BLUEPRINT_FILE")
noCloudEnv := os.Getenv("NO_CLOUD") noCloudEnv := os.Getenv("NO_CLOUD")
noCloud = noCloudEnv == "true" noCloud = noCloudEnv == "true"
provisioningKey = os.Getenv("NEWT_PROVISIONING_KEY") provisioningKey = os.Getenv("NEWT_PROVISIONING_KEY")
@@ -393,6 +395,9 @@ func runNewtMain(ctx context.Context) {
if blueprintFile == "" { if blueprintFile == "" {
flag.StringVar(&blueprintFile, "blueprint-file", "", "Path to blueprint file (if unset, no blueprint will be applied)") flag.StringVar(&blueprintFile, "blueprint-file", "", "Path to blueprint file (if unset, no blueprint will be applied)")
} }
if provisioningBlueprintFile == "" {
flag.StringVar(&provisioningBlueprintFile, "provisioning-blueprint-file", "", "Path to blueprint file applied once after a provisioning credential exchange (if unset, no provisioning blueprint will be applied)")
}
if noCloudEnv == "" { if noCloudEnv == "" {
flag.BoolVar(&noCloud, "no-cloud", false, "Disable cloud failover") flag.BoolVar(&noCloud, "no-cloud", false, "Disable cloud failover")
} }
@@ -1821,7 +1826,11 @@ persistent_keepalive_interval=5`, util.FixKey(privateKey.String()), util.FixKey(
logger.Warn("CLIENTS WILL NOT WORK ON THIS VERSION OF NEWT WITH THIS VERSION OF PANGOLIN, PLEASE UPDATE THE SERVER TO 1.13 OR HIGHER OR DOWNGRADE NEWT") logger.Warn("CLIENTS WILL NOT WORK ON THIS VERSION OF NEWT WITH THIS VERSION OF PANGOLIN, PLEASE UPDATE THE SERVER TO 1.13 OR HIGHER OR DOWNGRADE NEWT")
} }
sendBlueprint(client) sendBlueprint(client, blueprintFile)
if client.WasJustProvisioned() {
logger.Info("Provisioning detected sending provisioning blueprint")
sendBlueprint(client, provisioningBlueprintFile)
}
} else { } else {
// Resend current health check status for all targets in case the server // Resend current health check status for all targets in case the server
// missed updates while newt was disconnected. // missed updates while newt was disconnected.

View File

@@ -53,6 +53,7 @@ type Client struct {
processingMessage bool // Flag to track if a message is currently being processed processingMessage bool // Flag to track if a message is currently being processed
processingMux sync.RWMutex // Protects processingMessage processingMux sync.RWMutex // Protects processingMessage
processingWg sync.WaitGroup // WaitGroup to wait for message processing to complete processingWg sync.WaitGroup // WaitGroup to wait for message processing to complete
justProvisioned bool // Set to true when provisionIfNeeded exchanges a key for permanent credentials
} }
type ClientOption func(*Client) type ClientOption func(*Client)
@@ -102,6 +103,16 @@ func (c *Client) OnTokenUpdate(callback func(token string)) {
c.onTokenUpdate = callback c.onTokenUpdate = callback
} }
// WasJustProvisioned reports whether the client exchanged a provisioning key
// for permanent credentials during the most recent connection attempt. It
// consumes the flag subsequent calls return false until provisioning occurs
// again (which, in practice, never happens once credentials are persisted).
func (c *Client) WasJustProvisioned() bool {
v := c.justProvisioned
c.justProvisioned = false
return v
}
func (c *Client) metricsContext() context.Context { func (c *Client) metricsContext() context.Context {
c.metricsCtxMu.RLock() c.metricsCtxMu.RLock()
defer c.metricsCtxMu.RUnlock() defer c.metricsCtxMu.RUnlock()

View File

@@ -264,6 +264,7 @@ func (c *Client) provisionIfNeeded() error {
c.config.ProvisioningKey = "" c.config.ProvisioningKey = ""
c.config.Name = "" c.config.Name = ""
c.configNeedsSave = true c.configNeedsSave = true
c.justProvisioned = true
// Save immediately so that if the subsequent connection attempt fails the // Save immediately so that if the subsequent connection attempt fails the
// provisioning key is already gone from disk and the next retry uses the // provisioning key is already gone from disk and the next retry uses the