Skip to content
This repository has been archived by the owner on Oct 4, 2023. It is now read-only.

webconf service for the initial configuration of a device #143

Open
wants to merge 55 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
16b5c0c
spread test infrastructure, and initial installation test
Dec 14, 2016
8e763cb
check if the system is managed
Dec 16, 2016
7a5b68a
simplify
Jan 4, 2017
d466cea
optimize spread test suite
Jan 6, 2017
f7576b9
use 1G image; this should be sufficient
Jan 12, 2017
41a5ded
2G it is
Jan 12, 2017
e042012
add support for new firstboot test suite
Jan 12, 2017
c72de1b
compat with kvm
Jan 13, 2017
124a536
create 2nd image properly
Jan 13, 2017
5de0330
ok, 4G per image then
Jan 13, 2017
0828110
restore-each for the firstboot test suite
Jan 13, 2017
1701016
initial firstboot code
Jan 13, 2017
9fd16dc
initial implementation of a separate service for firstboot
Jan 13, 2017
736e9d1
start firstboot as a normal service; just exit gracefully if the syst…
Jan 13, 2017
ff26e7c
serve a specific JS code for firstboot
Jan 18, 2017
967522b
rename to web-conf, same as console-conf
Jan 18, 2017
4fc8bd7
simplify templates; let w-c run on managed for now
Jan 19, 2017
0bddf2c
initial view
Jan 25, 2017
164dcbe
re-establish the create-user feature in the new webconf module
Jan 26, 2017
b3e7c5b
lock down web-conf again; update spread tests
Jan 26, 2017
61a8590
add missing wrapper for starting webconf; adjust command names
Jan 30, 2017
a6518a4
don't panic, just wait for a signal from webconf
Jan 31, 2017
d8d6f01
gofmt
Feb 2, 2017
202e1fd
webconf now signals snapweb when it is done
Feb 2, 2017
b9f8bfb
send signal to snapweb, send /done url; build webconf with gulp by de…
Feb 3, 2017
7ec6d53
stop webconf and let snapweb resume
Feb 7, 2017
4341aae
verify the transition from webconf to snapweb
Feb 13, 2017
f8f9644
rename leftover bits to webconf, no dash
Feb 15, 2017
dbfbdc2
simplify run-checks by offloading all the npm setup to a travis-speci…
Feb 15, 2017
755c640
do the same for godeps
Feb 15, 2017
e6c31c8
refactor common functions into a helper module; add tests for it
Feb 16, 2017
ea4b776
refactor code shared between webconf and snapweb; add more tests
Feb 16, 2017
31820d5
gofmt
Feb 16, 2017
3c38c44
init.js has been renamed
Feb 16, 2017
58b06b1
update js unit test suite
Feb 16, 2017
c6ccf0b
fix spread test use of a variable
Feb 16, 2017
90e5d7c
fix copyright date
Feb 16, 2017
9a5063b
fix copyright date
Feb 16, 2017
0234f25
gofmt again
Feb 17, 2017
c0af22c
adjust kvm utils
Feb 20, 2017
11f528c
improve spread tests
Feb 21, 2017
ddcdcee
only expose the create-user API
Feb 23, 2017
47b236e
rebase with master; improve function name for creating a cert if needed
Feb 23, 2017
f8e7d65
improve unit tests; address review remarks
Mar 2, 2017
1e22c95
gofmt/lint et al
Mar 6, 2017
635e5b5
gulp and build.sh support for optional webconf build option
Mar 10, 2017
0ae6319
adjust packages and tests after master rebase
Mar 22, 2017
ea4e8de
ensure systemd won't try to restart the webconf service
Mar 22, 2017
947266d
adjust spread tests
Mar 22, 2017
4cb3664
move the default filter handler to the shared snappy package
Mar 22, 2017
6b41e6f
add support for the new netfilter; accept local networks by default
Mar 22, 2017
f077af8
gofmt
Mar 22, 2017
a54570d
verify that webconf is protected by the netfilter feature
Mar 23, 2017
9d69fe6
add spread test to verify if the netfilter feature protects snapweb
Mar 23, 2017
6d7d2c5
add a timer to de-activate webconf after 2 minutes
Mar 24, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,36 @@
# amd64, arm64, armhf and i386.
#
# [--ups]: Build the ubuntu-personal-store snap as well.
#
# [--with-webconf]: Adds the new webconf service to the snapweb snap.
#

set -e

if [ "$#" -eq 0 ] || ([ "$#" -eq 1 ] && [ "$1" = "--ups" ]); then
BUILD_UPS_SNAP=
WITH_WEBCONF=

while [ "$1" != "" ]; do
case $1 in
--ups)
BUILD_UPS_SNAP=1
;;
--with-webconf)
WITH_WEBCONF=1
;;
amd64|i386|arm64|armhf)
architectures+=("$1")
;;
*)
# print usage
head -13 $0 | tail -12
exit
esac
shift
done

if [ "$architectures" = "" ]; then
architectures=( amd64 arm64 armhf i386 )
else
architectures=( "$@" )
fi

AVAHI_VERSION="0.6.31-4ubuntu4snap2"
Expand Down Expand Up @@ -59,7 +82,10 @@ gobuild() {

mkdir -p $output_dir
cd $output_dir
GOARCH=$arch GOARM=7 CGO_ENABLED=1 CC=${plat_abi}-gcc go build -ldflags "-extld=${plat_abi}-gcc -X main.httpAddr=${httpAddr} -X main.httpsAddr=${httpsAddr}" github.com/snapcore/snapweb/cmd/snapweb
GOARCH=$arch GOARM=7 CGO_ENABLED=1 CC=${plat_abi}-gcc go build -ldflags "-extld=${plat_abi}-gcc" github.com/snapcore/snapweb/cmd/snapweb
if [ $WITH_WEBCONF ]; then
GOARCH=$arch GOARM=7 CGO_ENABLED=1 CC=${plat_abi}-gcc go build -ldflags "-extld=${plat_abi}-gcc" github.com/snapcore/snapweb/cmd/webconf
fi
GOARCH=$arch GOARM=7 CGO_ENABLED=1 CC=${plat_abi}-gcc go build -o generate-token -ldflags "-extld=${plat_abi}-gcc" $srcdir/cmd/generate-token/main.go
cp generate-token ../../
cd - > /dev/null
Expand Down Expand Up @@ -112,7 +138,7 @@ for ARCH in "${architectures[@]}"; do
snapcraft snap $builddir

# TODO: We only support amd64 ups builds for now -Need a cross-compile fix for "after: [desktop-qt5]"
if [[ $* == *--ups* ]] && [ $ARCH = amd64 ]; then
if [ $BUILD_UPS_SNAP ] && [ $ARCH = amd64 ]; then
HTTP_ADDR="127.0.0.1:5200"
HTTPS_ADDR="127.0.0.1:5201"

Expand Down
4 changes: 2 additions & 2 deletions cmd/snapweb/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import (
"time"
)

// DumpCertificate create the certificate & key files
func DumpCertificate() {
// CreateCertificateIfNeeded creates the certificate & key files, if they don't exist yet
func CreateCertificateIfNeeded() {
certFilename := filepath.Join(os.Getenv("SNAP_DATA"), "cert.pem")
keyFilename := filepath.Join(os.Getenv("SNAP_DATA"), "key.pem")

Expand Down
6 changes: 3 additions & 3 deletions cmd/snapweb/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,17 @@ func (s *CertSuite) TearDownTest(c *C) {
os.RemoveAll(s.snapdata)
}

func (s *CertSuite) TestDumpCertificate(c *C) {
func (s *CertSuite) TestCreateCertificateIfNeeded(c *C) {
c.Assert(ioutil.WriteFile(s.certFilename, nil, 0600), IsNil)
c.Assert(ioutil.WriteFile(s.keyFilename, nil, 0600), IsNil)

DumpCertificate()
CreateCertificateIfNeeded()
certData, err := ioutil.ReadFile(s.certFilename)
c.Assert(err, IsNil)
keyData, err := ioutil.ReadFile(s.keyFilename)
c.Assert(err, IsNil)

DumpCertificate()
CreateCertificateIfNeeded()
certData2, err := ioutil.ReadFile(s.certFilename)
c.Assert(err, IsNil)
keyData2, err := ioutil.ReadFile(s.keyFilename)
Expand Down
30 changes: 2 additions & 28 deletions cmd/snapweb/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ func initURLHandlers(log *log.Logger, config snappy.Config) http.Handler {

handler.HandleFunc("/", makeMainPageHandler())

return NewFilterHandlerFromConfig(handler, config)
return snappy.NewFilterHandlerFromConfig(handler, config)
}

// Name of the cookie transporting the access token
Expand Down Expand Up @@ -341,31 +341,5 @@ func redirHandler(config snappy.Config) http.Handler {
http.StatusSeeOther)
})

return NewFilterHandlerFromConfig(redir, config)
}

// NewFilterHandlerFromConfig creates a new http.Handler with an integrated NetFilter
func NewFilterHandlerFromConfig(handler http.Handler, config snappy.Config) http.Handler {
if config.DisableIPFilter {
return handler
}

f := snappy.NewFilter()

for _, net := range config.AllowNetworks {
f.AllowNetwork(net)
}

for _, ifname := range config.AllowInterfaces {
f.AddLocalNetworkForInterface(ifname)
}

// if nothing was specified, default to allowing all local networks
if (len(config.AllowNetworks) == 0) &&
(len(config.AllowInterfaces) == 0) {
logger.Println("Allowing local network access by default")
f.AddLocalNetworks()
}

return f.FilterHandler(handler)
return snappy.NewFilterHandlerFromConfig(redir, config)
}
23 changes: 22 additions & 1 deletion cmd/snapweb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"time"

"github.com/snapcore/snapweb/avahi"
"github.com/snapcore/snapweb/snappy/app"
Expand All @@ -43,7 +45,26 @@ func init() {
}
}

func redir(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req,
"https://"+strings.Replace(req.Host, httpAddr, httpsAddr, -1),
http.StatusSeeOther)
}

type blockerFn func() bool

func blockOn(managedCondition blockerFn) {
snappy.WritePidFile()
for managedCondition() == false {
snappy.WaitForSigHup()
// wait futher more, to let webconf release the 4200 port
time.Sleep(1000)
}
}

func main() {
blockOn(snappy.IsDeviceManaged)

// TODO set warning for too hazardous config?
config, err := snappy.ReadConfig()
if err != nil {
Expand All @@ -58,7 +79,7 @@ func main() {
logger.Println("Snapweb starting...")

if !config.DisableHTTPS {
DumpCertificate()
CreateCertificateIfNeeded()

go func() {
certFile := filepath.Join(os.Getenv("SNAP_DATA"), "cert.pem")
Expand Down
68 changes: 68 additions & 0 deletions cmd/snapweb/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (C) 2017 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package main

import (
"os"
"time"

"github.com/snapcore/snapweb/snappy/app"

. "gopkg.in/check.v1"
)

type MainSuite struct{}

var _ = Suite(&MainSuite{})

var mockManagedState = false

func mockIsDeviceManaged() bool {
return mockManagedState
}

func (s *MainSuite) TestBlockUntilManaged(c *C) {
os.Setenv("SNAP_DATA", c.MkDir())

ready := make(chan bool)
done := make(chan bool)

go func() {
ready <- true
blockOn(mockIsDeviceManaged)
done <- true
}()

// a signal cannot unblock snapweb until the system is managed
mockManagedState = false
<-ready
time.Sleep(1000) // give time to install the signal handler
snappy.SendSignalToSnapweb()
select {
case <-done:
c.Fail()
default:
}

// try to unblock once the system has changed
mockManagedState = true
snappy.SendSignalToSnapweb()
result := <-done

c.Assert(result, Equals, true)
}
146 changes: 146 additions & 0 deletions cmd/webconf/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright (C) 2017 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package main

import (
"log"
"net"
"net/http"
"os"
"path/filepath"
"text/template"
"time"

"github.com/snapcore/snapd/dirs"

"github.com/snapcore/snapweb/avahi"
"github.com/snapcore/snapweb/snappy/app"
)

var logger *log.Logger
var timer *time.Timer

const (
httpAddr string = ":4200"
)

func init() {
logger = log.New(os.Stderr, "webconf: ", log.Ldate|log.Ltime|log.Lshortfile)
}

type branding struct {
Name string
Subname string
}

type templateData struct {
Branding branding
SnapdVersion string
}

func makeMainPageHandler() http.HandlerFunc {

layoutPath := filepath.Join(os.Getenv("SNAP"), "www", "templates", "webconf.html")
t, err := template.ParseFiles(layoutPath)
if err != nil {
logger.Fatalf("%v", err)
}

data := templateData{
Branding: branding{
Name: "Ubuntu",
Subname: "",
},
SnapdVersion: "snapd",
}

return func(w http.ResponseWriter, r *http.Request) {

// reset the timer, and give 3 minutes to complete the setup process
timer.Reset(time.Second * 180)

err = t.Execute(w, &data)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Println(err)
return
}

}
}

func doneHandler(server net.Listener) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
snappy.SendSignalToSnapweb()
if server != nil {
server.Close()
}
}
}

func initURLHandlers(log *log.Logger, server net.Listener, config snappy.Config) http.Handler {
handler := http.NewServeMux()

// API
handler.Handle("/api/v2/create-user",
snappy.MakePassthroughHandler(dirs.SnapdSocket, "/api/v2/create-user"))
handler.HandleFunc("/done", doneHandler(server))

// Resources
handler.Handle("/public/", snappy.LoggingHandler(http.FileServer(http.Dir(filepath.Join(os.Getenv("SNAP"), "www")))))

handler.HandleFunc("/", makeMainPageHandler())

return snappy.NewFilterHandlerFromConfig(handler, config)
}

func main() {
if snappy.IsDeviceManaged() {
log.Println("webconf does not run on managed devices")
// this tells systemd to not restart this service
os.Exit(0)
}

config, err := snappy.ReadConfig()
if err != nil {
logger.Fatal("Configuration error", err)
}

go avahi.InitMDNS(logger)

// open a plain HTTP end-point on the "usual" 4200 port
server, err := net.Listen("tcp", httpAddr)
if err != nil {
logger.Fatalf("%v", err)
}

handler := initURLHandlers(logger, server, config)

timer = time.NewTimer(time.Second * 120)
go func() {
<-timer.C
logger.Println("disabling webconf automatically after 2 minutes")
server.Close()
os.Exit(0)
}()

http.Serve(server, handler)

// prepare to exit, but let snapweb start before that
time.Sleep(2 * time.Second)
}
Loading