@@ -30,6 +30,10 @@ const (
3030 nginxBackupFilePrefix = "BAK_IF_backend_"
3131 nginxFileMode = 0644
3232
33+ // nginxCertsPath is the directory the ssl_certificate templates below point
34+ // at; certificate file names have to stay inside it.
35+ nginxCertsPath = "/etc/nginx/certs"
36+
3337 HttpsScheme = "https"
3438 DefaultHttpsPort = "443"
3539)
@@ -58,6 +62,19 @@ func generateNginxConfig(website *model.WebsiteConfig) (string, error) {
5862 sslCertFilename := ""
5963 sslCertKeyFilename := ""
6064 if website .KeyFilename != "" && website .CertFilename != "" {
65+ for _ , filename := range []string {website .CertFilename , website .KeyFilename } {
66+ if err := validateCertFilename (filename ); err != nil {
67+ return "" , err
68+ }
69+
70+ // The template renders the name into an absolute path, so an
71+ // explicit check backs up the name validation above: the resolved
72+ // path has to stay inside the certificate directory.
73+ if filepath .Dir (filepath .Join (nginxCertsPath , filename )) != nginxCertsPath {
74+ return "" , fmt .Errorf ("invalid certificate file name %q: it leaves %s" , filename , nginxCertsPath )
75+ }
76+ }
77+
6178 sslFlag = " ssl" // with a blank ahead
6279 sslCertFilename = fmt .Sprintf (certTpl , website .CertFilename )
6380 sslCertKeyFilename = fmt .Sprintf (certKeyTpl , website .KeyFilename )
@@ -110,11 +127,28 @@ func generateFullConfigAndReload(msg []byte) error {
110127 return err
111128 }
112129
130+ if err := utils .EnsureDir (nginxConfigPath ); err != nil {
131+ return err
132+ }
133+
113134 configFilename := make (map [string ]struct {})
114135 for _ , website := range websites {
115136 configFilename [fmt .Sprintf ("%s%d" , nginxFilePrefix , website .Id )] = struct {}{}
116137 }
117- filepath .Walk (nginxConfigPath , func (path string , info fs.FileInfo , err error ) error {
138+
139+ if err := filepath .Walk (nginxConfigPath , func (path string , info fs.FileInfo , err error ) error {
140+ // filepath.Walk reports the entries it could not read through err and
141+ // leaves info nil in that case: touching info before this check is a
142+ // nil pointer dereference that takes the whole daemon down.
143+ if err != nil {
144+ logger .Warnf ("Skip %s: %v" , path , err )
145+ return nil
146+ }
147+
148+ if info == nil || info .IsDir () {
149+ return nil
150+ }
151+
118152 _ , ok := configFilename [info .Name ()]
119153 if ! ok && strings .HasPrefix (info .Name (), nginxFilePrefix ) {
120154 if err := os .Remove (path ); err != nil {
@@ -124,7 +158,9 @@ func generateFullConfigAndReload(msg []byte) error {
124158 }
125159
126160 return nil
127- })
161+ }); err != nil {
162+ return err
163+ }
128164
129165 for _ , website := range websites {
130166 if err := generateConfigAndReload (& website ); err != nil {
@@ -231,8 +267,8 @@ func sendResponse(stream pb.Website_SubscribeClient, eventType string, errMsg []
231267 })
232268}
233269
234- func websiteHandler (wc pb.WebsiteClient ) error {
235- stream , err := wc .Subscribe (context . Background () )
270+ func websiteHandler (ctx context. Context , wc pb.WebsiteClient ) error {
271+ stream , err := wc .Subscribe (ctx )
236272 if err != nil {
237273 logger .Errorf ("Subscribe failed: %v" , err )
238274 return err
0 commit comments