-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add internal routes for ssh hook comands #1471
Changes from all commits
c015e57
af91984
d4570aa
dc5980e
c76983c
4f2c3a7
def1241
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package private | ||
|
||
import ( | ||
"crypto/tls" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
|
||
"code.gitea.io/gitea/modules/httplib" | ||
"code.gitea.io/gitea/modules/log" | ||
"code.gitea.io/gitea/modules/setting" | ||
) | ||
|
||
func newRequest(url, method string) *httplib.Request { | ||
return httplib.NewRequest(url, method).Header("Authorization", | ||
fmt.Sprintf("Bearer %s", setting.InternalToken)) | ||
} | ||
|
||
// Response internal request response | ||
type Response struct { | ||
Err string `json:"err"` | ||
} | ||
|
||
func decodeJSONError(resp *http.Response) *Response { | ||
var res Response | ||
err := json.NewDecoder(resp.Body).Decode(&res) | ||
if err != nil { | ||
res.Err = err.Error() | ||
} | ||
return &res | ||
} | ||
|
||
// UpdatePublicKeyUpdated update publick key updates | ||
func UpdatePublicKeyUpdated(keyID int64) error { | ||
// Ask for running deliver hook and test pull request tasks. | ||
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/ssh/%d/update", keyID) | ||
log.GitLogger.Trace("UpdatePublicKeyUpdated: %s", reqURL) | ||
|
||
resp, err := newRequest(reqURL, "POST").SetTLSClientConfig(&tls.Config{ | ||
InsecureSkipVerify: true, | ||
}).Response() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
defer resp.Body.Close() | ||
|
||
// All 2XX status codes are accepted and others will return an error | ||
if resp.StatusCode/100 != 2 { | ||
return fmt.Errorf("Failed to update public key: %s", decodeJSONError(resp).Err) | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ import ( | |
"code.gitea.io/gitea/modules/user" | ||
|
||
"github.com/Unknwon/com" | ||
"github.com/dgrijalva/jwt-go" | ||
_ "github.com/go-macaron/cache/memcache" // memcache plugin for cache | ||
_ "github.com/go-macaron/cache/redis" | ||
"github.com/go-macaron/session" | ||
|
@@ -443,14 +444,15 @@ var ( | |
ShowFooterTemplateLoadTime bool | ||
|
||
// Global setting objects | ||
Cfg *ini.File | ||
CustomPath string // Custom directory path | ||
CustomConf string | ||
CustomPID string | ||
ProdMode bool | ||
RunUser string | ||
IsWindows bool | ||
HasRobotsTxt bool | ||
Cfg *ini.File | ||
CustomPath string // Custom directory path | ||
CustomConf string | ||
CustomPID string | ||
ProdMode bool | ||
RunUser string | ||
IsWindows bool | ||
HasRobotsTxt bool | ||
InternalToken string // internal access token | ||
) | ||
|
||
// DateLang transforms standard language locale name to corresponding value in datetime plugin. | ||
|
@@ -765,6 +767,43 @@ please consider changing to GITEA_CUSTOM`) | |
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") | ||
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6) | ||
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false) | ||
InternalToken = sec.Key("INTERNAL_TOKEN").String() | ||
if len(InternalToken) == 0 { | ||
secretBytes := make([]byte, 32) | ||
_, err := io.ReadFull(rand.Reader, secretBytes) | ||
if err != nil { | ||
log.Fatal(4, "Error reading random bytes: %v", err) | ||
} | ||
|
||
secretKey := base64.RawURLEncoding.EncodeToString(secretBytes) | ||
|
||
now := time.Now() | ||
InternalToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ | ||
"nbf": now.Unix(), | ||
}).SignedString([]byte(secretKey)) | ||
|
||
if err != nil { | ||
log.Fatal(4, "Error generate internal token: %v", err) | ||
} | ||
|
||
// Save secret | ||
cfgSave := ini.Empty() | ||
if com.IsFile(CustomConf) { | ||
// Keeps custom settings if there is already something. | ||
if err := cfgSave.Append(CustomConf); err != nil { | ||
log.Error(4, "Failed to load custom conf '%s': %v", CustomConf, err) | ||
} | ||
} | ||
|
||
cfgSave.Section("security").Key("INTERNAL_TOKEN").SetValue(InternalToken) | ||
|
||
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil { | ||
log.Fatal(4, "Failed to create '%s': %v", CustomConf, err) | ||
} | ||
if err := cfgSave.SaveTo(CustomConf); err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see a reason to keep that token in the Ini file either, just have it randomly generated upon start and let it die/regenerate on restart There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. because we have to share the token between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @strk There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. got it, thanks |
||
log.Fatal(4, "Error saving generated JWT Secret to custom config: %v", err) | ||
} | ||
} | ||
|
||
sec = Cfg.Section("attachment") | ||
AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments")) | ||
|
@@ -935,7 +974,6 @@ var Service struct { | |
EnableOpenIDSignUp bool | ||
OpenIDWhitelist []*regexp.Regexp | ||
OpenIDBlacklist []*regexp.Regexp | ||
|
||
} | ||
|
||
func newService() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Copyright 2017 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Package private includes all internal routes. The package name internal is ideal but Golang is not allowed, so we use private as package name instead. | ||
package private | ||
|
||
import ( | ||
"strings" | ||
|
||
"code.gitea.io/gitea/models" | ||
"code.gitea.io/gitea/modules/setting" | ||
macaron "gopkg.in/macaron.v1" | ||
) | ||
|
||
// CheckInternalToken check internal token is set | ||
func CheckInternalToken(ctx *macaron.Context) { | ||
tokens := ctx.Req.Header.Get("Authorization") | ||
fields := strings.Fields(tokens) | ||
if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken { | ||
ctx.Error(403) | ||
} | ||
} | ||
|
||
// UpdatePublicKey update publick key updates | ||
func UpdatePublicKey(ctx *macaron.Context) { | ||
keyID := ctx.ParamsInt64(":id") | ||
if err := models.UpdatePublicKeyUpdated(keyID); err != nil { | ||
ctx.JSON(500, map[string]interface{}{ | ||
"err": err.Error(), | ||
}) | ||
return | ||
} | ||
|
||
ctx.PlainText(200, []byte("success")) | ||
} | ||
|
||
// RegisterRoutes registers all internal APIs routes to web application. | ||
// These APIs will be invoked by internal commands for example `gitea serv` and etc. | ||
func RegisterRoutes(m *macaron.Macaron) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We really need to start doing this in more places ❤️ |
||
m.Group("/", func() { | ||
m.Post("/ssh/:id/update", UpdatePublicKey) | ||
}, CheckInternalToken) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it called
private
when then endpoint is/internal
? Pick one and stay with it 😉There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
because Go don't allow a package named
internal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooh right,
internal
is "special" 😒There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about using
/api/private
then ? It's good to be consistent :)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And private.go, for that same reason...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@strk
/api/private
is inaccurate than/api/internal
I think.