-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
509 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ cmd/squirrel/squirrel | |
cmd/caddy-squirrel/squirrel | ||
Caddyfile | ||
build/ | ||
vendor/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
FROM alpine:3.4 | ||
RUN apk --update add \ | ||
ca-certificates | ||
|
||
COPY ./build/squirrel-linux-amd64 /squirrel | ||
|
||
CMD ["/squirrel", "serve"] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,35 @@ | ||
Squirrel is a simple HTTP server for munki | ||
Squirrel is a Simple HTTPs server for munki. | ||
See the [legacy](https://github.com/micromdm/squirrel/tree/legacy) branch for the API implementation. I plan on adding it back with Munki v3 support. | ||
|
||
# Features | ||
|
||
* [X] **Automatic HTTPS** - squirrel provides a built in Let's Encrypt Client. | ||
* [X] Basic Authentication - Basic Auth for munki repo | ||
|
||
#Quickstart | ||
|
||
``` | ||
squirrel serve -repo=/path/to/munki_repo -tls-domain=munki.corp.example.com --basic-auth=CHANGEME | ||
``` | ||
|
||
`-repo` flag must be set to the path of a munki repository. | ||
`-tls-domain` flag must be set to the domain of your munki repo. This value is used by squirrel to obtain new TLS certificates. | ||
`-basic-auth` flag must be set to a password which will be used for authentication to the munki repo. | ||
|
||
Once the server starts, you will see a prompt which prints the correct Authorization header that you need to add to your munki configuration profile. | ||
|
||
Example: | ||
``` | ||
Authorization: Basic c3F1aXJyZWw6Q0hBTkdFTUU= | ||
``` | ||
|
||
See `squirrel help` for full usage. | ||
|
||
# Keep the process running with systemd | ||
|
||
For help with systemd see the example/systemd folder. | ||
|
||
# Enroll mac hosts: | ||
|
||
For help enrolling macOS hosts, check out the example/profile folder. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package cli | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/micromdm/squirrel/version" | ||
) | ||
|
||
func Main() { | ||
if len(os.Args) < 2 { | ||
printHelp() | ||
} | ||
switch os.Args[1] { | ||
case "serve": | ||
Serve() | ||
return | ||
case "help", "-h", "--help": | ||
printHelp() | ||
return | ||
case "version": | ||
version.Print() | ||
return | ||
default: | ||
fmt.Printf("no such command") | ||
return | ||
} | ||
} | ||
|
||
const usage = ` | ||
Usage: | ||
squirrel <COMMAND> | ||
Available Commands: | ||
help | ||
serve | ||
version | ||
Use squirel <command> -h for additional usage of each command. | ||
Example: squirrel serve -h | ||
` | ||
|
||
func printHelp() { | ||
fmt.Println(usage) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package cli | ||
|
||
import ( | ||
"crypto/tls" | ||
"encoding/base64" | ||
"flag" | ||
"fmt" | ||
"log" | ||
"net/http" | ||
"os" | ||
"time" | ||
|
||
"golang.org/x/crypto/acme/autocert" | ||
) | ||
|
||
func Serve() { | ||
serveCMD := flag.NewFlagSet("ca", flag.ExitOnError) | ||
status := serve(serveCMD) | ||
os.Exit(status) | ||
} | ||
|
||
const authUsername = "squirrel" | ||
|
||
func serve(cmd *flag.FlagSet) int { | ||
var ( | ||
flRepo = cmd.String("repo", envString("SQUIRREL_MUNKI_REPO_PATH", ""), "path to munki repo") | ||
flBasicPassword = cmd.String("basic-auth", envString("SQUIRREL_BASIC_AUTH", ""), "http basic auth password for /repo/") | ||
flTLS = cmd.Bool("tls", envBool("SQUIRREL_USE_TLS", true), "use https") | ||
flTLSCert = cmd.String("tls-cert", envString("SQUIRREL_TLS_CERT", ""), "path to TLS certificate") | ||
flTLSKey = cmd.String("tls-key", envString("SQUIRREL_TLS_KEY", ""), "path to TLS private key") | ||
flTLSAuto = cmd.String("tls-domain", envString("SQUIRREL_AUTO_TLS_DOMAIN", ""), "Automatically fetch certs from Let's Encrypt") | ||
) | ||
cmd.Parse(os.Args[2:]) | ||
mux := http.NewServeMux() | ||
mux.Handle("/repo/", repoHandler(*flBasicPassword, *flRepo)) | ||
|
||
srv := &http.Server{ | ||
Addr: ":https", | ||
Handler: mux, | ||
ReadTimeout: 60 * time.Second, | ||
WriteTimeout: 60 * time.Second, | ||
ReadHeaderTimeout: 10 * time.Second, | ||
IdleTimeout: 10 * time.Minute, | ||
MaxHeaderBytes: 1 << 18, // 0.25 MB | ||
TLSConfig: tlsConfig(), | ||
} | ||
|
||
printMunkiHeadersHelp(*flBasicPassword) | ||
if !*flTLS { | ||
log.Fatal(http.ListenAndServe(":80", mux)) | ||
return 0 | ||
} | ||
|
||
if *flTLSAuto != "" { | ||
serveACME(srv, *flTLSAuto) | ||
return 0 | ||
} | ||
|
||
tlsFromFile := *flTLSAuto == "" || (*flTLSCert != "" && *flTLSKey != "") | ||
if tlsFromFile { | ||
serveTLS(srv, *flTLSCert, *flTLSKey) | ||
return 0 | ||
} | ||
|
||
return 0 | ||
} | ||
|
||
func printMunkiHeadersHelp(password string) { | ||
const help = ` | ||
To connect your clients to the server, you will need to set the authentication headers. | ||
See https://github.com/munki/munki/wiki/Using-Basic-Authentication#configuring-the-clients-to-use-a-password | ||
for additional details: | ||
The headers header you should use is: | ||
%s | ||
To configure manually, use: | ||
sudo defaults write /Library/Preferences/ManagedInstalls AdditionalHttpHeaders -array "%s" | ||
` | ||
auth := basicAuth(password) | ||
header := fmt.Sprintf("Authorization: Basic %s", auth) | ||
fmt.Println(fmt.Sprintf(help, header, header)) | ||
} | ||
|
||
func serveTLS(server *http.Server, certPath, keyPath string) { | ||
redirectTLS() | ||
log.Fatal(server.ListenAndServeTLS(certPath, keyPath)) | ||
} | ||
|
||
func serveACME(server *http.Server, domain string) { | ||
m := autocert.Manager{ | ||
Prompt: autocert.AcceptTOS, | ||
HostPolicy: autocert.HostWhitelist(domain), | ||
Cache: autocert.DirCache("./certificates"), | ||
} | ||
server.TLSConfig.GetCertificate = m.GetCertificate | ||
redirectTLS() | ||
log.Fatal(server.ListenAndServeTLS("", "")) | ||
} | ||
|
||
// redirects port 80 to port 443 | ||
func redirectTLS() { | ||
srv := &http.Server{ | ||
ReadTimeout: 5 * time.Second, | ||
WriteTimeout: 5 * time.Second, | ||
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { | ||
w.Header().Set("Connection", "close") | ||
url := "https://" + req.Host + req.URL.String() | ||
http.Redirect(w, req, url, http.StatusMovedPermanently) | ||
}), | ||
} | ||
go func() { log.Fatal(srv.ListenAndServe()) }() | ||
} | ||
|
||
func repoHandler(repoPassword string, path string) http.HandlerFunc { | ||
repo := http.StripPrefix("/repo/", http.FileServer(http.Dir(path))) | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
_, password, ok := r.BasicAuth() | ||
if !ok || password != repoPassword { | ||
w.Header().Set("WWW-Authenticate", `Basic realm="munki"`) | ||
http.Error(w, "you need to log in", http.StatusUnauthorized) | ||
return | ||
} | ||
repo.ServeHTTP(w, r) | ||
} | ||
} | ||
|
||
func tlsConfig() *tls.Config { | ||
cfg := &tls.Config{ | ||
PreferServerCipherSuites: true, | ||
CurvePreferences: []tls.CurveID{ | ||
tls.CurveP256, | ||
tls.X25519, | ||
}, | ||
MinVersion: tls.VersionTLS12, | ||
CipherSuites: []uint16{ | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, | ||
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, | ||
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, | ||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, | ||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, | ||
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, | ||
}, | ||
} | ||
return cfg | ||
} | ||
|
||
func envString(key, def string) string { | ||
if env := os.Getenv(key); env != "" { | ||
return env | ||
} | ||
return def | ||
} | ||
|
||
func envBool(key string, def bool) bool { | ||
if env := os.Getenv(key); env == "true" { | ||
return true | ||
} | ||
return def | ||
} | ||
|
||
func basicAuth(password string) string { | ||
auth := authUsername + ":" + password | ||
return base64.StdEncoding.EncodeToString([]byte(auth)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
To enroll a mac into the `squirrel` server, install an updated version of this profile on the mac. | ||
|
||
First, you'll have to make a few changes. Open the profile in your text editor andupdate the values as needed. | ||
|
||
You _must_ set the correct value under | ||
``` | ||
<array> | ||
<string>Authorization: Basic CHANGEME</string> | ||
</array> | ||
``` | ||
The correct header will be printed when you run the `squirrel serve` command to start the server. | ||
|
||
|
||
And SoftwareRepoURL: | ||
``` | ||
<key>SoftwareRepoURL</key> | ||
<string>https://munki.corp.micromdm.io/repo</string> | ||
``` | ||
|
||
Note that `/repo` is a required path for squirrel. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>PayloadContent</key> | ||
<array> | ||
<dict> | ||
<key>PayloadContent</key> | ||
<dict> | ||
<key>ManagedInstalls</key> | ||
<dict> | ||
<key>Forced</key> | ||
<array> | ||
<dict> | ||
<key>mcx_preference_settings</key> | ||
<dict> | ||
<key>AppleSoftwareUpdatesOnly</key> | ||
<false/> | ||
<key>FollowHTTPRedirects</key> | ||
<string>https</string> | ||
<key>InstallAppleSoftwareUpdates</key> | ||
<true/> | ||
<key>SoftwareRepoURL</key> | ||
<string>https://munki.corp.micromdm.io/repo</string> | ||
<key>AdditionalHttpHeaders</key> | ||
<array> | ||
<string>Authorization: Basic CHANGEME</string> | ||
</array> | ||
</dict> | ||
</dict> | ||
</array> | ||
</dict> | ||
</dict> | ||
<key>PayloadEnabled</key> | ||
<true/> | ||
<key>PayloadIdentifier</key> | ||
<string>com.github.munki.munki_config</string> | ||
<key>PayloadType</key> | ||
<string>com.apple.ManagedClient.preferences</string> | ||
<key>PayloadUUID</key> | ||
<string>8d3324fc-b63a-4560-b9d3-f0f588158da1</string> | ||
<key>PayloadVersion</key> | ||
<integer>1</integer> | ||
</dict> | ||
</array> | ||
<key>PayloadDescription</key> | ||
<string>Configures ManagedSoftwareUpdate</string> | ||
<key>PayloadDisplayName</key> | ||
<string>ManagedSoftwareUpdate settings</string> | ||
<key>PayloadIdentifier</key> | ||
<string>com.github.munki.munki_config</string> | ||
<key>PayloadOrganization</key> | ||
<string>MicroMDM</string> | ||
<key>PayloadRemovalDisallowed</key> | ||
<true/> | ||
<key>PayloadScope</key> | ||
<string>System</string> | ||
<key>PayloadType</key> | ||
<string>Configuration</string> | ||
<key>PayloadUUID</key> | ||
<string>c8782978-a43b-44eb-b5a0-786d94e10bcd</string> | ||
<key>PayloadVersion</key> | ||
<integer>1</integer> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
The `squirrel.unit` file is an example systemd unit file for keeping the squirrel service running. | ||
|
||
|
||
- change the configuration flags under `[Service]`. | ||
- `sudo cp squirrel.unit /etc/systemd/system/` | ||
- `sudo systemctl start squirrel.service` | ||
|
||
# Useful commands | ||
Reload Unit File: | ||
``` | ||
sudo systemctl daemon-reload | ||
``` | ||
|
||
|
||
Start/Stop/Restart/status: | ||
|
||
``` | ||
sudo systemctl start squirrel.service | ||
sudo systemctl stop squirrel.service | ||
sudo systemctl restart squirrel.service | ||
sudo systemctl status squirrel.service | ||
``` | ||
|
||
Tail logs: | ||
``` | ||
journalctl -u squirrel.service -f | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[Unit] | ||
Description=Squirrel Server | ||
Documentation=https://github.com/micromdm/squirrel | ||
After=network.target | ||
|
||
[Service] | ||
ExecStart=/usr/bin/squirrel serve \ | ||
-basic-auth=CHANGEME \ | ||
-repo=/munki-repo \ | ||
-tls-domain=munki.corp.micromdm.io | ||
Restart=on-failure | ||
|
||
[Install] | ||
WantedBy=multi-user.target |
Oops, something went wrong.