mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-07-01 09:43:37 +00:00
Compare commits
12 Commits
v0.0.0_bet
...
v2.2.0
Author | SHA1 | Date | |
---|---|---|---|
067f1526b0 | |||
f43dea0d0d | |||
db8833fa25 | |||
1409e9ac31 | |||
adabe801d3 | |||
19f9494492 | |||
c50d2feaf9 | |||
03d3cc9177 | |||
141cc03b60 | |||
07559bd7ef | |||
cb206a18c2 | |||
6cb3ff80d9 |
49
.github/workflows/nightly.yml
vendored
Normal file
49
.github/workflows/nightly.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
name: Nightly
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * *"
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-11.0
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Signing
|
||||
env:
|
||||
SIGNING_DATA: ${{ secrets.SIGNING_DATA }}
|
||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||
HOST_PROFILE_DATA: ${{ secrets.HOST_PROFILE_DATA }}
|
||||
AGENT_PROFILE_DATA: ${{ secrets.AGENT_PROFILE_DATA }}
|
||||
APPLE_API_KEY_DATA: ${{ secrets.APPLE_API_KEY_DATA }}
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_13.2.1.app
|
||||
- name: Update Build Number
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
run: |
|
||||
sed -i '' -e "s/GITHUB_CI_VERSION/0.0.0/g" Sources/Config/Config.xcconfig
|
||||
sed -i '' -e "s/GITHUB_BUILD_NUMBER/1.$RUN_ID/g" Sources/Config/Config.xcconfig
|
||||
sed -i '' -e "s/GITHUB_BUILD_URL/https:\/\/github.com\/maxgoedjen\/secretive\/actions\/runs\/$RUN_ID/g" Sources/Secretive/Credits.rtf
|
||||
- name: Build
|
||||
run: xcrun xcodebuild -project Sources/Secretive.xcodeproj -scheme Secretive -configuration Release -archivePath Archive.xcarchive archive
|
||||
- name: Create ZIPs
|
||||
run: |
|
||||
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive/Products/Applications/Secretive.app ./Secretive.zip
|
||||
ditto -c -k --sequesterRsrc --keepParent Archive.xcarchive ./Archive.zip
|
||||
- name: Notarize
|
||||
env:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||
run: xcrun notarytool submit --key ~/.private_keys/AuthKey_$APPLE_API_KEY_ID.p8 --key-id $APPLE_API_KEY_ID --issuer $APPLE_API_ISSUER Secretive.zip
|
||||
- name: Document SHAs
|
||||
run: |
|
||||
shasum -a 512 Secretive.zip
|
||||
shasum -a 512 Archive.zip
|
||||
- name: Upload App to Artifacts
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: Secretive.zip
|
||||
path: Secretive.zip
|
@ -51,6 +51,30 @@ Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||
|
||||
Log out and log in again before launching Cyberduck.
|
||||
|
||||
## GitKraken
|
||||
|
||||
Add this to `~/Library/LaunchAgents/com.maxgoedjen.Secretive.SecretAgent.plist`
|
||||
|
||||
```
|
||||
<?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>Label</key>
|
||||
<string>link-ssh-auth-sock</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/bin/sh</string>
|
||||
<string>-c</string>
|
||||
<string>/bin/ln -sf $HOME/Library/Containers/com.maxgoedjen.Secretive.SecretAgent/Data/socket.ssh $SSH_AUTH_SOCK</string>
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
Log out and log in again before launching Gitkraken. Then enable "Use local SSH agent in GitKraken Preferences (Located under Preferences -> SSH)
|
||||
|
||||
# The app I use isn't listed here!
|
||||
|
||||
|
6
FAQ.md
6
FAQ.md
@ -38,7 +38,11 @@ Awesome! Just bear in mind that because an app only has access to the keychain i
|
||||
|
||||
### What's this network request to GitHub?
|
||||
|
||||
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Brief/Updater.swift).
|
||||
Secretive checks in with GitHub's releases API to check if there's a new version of Secretive available. You can audit the source code for this feature [here](https://github.com/maxgoedjen/secretive/blob/main/Sources/Packages/Sources/Brief/Updater.swift).
|
||||
|
||||
### How do I uninstall Secretive?
|
||||
|
||||
Drag Secretive.app to the trash and remove `~/Library/Containers/com.maxgoedjen.Secretive.SecretAgent`. `SecretAgent` may continue running until you quit it or reboot.
|
||||
|
||||
### I have a security issue
|
||||
|
||||
|
@ -30,20 +30,23 @@ extension Agent {
|
||||
/// - Parameters:
|
||||
/// - reader: A ``FileHandleReader`` to read the content of the request.
|
||||
/// - writer: A ``FileHandleWriter`` to write the response to.
|
||||
public func handle(reader: FileHandleReader, writer: FileHandleWriter) {
|
||||
/// - Return value:
|
||||
/// - Boolean if data could be read
|
||||
public func handle(reader: FileHandleReader, writer: FileHandleWriter) -> Bool {
|
||||
Logger().debug("Agent handling new data")
|
||||
let data = Data(reader.availableData)
|
||||
guard data.count > 4 else { return }
|
||||
guard data.count > 4 else { return false}
|
||||
let requestTypeInt = data[4]
|
||||
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
||||
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
|
||||
Logger().debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
||||
return
|
||||
return true
|
||||
}
|
||||
Logger().debug("Agent handling request of type \(requestType.debugDescription)")
|
||||
let subData = Data(data[5...])
|
||||
let response = handle(requestType: requestType, data: subData, reader: reader)
|
||||
writer.write(response)
|
||||
return true
|
||||
}
|
||||
|
||||
func handle(requestType: SSHAgent.RequestType, data: Data, reader: FileHandleReader) -> Data {
|
||||
|
@ -9,7 +9,9 @@ public class SocketController {
|
||||
/// The active SocketPort.
|
||||
private var port: SocketPort?
|
||||
/// A handler that will be notified when a new read/write handle is available.
|
||||
public var handler: ((FileHandleReader, FileHandleWriter) -> Void)?
|
||||
/// False if no data could be read
|
||||
public var handler: ((FileHandleReader, FileHandleWriter) -> Bool)?
|
||||
|
||||
|
||||
/// Initializes a socket controller with a specified path.
|
||||
/// - Parameter path: The path to use as a socket.
|
||||
@ -65,7 +67,7 @@ public class SocketController {
|
||||
@objc func handleConnectionAccept(notification: Notification) {
|
||||
Logger().debug("Socket controller accepted connection")
|
||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
|
||||
handler?(new, new)
|
||||
_ = handler?(new, new)
|
||||
new.waitForDataInBackgroundAndNotify()
|
||||
fileHandle?.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.current.currentMode!])
|
||||
}
|
||||
@ -76,7 +78,12 @@ public class SocketController {
|
||||
Logger().debug("Socket controller has new data available")
|
||||
guard let new = notification.object as? FileHandle else { return }
|
||||
Logger().debug("Socket controller received new file handle")
|
||||
handler?(new, new)
|
||||
if((handler?(new, new)) == true) {
|
||||
Logger().debug("Socket controller handled data, wait for more data")
|
||||
new.waitForDataInBackgroundAndNotify()
|
||||
} else {
|
||||
Logger().debug("Socket controller called with empty data, socked closed")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ public class PublicKeyFileStoreController {
|
||||
|
||||
private let logger = Logger()
|
||||
private let directory: String
|
||||
private let keyWriter = OpenSSHKeyWriter()
|
||||
|
||||
/// Initializes a PublicKeyFileStoreController.
|
||||
public init(homeDirectory: String) {
|
||||
@ -21,7 +22,6 @@ public class PublicKeyFileStoreController {
|
||||
try? FileManager.default.removeItem(at: URL(fileURLWithPath: directory))
|
||||
}
|
||||
try? FileManager.default.createDirectory(at: URL(fileURLWithPath: directory), withIntermediateDirectories: false, attributes: nil)
|
||||
let keyWriter = OpenSSHKeyWriter()
|
||||
for secret in secrets {
|
||||
let path = path(for: secret)
|
||||
guard let data = keyWriter.openSSHString(secret: secret).data(using: .utf8) else { continue }
|
||||
@ -35,7 +35,8 @@ public class PublicKeyFileStoreController {
|
||||
/// - Returns: The path to the Secret's public key.
|
||||
/// - Warning: This method returning a path does not imply that a key has been written to disk already. This method only describes where it will be written to.
|
||||
public func path<SecretType: Secret>(for secret: SecretType) -> String {
|
||||
directory.appending("/").appending("\(secret.name.replacingOccurrences(of: " ", with: "-")).pub")
|
||||
let minimalHex = keyWriter.openSSHMD5Fingerprint(secret: secret).replacingOccurrences(of: ":", with: "")
|
||||
return directory.appending("/").appending("\(minimalHex).pub")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,6 +56,9 @@ public protocol SecretStoreModifiable: SecretStore {
|
||||
|
||||
extension NSNotification.Name {
|
||||
|
||||
// Distributed notification that keys were modified out of process (ie, that the management tool added/removed secrets)
|
||||
public static let secretStoreUpdated = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.updated")
|
||||
// Internal notification that keys were reloaded from the backing store.
|
||||
public static let secretStoreReloaded = NSNotification.Name("com.maxgoedjen.Secretive.secretStore.reloaded")
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ extension SecureEnclave {
|
||||
/// Initializes a Store.
|
||||
public init() {
|
||||
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { _ in
|
||||
self.reloadSecrets(notify: false)
|
||||
self.reloadSecrets(notifyAgent: false)
|
||||
}
|
||||
loadSecrets()
|
||||
}
|
||||
@ -171,12 +171,13 @@ extension SecureEnclave {
|
||||
extension SecureEnclave.Store {
|
||||
|
||||
/// Reloads all secrets from the store.
|
||||
/// - Parameter notify: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well.
|
||||
private func reloadSecrets(notify: Bool = true) {
|
||||
/// - Parameter notifyAgent: A boolean indicating whether a distributed notification should be posted, notifying other processes (ie, the SecretAgent) to reload their stores as well.
|
||||
private func reloadSecrets(notifyAgent: Bool = true) {
|
||||
secrets.removeAll()
|
||||
loadSecrets()
|
||||
if notify {
|
||||
DistributedNotificationCenter.default().post(name: .secretStoreUpdated, object: nil)
|
||||
NotificationCenter.default.post(name: .secretStoreReloaded, object: self)
|
||||
if notifyAgent {
|
||||
DistributedNotificationCenter.default().postNotificationName(.secretStoreUpdated, object: nil, deliverImmediately: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
DispatchQueue.main.async {
|
||||
self.socketController.handler = self.agent.handle(reader:writer:)
|
||||
}
|
||||
DistributedNotificationCenter.default().addObserver(forName: .secretStoreUpdated, object: nil, queue: .main) { [self] _ in
|
||||
NotificationCenter.default.addObserver(forName: .secretStoreReloaded, object: nil, queue: .main) { [self] _ in
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
||||
}
|
||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.stores.flatMap({ $0.secrets }), clear: true)
|
||||
|
@ -32,6 +32,9 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
|
||||
appPathNotice
|
||||
newItem
|
||||
}
|
||||
.sheet(isPresented: $runningSetup) {
|
||||
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -119,9 +122,6 @@ extension ContentView {
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $runningSetup) {
|
||||
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ struct SetupView: View {
|
||||
}
|
||||
.frame(width: proxy.size.width)
|
||||
}
|
||||
.offset(x: -proxy.size.width * CGFloat(stepIndex), y: 0)
|
||||
.offset(x: -proxy.size.width * Double(stepIndex), y: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,7 +44,7 @@ struct StepView: View {
|
||||
let currentStep: Int
|
||||
|
||||
// Ideally we'd have a geometry reader inside this view doing this for us, but that crashes on 11.0b7
|
||||
let width: CGFloat
|
||||
let width: Double
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
@ -53,7 +53,7 @@ struct StepView: View {
|
||||
.frame(height: 5)
|
||||
Rectangle()
|
||||
.foregroundColor(.green)
|
||||
.frame(width: max(0, ((width - (Constants.padding * 2)) / CGFloat(numberOfSteps - 1)) * CGFloat(currentStep) - (Constants.circleWidth / 2)), height: 5)
|
||||
.frame(width: max(0, ((width - (Constants.padding * 2)) / Double(numberOfSteps - 1)) * Double(currentStep) - (Constants.circleWidth / 2)), height: 5)
|
||||
HStack {
|
||||
ForEach(0..<numberOfSteps) { index in
|
||||
ZStack {
|
||||
@ -92,8 +92,8 @@ extension StepView {
|
||||
|
||||
enum Constants {
|
||||
|
||||
static let padding: CGFloat = 15
|
||||
static let circleWidth: CGFloat = 30
|
||||
static let padding: Double = 15
|
||||
static let circleWidth: Double = 30
|
||||
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user